From dc59d344fa62d5ceba7d4881c34c8d6fbe3624f9 Mon Sep 17 00:00:00 2001 From: david Date: Mon, 6 Apr 2020 16:47:49 +0200 Subject: [PATCH 01/38] [ADD] sale_global_discount: New module --- sale_global_discount/README.rst | 107 ++++ sale_global_discount/__init__.py | 1 + sale_global_discount/__manifest__.py | 20 + sale_global_discount/i18n/es.po | 51 ++ .../i18n/sale_global_discount.pot | 46 ++ sale_global_discount/models/__init__.py | 1 + sale_global_discount/models/sale_order.py | 118 +++++ sale_global_discount/readme/CONFIGURE.rst | 14 + sale_global_discount/readme/CONTRIBUTORS.rst | 3 + sale_global_discount/readme/DESCRIPTION.rst | 2 + sale_global_discount/readme/USAGE.rst | 9 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/icon.svg | 79 +++ .../static/description/index.html | 456 ++++++++++++++++++ sale_global_discount/tests/__init__.py | 1 + .../tests/test_sale_global_discount.py | 177 +++++++ .../views/report_sale_order.xml | 26 + .../views/sale_order_views.xml | 21 + 18 files changed, 1132 insertions(+) create mode 100644 sale_global_discount/README.rst create mode 100644 sale_global_discount/__init__.py create mode 100644 sale_global_discount/__manifest__.py create mode 100644 sale_global_discount/i18n/es.po create mode 100644 sale_global_discount/i18n/sale_global_discount.pot create mode 100644 sale_global_discount/models/__init__.py create mode 100644 sale_global_discount/models/sale_order.py create mode 100644 sale_global_discount/readme/CONFIGURE.rst create mode 100644 sale_global_discount/readme/CONTRIBUTORS.rst create mode 100644 sale_global_discount/readme/DESCRIPTION.rst create mode 100644 sale_global_discount/readme/USAGE.rst create mode 100644 sale_global_discount/static/description/icon.png create mode 100644 sale_global_discount/static/description/icon.svg create mode 100644 sale_global_discount/static/description/index.html create mode 100644 sale_global_discount/tests/__init__.py create mode 100644 sale_global_discount/tests/test_sale_global_discount.py create mode 100644 sale_global_discount/views/report_sale_order.xml create mode 100644 sale_global_discount/views/sale_order_views.xml diff --git a/sale_global_discount/README.rst b/sale_global_discount/README.rst new file mode 100644 index 00000000000..7d111259f9c --- /dev/null +++ b/sale_global_discount/README.rst @@ -0,0 +1,107 @@ +==================== +Sale Global Discount +==================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/sale-workflow/tree/11.0/sale_global_discount + :alt: OCA/sale-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/sale-workflow-11-0/sale-workflow-11-0-sale_global_discount + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/167/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Apply global financial discounts to sales that will be transmited to invoices +and accounting. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to: + +#. Go to *Settings > Parameters > Global Discounts* +#. Add a new discount. +#. Choose the discount scope (sales or purchases). +#. You can also restrict it to a certain company if needed. + +You can assign global discounts to partners as well: + +#. Go to a partner that is a company. +#. Go to the *Sales & Purchases* tab. +#. In section sale (if the partner is a customer), you can set sale discounts. +#. In section purchase (if the partner is a supplier), you can set purchase + discounts. + +Usage +===== + +To use this module, you need to: + +#. Create a new sale order and choose a partner. +#. If the partner has customer global discounts set, those will be applied to + the order by default. +#. Otherwise, you can set them manually from the header of the sale order. +#. In the order footer, you can see the computed discounts. +#. When you create an invoice from the order, the proper global discounts will + be applied on it. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_ + + * David Vidal + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/sale-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_global_discount/__init__.py b/sale_global_discount/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/sale_global_discount/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py new file mode 100644 index 00000000000..0fc29784a2e --- /dev/null +++ b/sale_global_discount/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2020 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': 'Sale Global Discount', + 'version': '11.0.1.0.1', + 'category': 'Sales Management', + 'author': 'Tecnativa,' + 'Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/sale-workflow', + 'license': 'AGPL-3', + 'depends': [ + 'account_global_discount', + ], + 'data': [ + 'views/sale_order_views.xml', + 'views/report_sale_order.xml', + ], + 'application': False, + 'installable': True, +} diff --git a/sale_global_discount/i18n/es.po b/sale_global_discount/i18n/es.po new file mode 100644 index 00000000000..762ca42853e --- /dev/null +++ b/sale_global_discount/i18n/es.po @@ -0,0 +1,51 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_global_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-04-06 14:38+0000\n" +"PO-Revision-Date: 2020-04-06 14:38+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_global_discount +#: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +msgid "Global Discounts
" +msgstr "" + +#. module: sale_global_discount +#: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +msgid "Subtl. before disc." +msgstr "Subtl. antes desc." + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_total_before_global_discounts +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_untaxed_before_global_discounts +msgid "Amount Untaxed Before Discounts" +msgstr "Base Imponible sin Descuentos" + +#. module: sale_global_discount +#: model:ir.model,name:sale_global_discount.model_sale_order +msgid "Quotation" +msgstr "Presupuesto" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_global_discount_ids +msgid "Sale Global Discounts" +msgstr "Descuentos de venta globales" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_global_discount +msgid "Total Global Discounts" +msgstr "Total Descuentos Globales" + +#~ msgid "Global Discount" +#~ msgstr "Descuento Global" diff --git a/sale_global_discount/i18n/sale_global_discount.pot b/sale_global_discount/i18n/sale_global_discount.pot new file mode 100644 index 00000000000..a915a589ed2 --- /dev/null +++ b/sale_global_discount/i18n/sale_global_discount.pot @@ -0,0 +1,46 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_global_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_global_discount +#: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +msgid "Global Discounts
" +msgstr "" + +#. module: sale_global_discount +#: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +msgid "Subtl. before disc." +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_total_before_global_discounts +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_untaxed_before_global_discounts +msgid "Amount Untaxed Before Discounts" +msgstr "" + +#. module: sale_global_discount +#: model:ir.model,name:sale_global_discount.model_sale_order +msgid "Quotation" +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_global_discount_ids +msgid "Sale Global Discounts" +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_global_discount +msgid "Total Global Discounts" +msgstr "" + diff --git a/sale_global_discount/models/__init__.py b/sale_global_discount/models/__init__.py new file mode 100644 index 00000000000..6aacb753131 --- /dev/null +++ b/sale_global_discount/models/__init__.py @@ -0,0 +1 @@ +from . import sale_order diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py new file mode 100644 index 00000000000..146e11d8af8 --- /dev/null +++ b/sale_global_discount/models/sale_order.py @@ -0,0 +1,118 @@ +# Copyright 2020 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + global_discount_ids = fields.Many2many( + comodel_name='global.discount', + string='Sale Global Discounts', + domain="[('discount_scope', '=', 'sale'), " + "('account_id', '!=', False), '|', " + "('company_id', '=', company_id), ('company_id', '=', False)]", + ) + amount_global_discount = fields.Monetary( + string='Total Global Discounts', + compute='_amount_all', + currency_field='currency_id', + readonly=True, + ) + amount_untaxed_before_global_discounts = fields.Monetary( + string='Amount Untaxed Before Discounts', + compute='_amount_all', + currency_field='currency_id', + readonly=True, + ) + amount_total_before_global_discounts = fields.Monetary( + string='Amount Untaxed Before Discounts', + compute='_amount_all', + currency_field='currency_id', + readonly=True, + ) + + @api.model + def get_discounted_global(self, price=0, discounts=None): + """Compute discounts successively""" + discounts = discounts or [] + if not discounts: + return price + discount = discounts.pop(0) + price *= 1 - (discount / 100) + return self.get_discounted_global(price, discounts) + + @api.depends('order_line.price_total', 'global_discount_ids') + def _amount_all(self): + res = super()._amount_all() + for order in self: + amount_untaxed_before_global_discounts = order.amount_untaxed + amount_total_before_global_discounts = order.amount_total + discounts = order.global_discount_ids.mapped('discount') + amount_discounted_untaxed = amount_discounted_tax = 0 + for line in order.order_line: + discounted_subtotal = self.get_discounted_global( + line.price_subtotal, discounts.copy()) + amount_discounted_untaxed += discounted_subtotal + discounted_tax = line.tax_id.compute_all( + discounted_subtotal, line.order_id.currency_id, + 1.0, product=line.product_id, + partner=line.order_id.partner_shipping_id) + amount_discounted_tax += sum( + t.get('amount', 0.0) + for t in discounted_tax.get('taxes', [])) + order.update({ + 'amount_untaxed_before_global_discounts': ( + amount_untaxed_before_global_discounts), + 'amount_total_before_global_discounts': ( + amount_total_before_global_discounts), + 'amount_global_discount': ( + amount_untaxed_before_global_discounts - + amount_discounted_untaxed), + 'amount_untaxed': amount_discounted_untaxed, + 'amount_tax': amount_discounted_tax, + 'amount_total': ( + amount_discounted_untaxed + amount_discounted_tax), + }) + return res + + @api.onchange('partner_id') + def onchange_partner_id(self): + res = super().onchange_partner_id() + if self.partner_id.customer_global_discount_ids: + self.global_discount_ids = ( + self.partner_id.customer_global_discount_ids or + self.parnter_id.commercial_partner_id + .customer_global_discount_ids) + return res + + def _prepare_invoice(self): + invoice_vals = super()._prepare_invoice() + if self.global_discount_ids: + invoice_vals.update({ + 'global_discount_ids': [(6, 0, self.global_discount_ids.ids)], + }) + return invoice_vals + + @api.multi + def action_invoice_create(self, grouped=False, final=False): + res = super().action_invoice_create(grouped=grouped, final=final) + invoices = self.env['account.invoice'].browse(res) + invoices._set_global_discounts() + return res + + def _get_tax_amount_by_group(self): + """We can apply discounts directly by tax groups""" + tax_groups = super()._get_tax_amount_by_group() + discounts = self.global_discount_ids.mapped('discount') + if not discounts: + return tax_groups + round_curr = self.currency_id.round + res = [] + for tax in tax_groups: + tax_amount = round_curr( + self.get_discounted_global(tax[1], discounts.copy())) + tax_base = round_curr( + self.get_discounted_global(tax[2], discounts.copy())) + res.append((tax[0], tax_amount, tax_base, tax[3])) + return res diff --git a/sale_global_discount/readme/CONFIGURE.rst b/sale_global_discount/readme/CONFIGURE.rst new file mode 100644 index 00000000000..d681c7dc1d8 --- /dev/null +++ b/sale_global_discount/readme/CONFIGURE.rst @@ -0,0 +1,14 @@ +To configure this module, you need to: + +#. Go to *Settings > Parameters > Global Discounts* +#. Add a new discount. +#. Choose the discount scope (sales or purchases). +#. You can also restrict it to a certain company if needed. + +You can assign global discounts to partners as well: + +#. Go to a partner that is a company. +#. Go to the *Sales & Purchases* tab. +#. In section sale (if the partner is a customer), you can set sale discounts. +#. In section purchase (if the partner is a supplier), you can set purchase + discounts. diff --git a/sale_global_discount/readme/CONTRIBUTORS.rst b/sale_global_discount/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..fedc8467fd3 --- /dev/null +++ b/sale_global_discount/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Tecnativa `_ + + * David Vidal diff --git a/sale_global_discount/readme/DESCRIPTION.rst b/sale_global_discount/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..10a4a0538f4 --- /dev/null +++ b/sale_global_discount/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +Apply global financial discounts to sales that will be transmited to invoices +and accounting. diff --git a/sale_global_discount/readme/USAGE.rst b/sale_global_discount/readme/USAGE.rst new file mode 100644 index 00000000000..a1e6dd63e49 --- /dev/null +++ b/sale_global_discount/readme/USAGE.rst @@ -0,0 +1,9 @@ +To use this module, you need to: + +#. Create a new sale order and choose a partner. +#. If the partner has customer global discounts set, those will be applied to + the order by default. +#. Otherwise, you can set them manually from the header of the sale order. +#. In the order footer, you can see the computed discounts. +#. When you create an invoice from the order, the proper global discounts will + be applied on it. diff --git a/sale_global_discount/static/description/icon.png b/sale_global_discount/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/sale_global_discount/static/description/icon.svg b/sale_global_discount/static/description/icon.svg new file mode 100644 index 00000000000..a7a26d0932a --- /dev/null +++ b/sale_global_discount/static/description/icon.svg @@ -0,0 +1,79 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/sale_global_discount/static/description/index.html b/sale_global_discount/static/description/index.html new file mode 100644 index 00000000000..128d08cbfdb --- /dev/null +++ b/sale_global_discount/static/description/index.html @@ -0,0 +1,456 @@ + + + + + + +Sale Global Discount + + + +
+

Sale Global Discount

+ + +

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

+

Apply global financial discounts to sales that will be transmited to invoices +and accounting.

+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to:

+
    +
  1. Go to Settings > Parameters > Global Discounts
  2. +
  3. Add a new discount.
  4. +
  5. Choose the discount scope (sales or purchases).
  6. +
  7. You can also restrict it to a certain company if needed.
  8. +
+

You can assign global discounts to partners as well:

+
    +
  1. Go to a partner that is a company.
  2. +
  3. Go to the Sales & Purchases tab.
  4. +
  5. In section sale (if the partner is a customer), you can set sale discounts.
  6. +
  7. In section purchase (if the partner is a supplier), you can set purchase +discounts.
  8. +
+
+
+

Usage

+

To use this module, you need to:

+
    +
  1. Create a new sale order and choose a partner.
  2. +
  3. If the partner has customer global discounts set, those will be applied to +the order by default.
  4. +
  5. Otherwise, you can set them manually from the header of the sale order.
  6. +
  7. In the order footer, you can see the computed discounts.
  8. +
  9. When you create an invoice from the order, the proper global discounts will +be applied on it.
  10. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/sale-workflow project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/sale_global_discount/tests/__init__.py b/sale_global_discount/tests/__init__.py new file mode 100644 index 00000000000..ad431ef2fdd --- /dev/null +++ b/sale_global_discount/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_global_discount diff --git a/sale_global_discount/tests/test_sale_global_discount.py b/sale_global_discount/tests/test_sale_global_discount.py new file mode 100644 index 00000000000..c2f75e94cae --- /dev/null +++ b/sale_global_discount/tests/test_sale_global_discount.py @@ -0,0 +1,177 @@ +# Copyright 2020 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.tests import common + + +class TestSaleGlobalDiscount(common.SavepointCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.account_type = cls.env['account.account.type'].create({ + 'name': 'Test', + 'type': 'receivable', + }) + cls.account = cls.env['account.account'].create({ + 'name': 'Test account', + 'code': 'TEST', + 'user_type_id': cls.account_type.id, + 'reconcile': True, + }) + cls.global_discount_obj = cls.env['global.discount'] + cls.global_discount_1 = cls.global_discount_obj.create({ + 'name': 'Test Discount 1', + 'sequence': 1, + 'discount_scope': 'sale', + 'discount': 20, + 'account_id': cls.account.id, + }) + cls.global_discount_2 = cls.global_discount_obj.create({ + 'name': 'Test Discount 2', + 'sequence': 2, + 'discount_scope': 'sale', + 'discount': 30, + 'account_id': cls.account.id, + }) + cls.global_discount_3 = cls.global_discount_obj.create({ + 'name': 'Test Discount 3', + 'sequence': 3, + 'discount_scope': 'sale', + 'discount': 50, + 'account_id': cls.account.id, + }) + cls.partner_1 = cls.env['res.partner'].create({ + 'name': 'Mr. Odoo', + }) + cls.partner_2 = cls.env['res.partner'].create({ + 'name': 'Mrs. Odoo', + }) + cls.partner_2.customer_global_discount_ids = ( + cls.global_discount_2 + cls.global_discount_3) + cls.product_1 = cls.env['product.product'].create({ + 'name': 'Test Product 1', + 'type': 'service', + }) + cls.product_2 = cls.product_1.copy({ + 'name': 'Test Product 2', + }) + cls.tax_group_5pc = cls.env['account.tax.group'].create({ + 'name': 'Test Tax Group 5%', + 'sequence': 1, + }) + cls.tax_group_15pc = cls.env['account.tax.group'].create({ + 'name': 'Test Tax Group 15%', + 'sequence': 2, + }) + cls.tax_1 = cls.env['account.tax'].create({ + 'name': 'Test TAX 15%', + 'amount_type': 'percent', + 'type_tax_use': 'sale', + 'tax_group_id': cls.tax_group_15pc.id, + 'amount': 15.0, + }) + cls.tax_2 = cls.env['account.tax'].create({ + 'name': 'TAX 5%', + 'amount_type': 'percent', + 'tax_group_id': cls.tax_group_5pc.id, + 'type_tax_use': 'sale', + 'amount': 5.0, + }) + cls.sale = cls.env['sale.order'].create({ + 'partner_id': cls.partner_1.id, + 'order_line': [(0, 0, { + 'product_id': cls.product_1.id, + 'product_uom_qty': 2, + 'price_unit': 75.00, + # Test compound taxes as they tend to provoke corner cases + 'tax_id': [(6, 0, [cls.tax_1.id, cls.tax_2.id])] + }), (0, 0, { + 'name': 'On Site Assistance', + 'product_id': cls.product_2.id, + 'product_uom_qty': 3, + 'price_unit': 33.33, + 'tax_id': [(6, 0, cls.tax_1.ids)] + })], + }) + + def test_01_global_sale_succesive_discounts(self): + """Add global discounts to the sale order""" + self.assertAlmostEqual(self.sale.amount_total, 294.99) + self.assertAlmostEqual(self.sale.amount_tax, 45) + self.assertAlmostEqual(self.sale.amount_untaxed, 249.99) + # Apply a single 20% global discount + self.sale.global_discount_ids = self.global_discount_1 + # Discount is computed over the base and global taxes are computed + # according to it line by line with the core method + self.assertAlmostEqual(self.sale.amount_global_discount, 50) + self.assertAlmostEqual(self.sale.amount_untaxed, 199.99) + self.assertAlmostEqual( + self.sale.amount_untaxed_before_global_discounts, 249.99) + self.assertAlmostEqual(self.sale.amount_total, 235.99) + self.assertAlmostEqual( + self.sale.amount_total_before_global_discounts, 294.99) + self.assertAlmostEqual(self.sale.amount_tax, 36) + # Apply an additional 30% global discount + self.sale.global_discount_ids += self.global_discount_2 + self.assertAlmostEqual(self.sale.amount_global_discount, 110) + self.assertAlmostEqual(self.sale.amount_untaxed, 139.99) + self.assertAlmostEqual( + self.sale.amount_untaxed_before_global_discounts, 249.99) + self.assertAlmostEqual(self.sale.amount_total, 165.19) + self.assertAlmostEqual( + self.sale.amount_total_before_global_discounts, 294.99) + self.assertAlmostEqual(self.sale.amount_tax, 25.2) + + def test_02_global_sale_discounts_from_partner(self): + """Change the partner and his global discounts go to the invoice""" + self.assertAlmostEqual(self.sale.amount_total, 294.99) + self.assertAlmostEqual(self.sale.amount_tax, 45) + self.assertAlmostEqual(self.sale.amount_untaxed, 249.99) + # Change the partner and his globlal discounts are fetched + # (30% then 50%) + self.sale.partner_id = self.partner_2 + self.sale.onchange_partner_id() + self.assertAlmostEqual(self.sale.amount_global_discount, 162.49) + self.assertAlmostEqual(self.sale.amount_untaxed, 87.5) + self.assertAlmostEqual( + self.sale.amount_untaxed_before_global_discounts, 249.99) + self.assertAlmostEqual(self.sale.amount_total, 103.26) + self.assertAlmostEqual( + self.sale.amount_total_before_global_discounts, 294.99) + self.assertAlmostEqual(self.sale.amount_tax, 15.76) + + def test_03_global_sale_discounts_to_invoice(self): + """All the discounts go to the invoice""" + self.sale.partner_id = self.partner_2 + self.sale.onchange_partner_id() + self.sale.action_confirm() + self.sale.action_invoice_create() + invoice = self.sale.invoice_ids + self.assertEqual(len(invoice.invoice_global_discount_ids), 2) + line_tax_1 = invoice.tax_line_ids.filtered( + lambda x: x.tax_id == self.tax_1) + line_tax_2 = invoice.tax_line_ids.filtered( + lambda x: x.tax_id == self.tax_2) + self.assertAlmostEqual(line_tax_1.base, 87.5) + self.assertAlmostEqual(line_tax_2.base, 52.5) + self.assertAlmostEqual(line_tax_1.amount, 13.13) + self.assertAlmostEqual(line_tax_2.amount, 2.63) + discount_amount = sum( + invoice.invoice_global_discount_ids.mapped('discount_amount')) + self.assertAlmostEqual(discount_amount, 162.49) + self.assertAlmostEqual( + invoice.amount_untaxed_before_global_discounts, 249.99) + self.assertAlmostEqual(invoice.amount_untaxed, 87.5) + self.assertAlmostEqual(invoice.amount_total, 103.26) + + def test_04_report_taxes(self): + """Taxes by group shown in reports""" + self.sale.partner_id = self.partner_2 + self.sale.onchange_partner_id() + taxes_groups = self.sale._get_tax_amount_by_group() + # Taxes + self.assertAlmostEqual(taxes_groups[0][1], 2.63) + self.assertAlmostEqual(taxes_groups[1][1], 13.13) + # Bases + self.assertAlmostEqual(taxes_groups[0][2], 52.5) + self.assertAlmostEqual(taxes_groups[1][2], 87.5) diff --git a/sale_global_discount/views/report_sale_order.xml b/sale_global_discount/views/report_sale_order.xml new file mode 100644 index 00000000000..1995351e815 --- /dev/null +++ b/sale_global_discount/views/report_sale_order.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/sale_global_discount/views/sale_order_views.xml b/sale_global_discount/views/sale_order_views.xml new file mode 100644 index 00000000000..0be75186df6 --- /dev/null +++ b/sale_global_discount/views/sale_order_views.xml @@ -0,0 +1,21 @@ + + + + + + + sale.order + + + + + + + + + + + + + From 21dd15722126992758ecaf343b06033e9a932b49 Mon Sep 17 00:00:00 2001 From: David Vidal Date: Thu, 30 Apr 2020 07:44:52 +0000 Subject: [PATCH 02/38] Translated using Weblate (Spanish) Currently translated at 100.0% (6 of 6 strings) Translation: sale-workflow-11.0/sale-workflow-11.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-11-0/sale-workflow-11-0-sale_global_discount/es/ --- sale_global_discount/i18n/es.po | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sale_global_discount/i18n/es.po b/sale_global_discount/i18n/es.po index 762ca42853e..a7915602d83 100644 --- a/sale_global_discount/i18n/es.po +++ b/sale_global_discount/i18n/es.po @@ -7,19 +7,20 @@ msgstr "" "Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-04-06 14:38+0000\n" -"PO-Revision-Date: 2020-04-06 14:38+0000\n" -"Last-Translator: <>\n" +"PO-Revision-Date: 2020-04-30 10:19+0000\n" +"Last-Translator: David Vidal \n" "Language-Team: \n" -"Language: \n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Plural-Forms: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.10\n" #. module: sale_global_discount #: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document msgid "Global Discounts
" -msgstr "" +msgstr "Descuentos Globales
" #. module: sale_global_discount #: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document From c381036d2fece2cfac5714710a86bd481e5dec28 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 30 Jun 2020 07:21:11 +0200 Subject: [PATCH 03/38] [FIX] sale_global_discount: wrong property name --- sale_global_discount/__manifest__.py | 2 +- sale_global_discount/models/sale_order.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py index 0fc29784a2e..7341e88dd76 100644 --- a/sale_global_discount/__manifest__.py +++ b/sale_global_discount/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Sale Global Discount', - 'version': '11.0.1.0.1', + 'version': '11.0.1.0.2', 'category': 'Sales Management', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py index 146e11d8af8..311d94e9186 100644 --- a/sale_global_discount/models/sale_order.py +++ b/sale_global_discount/models/sale_order.py @@ -82,7 +82,7 @@ def onchange_partner_id(self): if self.partner_id.customer_global_discount_ids: self.global_discount_ids = ( self.partner_id.customer_global_discount_ids or - self.parnter_id.commercial_partner_id + self.partner_id.commercial_partner_id .customer_global_discount_ids) return res From d49125d013acc3c558d796b17594f8763ec09ad7 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Fri, 3 Jul 2020 17:44:29 +0200 Subject: [PATCH 04/38] [FIX] sale_global_discount: Constraint tax lines combinations Not all taxes combinations are possible with global discounts when they are later invoiced and posted, so we need to constraint them early. OCA/account-invoicing#745 addresses the problem at invoice level, posting correctly taxes for supported cases, and constraining the rest. We apply here the same constraints at sales order level. Existing tests have been changed because they were using one of the forbidden taxes combination, and 2 new tests have been added for covering the new constraints. --- sale_global_discount/README.rst | 7 +++ sale_global_discount/__manifest__.py | 2 +- sale_global_discount/i18n/es.po | 12 +++++ .../i18n/sale_global_discount.pot | 12 +++++ sale_global_discount/models/sale_order.py | 35 ++++++++++--- sale_global_discount/readme/ROADMAP.rst | 3 ++ .../static/description/index.html | 29 +++++++---- .../tests/test_sale_global_discount.py | 51 +++++++++++-------- 8 files changed, 113 insertions(+), 38 deletions(-) create mode 100644 sale_global_discount/readme/ROADMAP.rst diff --git a/sale_global_discount/README.rst b/sale_global_discount/README.rst index 7d111259f9c..08389981d06 100644 --- a/sale_global_discount/README.rst +++ b/sale_global_discount/README.rst @@ -64,6 +64,13 @@ To use this module, you need to: #. When you create an invoice from the order, the proper global discounts will be applied on it. +Known issues / Roadmap +====================== + +* Not all the taxes combination can be compatible with global discounts. An + error is raised in that cases. +* Currently, taxes in invoice lines are mandatory with global discounts. + Bug Tracker =========== diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py index 7341e88dd76..2f25f62b2bf 100644 --- a/sale_global_discount/__manifest__.py +++ b/sale_global_discount/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Sale Global Discount', - 'version': '11.0.1.0.2', + 'version': '11.0.1.0.3', 'category': 'Sales Management', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/sale_global_discount/i18n/es.po b/sale_global_discount/i18n/es.po index a7915602d83..9bba9a4d7ae 100644 --- a/sale_global_discount/i18n/es.po +++ b/sale_global_discount/i18n/es.po @@ -33,6 +33,12 @@ msgstr "Subtl. antes desc." msgid "Amount Untaxed Before Discounts" msgstr "Base Imponible sin Descuentos" +#. module: sale_global_discount +#: code:addons/sale_global_discount/models/sale_order.py:62 +#, python-format +msgid "Incompatible taxes found for global discounts." +msgstr "" + #. module: sale_global_discount #: model:ir.model,name:sale_global_discount.model_sale_order msgid "Quotation" @@ -48,5 +54,11 @@ msgstr "Descuentos de venta globales" msgid "Total Global Discounts" msgstr "Total Descuentos Globales" +#. module: sale_global_discount +#: code:addons/sale_global_discount/models/sale_order.py:55 +#, python-format +msgid "With global discounts, taxes in lines are required." +msgstr "" + #~ msgid "Global Discount" #~ msgstr "Descuento Global" diff --git a/sale_global_discount/i18n/sale_global_discount.pot b/sale_global_discount/i18n/sale_global_discount.pot index a915a589ed2..f676dc0ad84 100644 --- a/sale_global_discount/i18n/sale_global_discount.pot +++ b/sale_global_discount/i18n/sale_global_discount.pot @@ -29,6 +29,12 @@ msgstr "" msgid "Amount Untaxed Before Discounts" msgstr "" +#. module: sale_global_discount +#: code:addons/sale_global_discount/models/sale_order.py:62 +#, python-format +msgid "Incompatible taxes found for global discounts." +msgstr "" + #. module: sale_global_discount #: model:ir.model,name:sale_global_discount.model_sale_order msgid "Quotation" @@ -44,3 +50,9 @@ msgstr "" msgid "Total Global Discounts" msgstr "" +#. module: sale_global_discount +#: code:addons/sale_global_discount/models/sale_order.py:55 +#, python-format +msgid "With global discounts, taxes in lines are required." +msgstr "" + diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py index 311d94e9186..7d0de34a5cb 100644 --- a/sale_global_discount/models/sale_order.py +++ b/sale_global_discount/models/sale_order.py @@ -1,6 +1,6 @@ # Copyright 2020 Tecnativa - David Vidal # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import _, api, exceptions, fields, models class SaleOrder(models.Model): @@ -42,10 +42,34 @@ def get_discounted_global(self, price=0, discounts=None): price *= 1 - (discount / 100) return self.get_discounted_global(price, discounts) + def _check_global_discounts_sanity(self): + """Perform a sanity check for discarding cases that will lead to + incorrect data in discounts. + """ + self.ensure_one() + if not self.global_discount_ids: + return True + taxes_keys = {} + for line in self.order_line: + if not line.tax_id: + raise exceptions.UserError(_( + "With global discounts, taxes in lines are required." + )) + for key in taxes_keys: + if key == line.tax_id: + break + elif key & line.tax_id: + raise exceptions.UserError(_( + "Incompatible taxes found for global discounts." + )) + else: + taxes_keys[line.tax_id] = True + @api.depends('order_line.price_total', 'global_discount_ids') def _amount_all(self): res = super()._amount_all() for order in self: + order._check_global_discounts_sanity() amount_untaxed_before_global_discounts = order.amount_untaxed amount_total_before_global_discounts = order.amount_total discounts = order.global_discount_ids.mapped('discount') @@ -79,11 +103,10 @@ def _amount_all(self): @api.onchange('partner_id') def onchange_partner_id(self): res = super().onchange_partner_id() - if self.partner_id.customer_global_discount_ids: - self.global_discount_ids = ( - self.partner_id.customer_global_discount_ids or - self.partner_id.commercial_partner_id - .customer_global_discount_ids) + self.global_discount_ids = ( + self.partner_id.customer_global_discount_ids or + self.partner_id.commercial_partner_id + .customer_global_discount_ids) return res def _prepare_invoice(self): diff --git a/sale_global_discount/readme/ROADMAP.rst b/sale_global_discount/readme/ROADMAP.rst new file mode 100644 index 00000000000..8e5c2e79bc8 --- /dev/null +++ b/sale_global_discount/readme/ROADMAP.rst @@ -0,0 +1,3 @@ +* Not all the taxes combination can be compatible with global discounts. An + error is raised in that cases. +* Currently, taxes in invoice lines are mandatory with global discounts. diff --git a/sale_global_discount/static/description/index.html b/sale_global_discount/static/description/index.html index 128d08cbfdb..3bc0c5e2046 100644 --- a/sale_global_discount/static/description/index.html +++ b/sale_global_discount/static/description/index.html @@ -375,11 +375,12 @@

Sale Global Discount

  • Configuration
  • Usage
  • -
  • Bug Tracker
  • -
  • Credits @@ -415,8 +416,16 @@

    Usage

    be applied on it.
  • +
    +

    Known issues / Roadmap

    +
      +
    • Not all the taxes combination can be compatible with global discounts. An +error is raised in that cases.
    • +
    • Currently, taxes in invoice lines are mandatory with global discounts.
    • +
    +
    -

    Bug Tracker

    +

    Bug Tracker

    Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed @@ -424,15 +433,15 @@

    Bug Tracker

    Do not contact contributors directly about support or help with technical issues.

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • Tecnativa
    -

    Contributors

    +

    Contributors

    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association

    OCA, or the Odoo Community Association, is a nonprofit organization whose diff --git a/sale_global_discount/tests/test_sale_global_discount.py b/sale_global_discount/tests/test_sale_global_discount.py index c2f75e94cae..9ccc09b3b89 100644 --- a/sale_global_discount/tests/test_sale_global_discount.py +++ b/sale_global_discount/tests/test_sale_global_discount.py @@ -1,5 +1,6 @@ # Copyright 2020 Tecnativa - David Vidal # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import exceptions from odoo.tests import common @@ -90,14 +91,14 @@ def setUpClass(cls): 'product_id': cls.product_2.id, 'product_uom_qty': 3, 'price_unit': 33.33, - 'tax_id': [(6, 0, cls.tax_1.ids)] + 'tax_id': [(6, 0, [cls.tax_1.id, cls.tax_2.id])] })], }) def test_01_global_sale_succesive_discounts(self): """Add global discounts to the sale order""" - self.assertAlmostEqual(self.sale.amount_total, 294.99) - self.assertAlmostEqual(self.sale.amount_tax, 45) + self.assertAlmostEqual(self.sale.amount_total, 299.99) + self.assertAlmostEqual(self.sale.amount_tax, 50) self.assertAlmostEqual(self.sale.amount_untaxed, 249.99) # Apply a single 20% global discount self.sale.global_discount_ids = self.global_discount_1 @@ -107,27 +108,23 @@ def test_01_global_sale_succesive_discounts(self): self.assertAlmostEqual(self.sale.amount_untaxed, 199.99) self.assertAlmostEqual( self.sale.amount_untaxed_before_global_discounts, 249.99) - self.assertAlmostEqual(self.sale.amount_total, 235.99) + self.assertAlmostEqual(self.sale.amount_total, 239.99) self.assertAlmostEqual( - self.sale.amount_total_before_global_discounts, 294.99) - self.assertAlmostEqual(self.sale.amount_tax, 36) + self.sale.amount_total_before_global_discounts, 299.99) + self.assertAlmostEqual(self.sale.amount_tax, 40) # Apply an additional 30% global discount self.sale.global_discount_ids += self.global_discount_2 self.assertAlmostEqual(self.sale.amount_global_discount, 110) self.assertAlmostEqual(self.sale.amount_untaxed, 139.99) self.assertAlmostEqual( self.sale.amount_untaxed_before_global_discounts, 249.99) - self.assertAlmostEqual(self.sale.amount_total, 165.19) + self.assertAlmostEqual(self.sale.amount_total, 167.99) self.assertAlmostEqual( - self.sale.amount_total_before_global_discounts, 294.99) - self.assertAlmostEqual(self.sale.amount_tax, 25.2) + self.sale.amount_total_before_global_discounts, 299.99) + self.assertAlmostEqual(self.sale.amount_tax, 28) def test_02_global_sale_discounts_from_partner(self): """Change the partner and his global discounts go to the invoice""" - self.assertAlmostEqual(self.sale.amount_total, 294.99) - self.assertAlmostEqual(self.sale.amount_tax, 45) - self.assertAlmostEqual(self.sale.amount_untaxed, 249.99) - # Change the partner and his globlal discounts are fetched # (30% then 50%) self.sale.partner_id = self.partner_2 self.sale.onchange_partner_id() @@ -135,10 +132,10 @@ def test_02_global_sale_discounts_from_partner(self): self.assertAlmostEqual(self.sale.amount_untaxed, 87.5) self.assertAlmostEqual( self.sale.amount_untaxed_before_global_discounts, 249.99) - self.assertAlmostEqual(self.sale.amount_total, 103.26) + self.assertAlmostEqual(self.sale.amount_total, 105.01) self.assertAlmostEqual( - self.sale.amount_total_before_global_discounts, 294.99) - self.assertAlmostEqual(self.sale.amount_tax, 15.76) + self.sale.amount_total_before_global_discounts, 299.99) + self.assertAlmostEqual(self.sale.amount_tax, 17.51) def test_03_global_sale_discounts_to_invoice(self): """All the discounts go to the invoice""" @@ -153,16 +150,16 @@ def test_03_global_sale_discounts_to_invoice(self): line_tax_2 = invoice.tax_line_ids.filtered( lambda x: x.tax_id == self.tax_2) self.assertAlmostEqual(line_tax_1.base, 87.5) - self.assertAlmostEqual(line_tax_2.base, 52.5) + self.assertAlmostEqual(line_tax_2.base, 87.5) self.assertAlmostEqual(line_tax_1.amount, 13.13) - self.assertAlmostEqual(line_tax_2.amount, 2.63) + self.assertAlmostEqual(line_tax_2.amount, 4.38) discount_amount = sum( invoice.invoice_global_discount_ids.mapped('discount_amount')) self.assertAlmostEqual(discount_amount, 162.49) self.assertAlmostEqual( invoice.amount_untaxed_before_global_discounts, 249.99) self.assertAlmostEqual(invoice.amount_untaxed, 87.5) - self.assertAlmostEqual(invoice.amount_total, 103.26) + self.assertAlmostEqual(invoice.amount_total, 105.01) def test_04_report_taxes(self): """Taxes by group shown in reports""" @@ -170,8 +167,20 @@ def test_04_report_taxes(self): self.sale.onchange_partner_id() taxes_groups = self.sale._get_tax_amount_by_group() # Taxes - self.assertAlmostEqual(taxes_groups[0][1], 2.63) + self.assertAlmostEqual(taxes_groups[0][1], 4.38) self.assertAlmostEqual(taxes_groups[1][1], 13.13) # Bases - self.assertAlmostEqual(taxes_groups[0][2], 52.5) + self.assertAlmostEqual(taxes_groups[0][2], 87.5) self.assertAlmostEqual(taxes_groups[1][2], 87.5) + + def test_05_incompatible_taxes(self): + # Line 1 with tax 1 and tax 2 + # Line 2 with only tax 2 + self.sale.order_line[1].tax_id = [(6, 0, self.tax_1.ids)] + with self.assertRaises(exceptions.UserError): + self.sale.global_discount_ids = self.global_discount_1 + + def test_06_no_taxes(self): + self.sale.order_line[1].tax_id = False + with self.assertRaises(exceptions.UserError): + self.sale.global_discount_ids = self.global_discount_1 From fb1237fae8e1377272d06eb77ea158f16891f64e Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Mon, 20 Jul 2020 09:57:34 +0200 Subject: [PATCH 05/38] [MIG] sale_global_discount: Migration to 12.0 * Standard procedure * Code adaptation * README regeneration * Tests adaptation [UPD] Update sale_global_discount.pot Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/ Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/ --- sale_global_discount/README.rst | 13 +++---- sale_global_discount/__manifest__.py | 3 +- sale_global_discount/i18n/es.po | 34 ++++++++++++------- .../i18n/sale_global_discount.pot | 28 ++++++++------- sale_global_discount/models/sale_order.py | 29 ++++++++-------- sale_global_discount/readme/CONFIGURE.rst | 2 +- sale_global_discount/readme/CONTRIBUTORS.rst | 1 + .../static/description/index.html | 9 ++--- .../tests/test_sale_global_discount.py | 3 +- 9 files changed, 70 insertions(+), 52 deletions(-) diff --git a/sale_global_discount/README.rst b/sale_global_discount/README.rst index 08389981d06..89d3aa95cd3 100644 --- a/sale_global_discount/README.rst +++ b/sale_global_discount/README.rst @@ -14,13 +14,13 @@ Sale Global Discount :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github - :target: https://github.com/OCA/sale-workflow/tree/11.0/sale_global_discount + :target: https://github.com/OCA/sale-workflow/tree/12.0/sale_global_discount :alt: OCA/sale-workflow .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/sale-workflow-11-0/sale-workflow-11-0-sale_global_discount + :target: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/167/11.0 + :target: https://runbot.odoo-community.org/runbot/167/12.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -38,7 +38,7 @@ Configuration To configure this module, you need to: -#. Go to *Settings > Parameters > Global Discounts* +#. Go to *Settings > Technical > Parameters > Global Discounts* #. Add a new discount. #. Choose the discount scope (sales or purchases). #. You can also restrict it to a certain company if needed. @@ -77,7 +77,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -95,6 +95,7 @@ Contributors * `Tecnativa `_ * David Vidal + * Pedro M. Baeza Maintainers ~~~~~~~~~~~ @@ -109,6 +110,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/sale-workflow `_ project on GitHub. +This module is part of the `OCA/sale-workflow `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py index 2f25f62b2bf..50f40ea93f1 100644 --- a/sale_global_discount/__manifest__.py +++ b/sale_global_discount/__manifest__.py @@ -1,8 +1,9 @@ # Copyright 2020 Tecnativa - David Vidal +# Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Sale Global Discount', - 'version': '11.0.1.0.3', + 'version': '12.0.1.0.0', 'category': 'Sales Management', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/sale_global_discount/i18n/es.po b/sale_global_discount/i18n/es.po index 9bba9a4d7ae..17c7ca22ede 100644 --- a/sale_global_discount/i18n/es.po +++ b/sale_global_discount/i18n/es.po @@ -18,47 +18,55 @@ msgstr "" "X-Generator: Weblate 3.10\n" #. module: sale_global_discount -#: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +#: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document msgid "Global Discounts
    " msgstr "Descuentos Globales
    " #. module: sale_global_discount -#: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +#: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document msgid "Subtl. before disc." msgstr "Subtl. antes desc." #. module: sale_global_discount -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_total_before_global_discounts -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_total_before_global_discounts +#, fuzzy +msgid "Amount Total Before Discounts" +msgstr "Base Imponible sin Descuentos" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_untaxed_before_global_discounts msgid "Amount Untaxed Before Discounts" msgstr "Base Imponible sin Descuentos" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:62 +#: code:addons/sale_global_discount/models/sale_order.py:63 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" #. module: sale_global_discount -#: model:ir.model,name:sale_global_discount.model_sale_order -msgid "Quotation" -msgstr "Presupuesto" - -#. module: sale_global_discount -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_global_discount_ids +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids msgid "Sale Global Discounts" msgstr "Descuentos de venta globales" #. module: sale_global_discount -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_global_discount +#: model:ir.model,name:sale_global_discount.model_sale_order +msgid "Sale Order" +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_global_discount msgid "Total Global Discounts" msgstr "Total Descuentos Globales" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:55 +#: code:addons/sale_global_discount/models/sale_order.py:56 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" +#~ msgid "Quotation" +#~ msgstr "Presupuesto" + #~ msgid "Global Discount" #~ msgstr "Descuento Global" diff --git a/sale_global_discount/i18n/sale_global_discount.pot b/sale_global_discount/i18n/sale_global_discount.pot index f676dc0ad84..82c79e8a8f8 100644 --- a/sale_global_discount/i18n/sale_global_discount.pot +++ b/sale_global_discount/i18n/sale_global_discount.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" +"Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: <>\n" "Language-Team: \n" @@ -14,44 +14,48 @@ msgstr "" "Plural-Forms: \n" #. module: sale_global_discount -#: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +#: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document msgid "Global Discounts
    " msgstr "" #. module: sale_global_discount -#: model:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +#: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document msgid "Subtl. before disc." msgstr "" #. module: sale_global_discount -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_total_before_global_discounts -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_total_before_global_discounts +msgid "Amount Total Before Discounts" +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_untaxed_before_global_discounts msgid "Amount Untaxed Before Discounts" msgstr "" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:62 +#: code:addons/sale_global_discount/models/sale_order.py:63 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" #. module: sale_global_discount -#: model:ir.model,name:sale_global_discount.model_sale_order -msgid "Quotation" +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids +msgid "Sale Global Discounts" msgstr "" #. module: sale_global_discount -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_global_discount_ids -msgid "Sale Global Discounts" +#: model:ir.model,name:sale_global_discount.model_sale_order +msgid "Sale Order" msgstr "" #. module: sale_global_discount -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order_amount_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_global_discount msgid "Total Global Discounts" msgstr "" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:55 +#: code:addons/sale_global_discount/models/sale_order.py:56 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py index 7d0de34a5cb..10d166f9f75 100644 --- a/sale_global_discount/models/sale_order.py +++ b/sale_global_discount/models/sale_order.py @@ -1,4 +1,5 @@ # Copyright 2020 Tecnativa - David Vidal +# Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import _, api, exceptions, fields, models @@ -26,7 +27,7 @@ class SaleOrder(models.Model): readonly=True, ) amount_total_before_global_discounts = fields.Monetary( - string='Amount Untaxed Before Discounts', + string='Amount Total Before Discounts', compute='_amount_all', currency_field='currency_id', readonly=True, @@ -117,25 +118,25 @@ def _prepare_invoice(self): }) return invoice_vals - @api.multi def action_invoice_create(self, grouped=False, final=False): res = super().action_invoice_create(grouped=grouped, final=final) invoices = self.env['account.invoice'].browse(res) invoices._set_global_discounts() return res - def _get_tax_amount_by_group(self): - """We can apply discounts directly by tax groups""" - tax_groups = super()._get_tax_amount_by_group() + def _amount_by_group(self): + """We can apply discounts directly by tax groups.""" + super()._amount_by_group() discounts = self.global_discount_ids.mapped('discount') if not discounts: - return tax_groups + return round_curr = self.currency_id.round - res = [] - for tax in tax_groups: - tax_amount = round_curr( - self.get_discounted_global(tax[1], discounts.copy())) - tax_base = round_curr( - self.get_discounted_global(tax[2], discounts.copy())) - res.append((tax[0], tax_amount, tax_base, tax[3])) - return res + for order in self: + res = [] + for tax in order.amount_by_group: + tax_amount = round_curr( + self.get_discounted_global(tax[1], discounts.copy())) + tax_base = round_curr( + self.get_discounted_global(tax[2], discounts.copy())) + res.append((tax[0], tax_amount, tax_base, tax[3])) + order.amount_by_group = res diff --git a/sale_global_discount/readme/CONFIGURE.rst b/sale_global_discount/readme/CONFIGURE.rst index d681c7dc1d8..2f46c663449 100644 --- a/sale_global_discount/readme/CONFIGURE.rst +++ b/sale_global_discount/readme/CONFIGURE.rst @@ -1,6 +1,6 @@ To configure this module, you need to: -#. Go to *Settings > Parameters > Global Discounts* +#. Go to *Settings > Technical > Parameters > Global Discounts* #. Add a new discount. #. Choose the discount scope (sales or purchases). #. You can also restrict it to a certain company if needed. diff --git a/sale_global_discount/readme/CONTRIBUTORS.rst b/sale_global_discount/readme/CONTRIBUTORS.rst index fedc8467fd3..ac6cb650339 100644 --- a/sale_global_discount/readme/CONTRIBUTORS.rst +++ b/sale_global_discount/readme/CONTRIBUTORS.rst @@ -1,3 +1,4 @@ * `Tecnativa `_ * David Vidal + * Pedro M. Baeza diff --git a/sale_global_discount/static/description/index.html b/sale_global_discount/static/description/index.html index 3bc0c5e2046..7c94c2f0546 100644 --- a/sale_global_discount/static/description/index.html +++ b/sale_global_discount/static/description/index.html @@ -367,7 +367,7 @@

    Sale Global Discount

    !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

    Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

    +

    Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

    Apply global financial discounts to sales that will be transmited to invoices and accounting.

    Table of contents

    @@ -389,7 +389,7 @@

    Sale Global Discount

    Configuration

    To configure this module, you need to:

      -
    1. Go to Settings > Parameters > Global Discounts
    2. +
    3. Go to Settings > Technical > Parameters > Global Discounts
    4. Add a new discount.
    5. Choose the discount scope (sales or purchases).
    6. You can also restrict it to a certain company if needed.
    7. @@ -429,7 +429,7 @@

      Bug Tracker

      Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

      +feedback.

      Do not contact contributors directly about support or help with technical issues.

    @@ -445,6 +445,7 @@

    Contributors

    @@ -456,7 +457,7 @@

    Maintainers

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    -

    This module is part of the OCA/sale-workflow project on GitHub.

    +

    This module is part of the OCA/sale-workflow project on GitHub.

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    diff --git a/sale_global_discount/tests/test_sale_global_discount.py b/sale_global_discount/tests/test_sale_global_discount.py index 9ccc09b3b89..56da6372c95 100644 --- a/sale_global_discount/tests/test_sale_global_discount.py +++ b/sale_global_discount/tests/test_sale_global_discount.py @@ -165,8 +165,9 @@ def test_04_report_taxes(self): """Taxes by group shown in reports""" self.sale.partner_id = self.partner_2 self.sale.onchange_partner_id() - taxes_groups = self.sale._get_tax_amount_by_group() + self.sale._amount_by_group() # Taxes + taxes_groups = self.sale.amount_by_group self.assertAlmostEqual(taxes_groups[0][1], 4.38) self.assertAlmostEqual(taxes_groups[1][1], 13.13) # Bases From da0fd9638dad3765fc0913a35acfcfc4f51f5f4f Mon Sep 17 00:00:00 2001 From: Pedro Castro Silva Date: Tue, 15 Sep 2020 10:58:12 +0000 Subject: [PATCH 06/38] Added translation using Weblate (Portuguese) --- sale_global_discount/i18n/pt.po | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 sale_global_discount/i18n/pt.po diff --git a/sale_global_discount/i18n/pt.po b/sale_global_discount/i18n/pt.po new file mode 100644 index 00000000000..213f609de08 --- /dev/null +++ b/sale_global_discount/i18n/pt.po @@ -0,0 +1,62 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_global_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" + +#. module: sale_global_discount +#: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +msgid "Global Discounts
    " +msgstr "" + +#. module: sale_global_discount +#: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document +msgid "Subtl. before disc." +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_total_before_global_discounts +msgid "Amount Total Before Discounts" +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_untaxed_before_global_discounts +msgid "Amount Untaxed Before Discounts" +msgstr "" + +#. module: sale_global_discount +#: code:addons/sale_global_discount/models/sale_order.py:63 +#, python-format +msgid "Incompatible taxes found for global discounts." +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids +msgid "Sale Global Discounts" +msgstr "" + +#. module: sale_global_discount +#: model:ir.model,name:sale_global_discount.model_sale_order +msgid "Sale Order" +msgstr "" + +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_global_discount +msgid "Total Global Discounts" +msgstr "" + +#. module: sale_global_discount +#: code:addons/sale_global_discount/models/sale_order.py:56 +#, python-format +msgid "With global discounts, taxes in lines are required." +msgstr "" From f8bf3eea59e6d07221ffb87950e7b6c98e27be1b Mon Sep 17 00:00:00 2001 From: Pedro Castro Silva Date: Tue, 15 Sep 2020 10:58:34 +0000 Subject: [PATCH 07/38] Translated using Weblate (Portuguese) Currently translated at 100.0% (9 of 9 strings) Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/pt/ --- sale_global_discount/i18n/pt.po | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sale_global_discount/i18n/pt.po b/sale_global_discount/i18n/pt.po index 213f609de08..39c5188a383 100644 --- a/sale_global_discount/i18n/pt.po +++ b/sale_global_discount/i18n/pt.po @@ -6,57 +6,59 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" +"PO-Revision-Date: 2020-09-15 14:00+0000\n" +"Last-Translator: Pedro Castro Silva \n" "Language-Team: none\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 3.10\n" #. module: sale_global_discount #: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document msgid "Global Discounts
    " -msgstr "" +msgstr "Descontos Globais
    " #. module: sale_global_discount #: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document msgid "Subtl. before disc." -msgstr "" +msgstr "Subtotal s/ desc." #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_total_before_global_discounts msgid "Amount Total Before Discounts" -msgstr "" +msgstr "Total sem Descontos" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_untaxed_before_global_discounts msgid "Amount Untaxed Before Discounts" -msgstr "" +msgstr "Total sem Impostos antes de Descontos" #. module: sale_global_discount #: code:addons/sale_global_discount/models/sale_order.py:63 #, python-format msgid "Incompatible taxes found for global discounts." -msgstr "" +msgstr "Foram encontrados impostos incompatíveis para descontos globais." #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids msgid "Sale Global Discounts" -msgstr "" +msgstr "Descontos Globais de Venda" #. module: sale_global_discount #: model:ir.model,name:sale_global_discount.model_sale_order msgid "Sale Order" -msgstr "" +msgstr "Encomenda de Venda" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_global_discount msgid "Total Global Discounts" -msgstr "" +msgstr "Total de Descontos Globais" #. module: sale_global_discount #: code:addons/sale_global_discount/models/sale_order.py:56 #, python-format msgid "With global discounts, taxes in lines are required." -msgstr "" +msgstr "Com descontos globais, é obrigatório ter impostos nas linhas." From 1b4e38aaa353319166c845314d907a87979bcc76 Mon Sep 17 00:00:00 2001 From: Pedro Castro Silva Date: Thu, 17 Sep 2020 11:20:22 +0000 Subject: [PATCH 08/38] Translated using Weblate (Portuguese) Currently translated at 100.0% (9 of 9 strings) Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/pt/ --- sale_global_discount/i18n/pt.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sale_global_discount/i18n/pt.po b/sale_global_discount/i18n/pt.po index 39c5188a383..ae8b44e540a 100644 --- a/sale_global_discount/i18n/pt.po +++ b/sale_global_discount/i18n/pt.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2020-09-15 14:00+0000\n" +"PO-Revision-Date: 2020-09-17 14:00+0000\n" "Last-Translator: Pedro Castro Silva \n" "Language-Team: none\n" "Language: pt\n" @@ -24,7 +24,7 @@ msgstr "Descontos Globais
    " #. module: sale_global_discount #: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document msgid "Subtl. before disc." -msgstr "Subtotal s/ desc." +msgstr "Subtotal s/ Desc." #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_total_before_global_discounts From 280446bf8bae845e1cd512fdda9954c115f5852a Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Fri, 9 Oct 2020 12:24:48 +0200 Subject: [PATCH 09/38] [FIX] sale_global_discount: Proper amount_by_group results The tuple has changed from 4 to 6 elements. [UPD] Update sale_global_discount.pot Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/ --- sale_global_discount/__manifest__.py | 2 +- sale_global_discount/i18n/es.po | 4 ++-- sale_global_discount/i18n/pt.po | 6 +++--- .../i18n/sale_global_discount.pot | 4 ++-- sale_global_discount/models/sale_order.py | 19 +++++++++++++++++-- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py index 50f40ea93f1..efbf8bd8372 100644 --- a/sale_global_discount/__manifest__.py +++ b/sale_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Sale Global Discount', - 'version': '12.0.1.0.0', + 'version': '12.0.1.0.1', 'category': 'Sales Management', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/sale_global_discount/i18n/es.po b/sale_global_discount/i18n/es.po index 17c7ca22ede..d3dad3095aa 100644 --- a/sale_global_discount/i18n/es.po +++ b/sale_global_discount/i18n/es.po @@ -39,7 +39,7 @@ msgid "Amount Untaxed Before Discounts" msgstr "Base Imponible sin Descuentos" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:63 +#: code:addons/sale_global_discount/models/sale_order.py:65 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" @@ -60,7 +60,7 @@ msgid "Total Global Discounts" msgstr "Total Descuentos Globales" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:56 +#: code:addons/sale_global_discount/models/sale_order.py:58 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/sale_global_discount/i18n/pt.po b/sale_global_discount/i18n/pt.po index ae8b44e540a..1ce845ae4e3 100644 --- a/sale_global_discount/i18n/pt.po +++ b/sale_global_discount/i18n/pt.po @@ -1,6 +1,6 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * sale_global_discount +# * sale_global_discount # msgid "" msgstr "" @@ -37,7 +37,7 @@ msgid "Amount Untaxed Before Discounts" msgstr "Total sem Impostos antes de Descontos" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:63 +#: code:addons/sale_global_discount/models/sale_order.py:65 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "Foram encontrados impostos incompatíveis para descontos globais." @@ -58,7 +58,7 @@ msgid "Total Global Discounts" msgstr "Total de Descontos Globais" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:56 +#: code:addons/sale_global_discount/models/sale_order.py:58 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "Com descontos globais, é obrigatório ter impostos nas linhas." diff --git a/sale_global_discount/i18n/sale_global_discount.pot b/sale_global_discount/i18n/sale_global_discount.pot index 82c79e8a8f8..bb41100f598 100644 --- a/sale_global_discount/i18n/sale_global_discount.pot +++ b/sale_global_discount/i18n/sale_global_discount.pot @@ -34,7 +34,7 @@ msgid "Amount Untaxed Before Discounts" msgstr "" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:63 +#: code:addons/sale_global_discount/models/sale_order.py:65 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" @@ -55,7 +55,7 @@ msgid "Total Global Discounts" msgstr "" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:56 +#: code:addons/sale_global_discount/models/sale_order.py:58 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py index 10d166f9f75..fb2fcb64971 100644 --- a/sale_global_discount/models/sale_order.py +++ b/sale_global_discount/models/sale_order.py @@ -1,7 +1,9 @@ # Copyright 2020 Tecnativa - David Vidal # Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from functools import partial from odoo import _, api, exceptions, fields, models +from odoo.tools.misc import formatLang class SaleOrder(models.Model): @@ -130,13 +132,26 @@ def _amount_by_group(self): discounts = self.global_discount_ids.mapped('discount') if not discounts: return - round_curr = self.currency_id.round for order in self: + round_curr = order.currency_id.round + fmt = partial( + formatLang, self.with_context(lang=order.partner_id.lang).env, + currency_obj=order.currency_id + ) res = [] for tax in order.amount_by_group: tax_amount = round_curr( self.get_discounted_global(tax[1], discounts.copy())) tax_base = round_curr( self.get_discounted_global(tax[2], discounts.copy())) - res.append((tax[0], tax_amount, tax_base, tax[3])) + res.append( + ( + tax[0], + tax_amount, + tax_base, + fmt(tax_amount), + fmt(tax_base), + len(order.amount_by_group) + ) + ) order.amount_by_group = res From ab430206c2f9affb8d392bbc1bc18acdb4fe1e22 Mon Sep 17 00:00:00 2001 From: pedrocasi Date: Tue, 13 Oct 2020 18:41:48 +0100 Subject: [PATCH 10/38] [FIX][12.0] sale_global_discount: sale order templates compatibility --- sale_global_discount/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py index fb2fcb64971..17d79c5b760 100644 --- a/sale_global_discount/models/sale_order.py +++ b/sale_global_discount/models/sale_order.py @@ -53,7 +53,7 @@ def _check_global_discounts_sanity(self): if not self.global_discount_ids: return True taxes_keys = {} - for line in self.order_line: + for line in self.order_line.filtered(lambda l: not l.display_type): if not line.tax_id: raise exceptions.UserError(_( "With global discounts, taxes in lines are required." From 002dad989dbcbdee756ebf054f917644ac7e6b3d Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 15 Oct 2020 10:56:50 +0000 Subject: [PATCH 11/38] sale_global_discount 12.0.1.0.2 --- sale_global_discount/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py index efbf8bd8372..1a8e66cf2c3 100644 --- a/sale_global_discount/__manifest__.py +++ b/sale_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Sale Global Discount', - 'version': '12.0.1.0.1', + 'version': '12.0.1.0.2', 'category': 'Sales Management', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', From bea2dab8d9b6de77d950a60fb24ba153d4210e21 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 11 Dec 2020 16:59:31 +0100 Subject: [PATCH 12/38] [IMP] sale_global_discount security Only global discount managers can set global discounts [UPD] Update sale_global_discount.pot --- sale_global_discount/i18n/sale_global_discount.pot | 3 +++ sale_global_discount/models/sale_order.py | 8 ++++++++ sale_global_discount/views/sale_order_views.xml | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/sale_global_discount/i18n/sale_global_discount.pot b/sale_global_discount/i18n/sale_global_discount.pot index bb41100f598..e310c31c7e1 100644 --- a/sale_global_discount/i18n/sale_global_discount.pot +++ b/sale_global_discount/i18n/sale_global_discount.pot @@ -35,12 +35,14 @@ msgstr "" #. module: sale_global_discount #: code:addons/sale_global_discount/models/sale_order.py:65 +#: code:addons/sale_global_discount/models/sale_order.py:73 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly msgid "Sale Global Discounts" msgstr "" @@ -56,6 +58,7 @@ msgstr "" #. module: sale_global_discount #: code:addons/sale_global_discount/models/sale_order.py:58 +#: code:addons/sale_global_discount/models/sale_order.py:66 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py index 17d79c5b760..15ed41fb694 100644 --- a/sale_global_discount/models/sale_order.py +++ b/sale_global_discount/models/sale_order.py @@ -16,6 +16,14 @@ class SaleOrder(models.Model): "('account_id', '!=', False), '|', " "('company_id', '=', company_id), ('company_id', '=', False)]", ) + # HACK: Looks like UI doesn't behave well with Many2many fields and + # negative groups when the same field is shown. In this case, we want to + # show the readonly version to any not in the global discount group. + # TODO: Check if it's fixed in future versions + global_discount_ids_readonly = fields.Many2many( + related="global_discount_ids", + readonly=True, + ) amount_global_discount = fields.Monetary( string='Total Global Discounts', compute='_amount_all', diff --git a/sale_global_discount/views/sale_order_views.xml b/sale_global_discount/views/sale_order_views.xml index 0be75186df6..d6acc9bd31f 100644 --- a/sale_global_discount/views/sale_order_views.xml +++ b/sale_global_discount/views/sale_order_views.xml @@ -9,7 +9,8 @@ - + + From dbdee22e39ee9bfbb6a9ac476dc7beb23fc58882 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 15 Dec 2020 22:37:15 +0000 Subject: [PATCH 13/38] sale_global_discount 12.0.1.1.0 Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/ --- sale_global_discount/__manifest__.py | 2 +- sale_global_discount/i18n/es.po | 3 +++ sale_global_discount/i18n/pt.po | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py index 1a8e66cf2c3..5e6df7ae9e5 100644 --- a/sale_global_discount/__manifest__.py +++ b/sale_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Sale Global Discount', - 'version': '12.0.1.0.2', + 'version': '12.0.1.1.0', 'category': 'Sales Management', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/sale_global_discount/i18n/es.po b/sale_global_discount/i18n/es.po index d3dad3095aa..70d5f3c0d00 100644 --- a/sale_global_discount/i18n/es.po +++ b/sale_global_discount/i18n/es.po @@ -40,12 +40,14 @@ msgstr "Base Imponible sin Descuentos" #. module: sale_global_discount #: code:addons/sale_global_discount/models/sale_order.py:65 +#: code:addons/sale_global_discount/models/sale_order.py:73 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly msgid "Sale Global Discounts" msgstr "Descuentos de venta globales" @@ -61,6 +63,7 @@ msgstr "Total Descuentos Globales" #. module: sale_global_discount #: code:addons/sale_global_discount/models/sale_order.py:58 +#: code:addons/sale_global_discount/models/sale_order.py:66 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/sale_global_discount/i18n/pt.po b/sale_global_discount/i18n/pt.po index 1ce845ae4e3..f8a4a9482cb 100644 --- a/sale_global_discount/i18n/pt.po +++ b/sale_global_discount/i18n/pt.po @@ -38,12 +38,14 @@ msgstr "Total sem Impostos antes de Descontos" #. module: sale_global_discount #: code:addons/sale_global_discount/models/sale_order.py:65 +#: code:addons/sale_global_discount/models/sale_order.py:73 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "Foram encontrados impostos incompatíveis para descontos globais." #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly msgid "Sale Global Discounts" msgstr "Descontos Globais de Venda" @@ -59,6 +61,7 @@ msgstr "Total de Descontos Globais" #. module: sale_global_discount #: code:addons/sale_global_discount/models/sale_order.py:58 +#: code:addons/sale_global_discount/models/sale_order.py:66 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "Com descontos globais, é obrigatório ter impostos nas linhas." From e3a8175b07b744ad9453dbcaa0a2877ecd72492c Mon Sep 17 00:00:00 2001 From: david Date: Tue, 22 Dec 2020 11:36:41 +0100 Subject: [PATCH 14/38] [FIX] sale_global_discount: duplicated label warning [UPD] Update sale_global_discount.pot --- sale_global_discount/i18n/sale_global_discount.pot | 11 ++++++++--- sale_global_discount/models/sale_order.py | 1 + sale_global_discount/views/sale_order_views.xml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sale_global_discount/i18n/sale_global_discount.pot b/sale_global_discount/i18n/sale_global_discount.pot index e310c31c7e1..cfc75251d34 100644 --- a/sale_global_discount/i18n/sale_global_discount.pot +++ b/sale_global_discount/i18n/sale_global_discount.pot @@ -34,18 +34,23 @@ msgid "Amount Untaxed Before Discounts" msgstr "" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:65 #: code:addons/sale_global_discount/models/sale_order.py:73 +#: code:addons/sale_global_discount/models/sale_order.py:74 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly +#: model_terms:ir.ui.view,arch_db:sale_global_discount.view_order_form msgid "Sale Global Discounts" msgstr "" +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly +msgid "Sale Global Discounts (readonly)" +msgstr "" + #. module: sale_global_discount #: model:ir.model,name:sale_global_discount.model_sale_order msgid "Sale Order" @@ -57,8 +62,8 @@ msgid "Total Global Discounts" msgstr "" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:58 #: code:addons/sale_global_discount/models/sale_order.py:66 +#: code:addons/sale_global_discount/models/sale_order.py:67 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py index 15ed41fb694..9aeae7acfdd 100644 --- a/sale_global_discount/models/sale_order.py +++ b/sale_global_discount/models/sale_order.py @@ -22,6 +22,7 @@ class SaleOrder(models.Model): # TODO: Check if it's fixed in future versions global_discount_ids_readonly = fields.Many2many( related="global_discount_ids", + string="Sale Global Discounts (readonly)", readonly=True, ) amount_global_discount = fields.Monetary( diff --git a/sale_global_discount/views/sale_order_views.xml b/sale_global_discount/views/sale_order_views.xml index d6acc9bd31f..a3575f9c50c 100644 --- a/sale_global_discount/views/sale_order_views.xml +++ b/sale_global_discount/views/sale_order_views.xml @@ -10,7 +10,7 @@ - + From a44fb454b36aeeb6cc3d3fa704d18f41200acfc2 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 23 Dec 2020 15:07:56 +0000 Subject: [PATCH 15/38] sale_global_discount 12.0.1.2.0 Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/ [UPD] Update sale_global_discount.pot Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/ --- sale_global_discount/__manifest__.py | 2 +- sale_global_discount/i18n/es.po | 13 ++++++++----- sale_global_discount/i18n/pt.po | 13 ++++++++----- sale_global_discount/i18n/sale_global_discount.pot | 2 -- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py index 5e6df7ae9e5..395d7c2aeb7 100644 --- a/sale_global_discount/__manifest__.py +++ b/sale_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Sale Global Discount', - 'version': '12.0.1.1.0', + 'version': '12.0.1.2.0', 'category': 'Sales Management', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/sale_global_discount/i18n/es.po b/sale_global_discount/i18n/es.po index 70d5f3c0d00..64a1a045951 100644 --- a/sale_global_discount/i18n/es.po +++ b/sale_global_discount/i18n/es.po @@ -39,18 +39,22 @@ msgid "Amount Untaxed Before Discounts" msgstr "Base Imponible sin Descuentos" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:65 -#: code:addons/sale_global_discount/models/sale_order.py:73 +#: code:addons/sale_global_discount/models/sale_order.py:74 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly +#: model_terms:ir.ui.view,arch_db:sale_global_discount.view_order_form msgid "Sale Global Discounts" msgstr "Descuentos de venta globales" +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly +msgid "Sale Global Discounts (readonly)" +msgstr "" + #. module: sale_global_discount #: model:ir.model,name:sale_global_discount.model_sale_order msgid "Sale Order" @@ -62,8 +66,7 @@ msgid "Total Global Discounts" msgstr "Total Descuentos Globales" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:58 -#: code:addons/sale_global_discount/models/sale_order.py:66 +#: code:addons/sale_global_discount/models/sale_order.py:67 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/sale_global_discount/i18n/pt.po b/sale_global_discount/i18n/pt.po index f8a4a9482cb..5e3791e4108 100644 --- a/sale_global_discount/i18n/pt.po +++ b/sale_global_discount/i18n/pt.po @@ -37,18 +37,22 @@ msgid "Amount Untaxed Before Discounts" msgstr "Total sem Impostos antes de Descontos" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:65 -#: code:addons/sale_global_discount/models/sale_order.py:73 +#: code:addons/sale_global_discount/models/sale_order.py:74 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "Foram encontrados impostos incompatíveis para descontos globais." #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids -#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly +#: model_terms:ir.ui.view,arch_db:sale_global_discount.view_order_form msgid "Sale Global Discounts" msgstr "Descontos Globais de Venda" +#. module: sale_global_discount +#: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly +msgid "Sale Global Discounts (readonly)" +msgstr "" + #. module: sale_global_discount #: model:ir.model,name:sale_global_discount.model_sale_order msgid "Sale Order" @@ -60,8 +64,7 @@ msgid "Total Global Discounts" msgstr "Total de Descontos Globais" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:58 -#: code:addons/sale_global_discount/models/sale_order.py:66 +#: code:addons/sale_global_discount/models/sale_order.py:67 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "Com descontos globais, é obrigatório ter impostos nas linhas." diff --git a/sale_global_discount/i18n/sale_global_discount.pot b/sale_global_discount/i18n/sale_global_discount.pot index cfc75251d34..11b3cdc55f6 100644 --- a/sale_global_discount/i18n/sale_global_discount.pot +++ b/sale_global_discount/i18n/sale_global_discount.pot @@ -34,7 +34,6 @@ msgid "Amount Untaxed Before Discounts" msgstr "" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:73 #: code:addons/sale_global_discount/models/sale_order.py:74 #, python-format msgid "Incompatible taxes found for global discounts." @@ -62,7 +61,6 @@ msgid "Total Global Discounts" msgstr "" #. module: sale_global_discount -#: code:addons/sale_global_discount/models/sale_order.py:66 #: code:addons/sale_global_discount/models/sale_order.py:67 #, python-format msgid "With global discounts, taxes in lines are required." From 0ec4e61f8af9f40d2cdd70cf193e76c950c362ae Mon Sep 17 00:00:00 2001 From: Pedro Castro Silva Date: Tue, 5 Jan 2021 12:34:54 +0000 Subject: [PATCH 16/38] Translated using Weblate (Portuguese) Currently translated at 100.0% (10 of 10 strings) Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/pt/ --- sale_global_discount/i18n/pt.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sale_global_discount/i18n/pt.po b/sale_global_discount/i18n/pt.po index 5e3791e4108..246c1a2811b 100644 --- a/sale_global_discount/i18n/pt.po +++ b/sale_global_discount/i18n/pt.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2020-09-17 14:00+0000\n" +"PO-Revision-Date: 2021-01-05 14:44+0000\n" "Last-Translator: Pedro Castro Silva \n" "Language-Team: none\n" "Language: pt\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 3.10\n" +"X-Generator: Weblate 4.3.2\n" #. module: sale_global_discount #: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document @@ -51,7 +51,7 @@ msgstr "Descontos Globais de Venda" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly msgid "Sale Global Discounts (readonly)" -msgstr "" +msgstr "Descontos Globais de Venda (apenas leitura)" #. module: sale_global_discount #: model:ir.model,name:sale_global_discount.model_sale_order From 01c820af803d8b595ae540f8bfe266e5d46a28b8 Mon Sep 17 00:00:00 2001 From: Daniel Martinez Vila Date: Wed, 10 Mar 2021 09:40:42 +0000 Subject: [PATCH 17/38] Translated using Weblate (Spanish) Currently translated at 100.0% (10 of 10 strings) Translation: sale-workflow-12.0/sale-workflow-12.0-sale_global_discount Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_global_discount/es/ --- sale_global_discount/i18n/es.po | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sale_global_discount/i18n/es.po b/sale_global_discount/i18n/es.po index 64a1a045951..b54582540b6 100644 --- a/sale_global_discount/i18n/es.po +++ b/sale_global_discount/i18n/es.po @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-04-06 14:38+0000\n" -"PO-Revision-Date: 2020-04-30 10:19+0000\n" -"Last-Translator: David Vidal \n" +"PO-Revision-Date: 2021-03-10 10:45+0000\n" +"Last-Translator: Daniel Martinez Vila \n" "Language-Team: \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.10\n" +"X-Generator: Weblate 4.3.2\n" #. module: sale_global_discount #: model_terms:ir.ui.view,arch_db:sale_global_discount.report_saleorder_document @@ -29,7 +29,6 @@ msgstr "Subtl. antes desc." #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_total_before_global_discounts -#, fuzzy msgid "Amount Total Before Discounts" msgstr "Base Imponible sin Descuentos" @@ -42,7 +41,7 @@ msgstr "Base Imponible sin Descuentos" #: code:addons/sale_global_discount/models/sale_order.py:74 #, python-format msgid "Incompatible taxes found for global discounts." -msgstr "" +msgstr "Se han encontrado impuestos incompatibles para descuentos globales." #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids @@ -53,12 +52,12 @@ msgstr "Descuentos de venta globales" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__global_discount_ids_readonly msgid "Sale Global Discounts (readonly)" -msgstr "" +msgstr "Venta Descuentos Globales (solo lectura)" #. module: sale_global_discount #: model:ir.model,name:sale_global_discount.model_sale_order msgid "Sale Order" -msgstr "" +msgstr "Pedido de venta" #. module: sale_global_discount #: model:ir.model.fields,field_description:sale_global_discount.field_sale_order__amount_global_discount @@ -69,7 +68,7 @@ msgstr "Total Descuentos Globales" #: code:addons/sale_global_discount/models/sale_order.py:67 #, python-format msgid "With global discounts, taxes in lines are required." -msgstr "" +msgstr "Con descuentos globales, se requieren impuestos en líneas." #~ msgid "Quotation" #~ msgstr "Presupuesto" From 014451af977eb81d406661a4949e1f786a8dfcdd Mon Sep 17 00:00:00 2001 From: david Date: Mon, 19 Apr 2021 13:10:43 +0200 Subject: [PATCH 18/38] [IMP] sale_global_discount: black, isort, prettier --- sale_global_discount/__manifest__.py | 26 +-- sale_global_discount/models/sale_order.py | 122 +++++----- .../tests/test_sale_global_discount.py | 215 +++++++++--------- .../views/report_sale_order.xml | 34 ++- .../views/sale_order_views.xml | 22 +- 5 files changed, 225 insertions(+), 194 deletions(-) diff --git a/sale_global_discount/__manifest__.py b/sale_global_discount/__manifest__.py index 395d7c2aeb7..7ee2e887e69 100644 --- a/sale_global_discount/__manifest__.py +++ b/sale_global_discount/__manifest__.py @@ -2,20 +2,14 @@ # Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { - 'name': 'Sale Global Discount', - 'version': '12.0.1.2.0', - 'category': 'Sales Management', - 'author': 'Tecnativa,' - 'Odoo Community Association (OCA)', - 'website': 'https://github.com/OCA/sale-workflow', - 'license': 'AGPL-3', - 'depends': [ - 'account_global_discount', - ], - 'data': [ - 'views/sale_order_views.xml', - 'views/report_sale_order.xml', - ], - 'application': False, - 'installable': True, + "name": "Sale Global Discount", + "version": "12.0.1.2.0", + "category": "Sales Management", + "author": "Tecnativa," "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/sale-workflow", + "license": "AGPL-3", + "depends": ["account_global_discount"], + "data": ["views/sale_order_views.xml", "views/report_sale_order.xml"], + "application": False, + "installable": True, } diff --git a/sale_global_discount/models/sale_order.py b/sale_global_discount/models/sale_order.py index 9aeae7acfdd..22518ed01d9 100644 --- a/sale_global_discount/models/sale_order.py +++ b/sale_global_discount/models/sale_order.py @@ -2,19 +2,20 @@ # Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from functools import partial + from odoo import _, api, exceptions, fields, models from odoo.tools.misc import formatLang class SaleOrder(models.Model): - _inherit = 'sale.order' + _inherit = "sale.order" global_discount_ids = fields.Many2many( - comodel_name='global.discount', - string='Sale Global Discounts', + comodel_name="global.discount", + string="Sale Global Discounts", domain="[('discount_scope', '=', 'sale'), " - "('account_id', '!=', False), '|', " - "('company_id', '=', company_id), ('company_id', '=', False)]", + "('account_id', '!=', False), '|', " + "('company_id', '=', company_id), ('company_id', '=', False)]", ) # HACK: Looks like UI doesn't behave well with Many2many fields and # negative groups when the same field is shown. In this case, we want to @@ -26,21 +27,21 @@ class SaleOrder(models.Model): readonly=True, ) amount_global_discount = fields.Monetary( - string='Total Global Discounts', - compute='_amount_all', - currency_field='currency_id', + string="Total Global Discounts", + compute="_amount_all", + currency_field="currency_id", readonly=True, ) amount_untaxed_before_global_discounts = fields.Monetary( - string='Amount Untaxed Before Discounts', - compute='_amount_all', - currency_field='currency_id', + string="Amount Untaxed Before Discounts", + compute="_amount_all", + currency_field="currency_id", readonly=True, ) amount_total_before_global_discounts = fields.Monetary( - string='Amount Total Before Discounts', - compute='_amount_all', - currency_field='currency_id', + string="Amount Total Before Discounts", + compute="_amount_all", + currency_field="currency_id", readonly=True, ) @@ -64,95 +65,106 @@ def _check_global_discounts_sanity(self): taxes_keys = {} for line in self.order_line.filtered(lambda l: not l.display_type): if not line.tax_id: - raise exceptions.UserError(_( - "With global discounts, taxes in lines are required." - )) + raise exceptions.UserError( + _("With global discounts, taxes in lines are required.") + ) for key in taxes_keys: if key == line.tax_id: break elif key & line.tax_id: - raise exceptions.UserError(_( - "Incompatible taxes found for global discounts." - )) + raise exceptions.UserError( + _("Incompatible taxes found for global discounts.") + ) else: taxes_keys[line.tax_id] = True - @api.depends('order_line.price_total', 'global_discount_ids') + @api.depends("order_line.price_total", "global_discount_ids") def _amount_all(self): res = super()._amount_all() for order in self: order._check_global_discounts_sanity() amount_untaxed_before_global_discounts = order.amount_untaxed amount_total_before_global_discounts = order.amount_total - discounts = order.global_discount_ids.mapped('discount') + discounts = order.global_discount_ids.mapped("discount") amount_discounted_untaxed = amount_discounted_tax = 0 for line in order.order_line: discounted_subtotal = self.get_discounted_global( - line.price_subtotal, discounts.copy()) + line.price_subtotal, discounts.copy() + ) amount_discounted_untaxed += discounted_subtotal discounted_tax = line.tax_id.compute_all( - discounted_subtotal, line.order_id.currency_id, - 1.0, product=line.product_id, - partner=line.order_id.partner_shipping_id) + discounted_subtotal, + line.order_id.currency_id, + 1.0, + product=line.product_id, + partner=line.order_id.partner_shipping_id, + ) amount_discounted_tax += sum( - t.get('amount', 0.0) - for t in discounted_tax.get('taxes', [])) - order.update({ - 'amount_untaxed_before_global_discounts': ( - amount_untaxed_before_global_discounts), - 'amount_total_before_global_discounts': ( - amount_total_before_global_discounts), - 'amount_global_discount': ( - amount_untaxed_before_global_discounts - - amount_discounted_untaxed), - 'amount_untaxed': amount_discounted_untaxed, - 'amount_tax': amount_discounted_tax, - 'amount_total': ( - amount_discounted_untaxed + amount_discounted_tax), - }) + t.get("amount", 0.0) for t in discounted_tax.get("taxes", []) + ) + order.update( + { + "amount_untaxed_before_global_discounts": ( + amount_untaxed_before_global_discounts + ), + "amount_total_before_global_discounts": ( + amount_total_before_global_discounts + ), + "amount_global_discount": ( + amount_untaxed_before_global_discounts + - amount_discounted_untaxed + ), + "amount_untaxed": amount_discounted_untaxed, + "amount_tax": amount_discounted_tax, + "amount_total": (amount_discounted_untaxed + amount_discounted_tax), + } + ) return res - @api.onchange('partner_id') + @api.onchange("partner_id") def onchange_partner_id(self): res = super().onchange_partner_id() self.global_discount_ids = ( - self.partner_id.customer_global_discount_ids or - self.partner_id.commercial_partner_id - .customer_global_discount_ids) + self.partner_id.customer_global_discount_ids + or self.partner_id.commercial_partner_id.customer_global_discount_ids + ) return res def _prepare_invoice(self): invoice_vals = super()._prepare_invoice() if self.global_discount_ids: - invoice_vals.update({ - 'global_discount_ids': [(6, 0, self.global_discount_ids.ids)], - }) + invoice_vals.update( + {"global_discount_ids": [(6, 0, self.global_discount_ids.ids)]} + ) return invoice_vals def action_invoice_create(self, grouped=False, final=False): res = super().action_invoice_create(grouped=grouped, final=final) - invoices = self.env['account.invoice'].browse(res) + invoices = self.env["account.invoice"].browse(res) invoices._set_global_discounts() return res def _amount_by_group(self): """We can apply discounts directly by tax groups.""" super()._amount_by_group() - discounts = self.global_discount_ids.mapped('discount') + discounts = self.global_discount_ids.mapped("discount") if not discounts: return for order in self: round_curr = order.currency_id.round fmt = partial( - formatLang, self.with_context(lang=order.partner_id.lang).env, - currency_obj=order.currency_id + formatLang, + self.with_context(lang=order.partner_id.lang).env, + currency_obj=order.currency_id, ) res = [] for tax in order.amount_by_group: tax_amount = round_curr( - self.get_discounted_global(tax[1], discounts.copy())) + self.get_discounted_global(tax[1], discounts.copy()) + ) tax_base = round_curr( - self.get_discounted_global(tax[2], discounts.copy())) + self.get_discounted_global(tax[2], discounts.copy()) + ) res.append( ( tax[0], @@ -160,7 +172,7 @@ def _amount_by_group(self): tax_base, fmt(tax_amount), fmt(tax_base), - len(order.amount_by_group) + len(order.amount_by_group), ) ) order.amount_by_group = res diff --git a/sale_global_discount/tests/test_sale_global_discount.py b/sale_global_discount/tests/test_sale_global_discount.py index 56da6372c95..6b55f4a45e0 100644 --- a/sale_global_discount/tests/test_sale_global_discount.py +++ b/sale_global_discount/tests/test_sale_global_discount.py @@ -5,95 +5,110 @@ class TestSaleGlobalDiscount(common.SavepointCase): - @classmethod def setUpClass(cls): super().setUpClass() - cls.account_type = cls.env['account.account.type'].create({ - 'name': 'Test', - 'type': 'receivable', - }) - cls.account = cls.env['account.account'].create({ - 'name': 'Test account', - 'code': 'TEST', - 'user_type_id': cls.account_type.id, - 'reconcile': True, - }) - cls.global_discount_obj = cls.env['global.discount'] - cls.global_discount_1 = cls.global_discount_obj.create({ - 'name': 'Test Discount 1', - 'sequence': 1, - 'discount_scope': 'sale', - 'discount': 20, - 'account_id': cls.account.id, - }) - cls.global_discount_2 = cls.global_discount_obj.create({ - 'name': 'Test Discount 2', - 'sequence': 2, - 'discount_scope': 'sale', - 'discount': 30, - 'account_id': cls.account.id, - }) - cls.global_discount_3 = cls.global_discount_obj.create({ - 'name': 'Test Discount 3', - 'sequence': 3, - 'discount_scope': 'sale', - 'discount': 50, - 'account_id': cls.account.id, - }) - cls.partner_1 = cls.env['res.partner'].create({ - 'name': 'Mr. Odoo', - }) - cls.partner_2 = cls.env['res.partner'].create({ - 'name': 'Mrs. Odoo', - }) + cls.account_type = cls.env["account.account.type"].create( + {"name": "Test", "type": "receivable"} + ) + cls.account = cls.env["account.account"].create( + { + "name": "Test account", + "code": "TEST", + "user_type_id": cls.account_type.id, + "reconcile": True, + } + ) + cls.global_discount_obj = cls.env["global.discount"] + cls.global_discount_1 = cls.global_discount_obj.create( + { + "name": "Test Discount 1", + "sequence": 1, + "discount_scope": "sale", + "discount": 20, + "account_id": cls.account.id, + } + ) + cls.global_discount_2 = cls.global_discount_obj.create( + { + "name": "Test Discount 2", + "sequence": 2, + "discount_scope": "sale", + "discount": 30, + "account_id": cls.account.id, + } + ) + cls.global_discount_3 = cls.global_discount_obj.create( + { + "name": "Test Discount 3", + "sequence": 3, + "discount_scope": "sale", + "discount": 50, + "account_id": cls.account.id, + } + ) + cls.partner_1 = cls.env["res.partner"].create({"name": "Mr. Odoo"}) + cls.partner_2 = cls.env["res.partner"].create({"name": "Mrs. Odoo"}) cls.partner_2.customer_global_discount_ids = ( - cls.global_discount_2 + cls.global_discount_3) - cls.product_1 = cls.env['product.product'].create({ - 'name': 'Test Product 1', - 'type': 'service', - }) - cls.product_2 = cls.product_1.copy({ - 'name': 'Test Product 2', - }) - cls.tax_group_5pc = cls.env['account.tax.group'].create({ - 'name': 'Test Tax Group 5%', - 'sequence': 1, - }) - cls.tax_group_15pc = cls.env['account.tax.group'].create({ - 'name': 'Test Tax Group 15%', - 'sequence': 2, - }) - cls.tax_1 = cls.env['account.tax'].create({ - 'name': 'Test TAX 15%', - 'amount_type': 'percent', - 'type_tax_use': 'sale', - 'tax_group_id': cls.tax_group_15pc.id, - 'amount': 15.0, - }) - cls.tax_2 = cls.env['account.tax'].create({ - 'name': 'TAX 5%', - 'amount_type': 'percent', - 'tax_group_id': cls.tax_group_5pc.id, - 'type_tax_use': 'sale', - 'amount': 5.0, - }) - cls.sale = cls.env['sale.order'].create({ - 'partner_id': cls.partner_1.id, - 'order_line': [(0, 0, { - 'product_id': cls.product_1.id, - 'product_uom_qty': 2, - 'price_unit': 75.00, - # Test compound taxes as they tend to provoke corner cases - 'tax_id': [(6, 0, [cls.tax_1.id, cls.tax_2.id])] - }), (0, 0, { - 'name': 'On Site Assistance', - 'product_id': cls.product_2.id, - 'product_uom_qty': 3, - 'price_unit': 33.33, - 'tax_id': [(6, 0, [cls.tax_1.id, cls.tax_2.id])] - })], - }) + cls.global_discount_2 + cls.global_discount_3 + ) + cls.product_1 = cls.env["product.product"].create( + {"name": "Test Product 1", "type": "service"} + ) + cls.product_2 = cls.product_1.copy({"name": "Test Product 2"}) + cls.tax_group_5pc = cls.env["account.tax.group"].create( + {"name": "Test Tax Group 5%", "sequence": 1} + ) + cls.tax_group_15pc = cls.env["account.tax.group"].create( + {"name": "Test Tax Group 15%", "sequence": 2} + ) + cls.tax_1 = cls.env["account.tax"].create( + { + "name": "Test TAX 15%", + "amount_type": "percent", + "type_tax_use": "sale", + "tax_group_id": cls.tax_group_15pc.id, + "amount": 15.0, + } + ) + cls.tax_2 = cls.env["account.tax"].create( + { + "name": "TAX 5%", + "amount_type": "percent", + "tax_group_id": cls.tax_group_5pc.id, + "type_tax_use": "sale", + "amount": 5.0, + } + ) + cls.sale = cls.env["sale.order"].create( + { + "partner_id": cls.partner_1.id, + "order_line": [ + ( + 0, + 0, + { + "product_id": cls.product_1.id, + "product_uom_qty": 2, + "price_unit": 75.00, + # Test compound taxes as they tend to provoke corner cases + "tax_id": [(6, 0, [cls.tax_1.id, cls.tax_2.id])], + }, + ), + ( + 0, + 0, + { + "name": "On Site Assistance", + "product_id": cls.product_2.id, + "product_uom_qty": 3, + "price_unit": 33.33, + "tax_id": [(6, 0, [cls.tax_1.id, cls.tax_2.id])], + }, + ), + ], + } + ) def test_01_global_sale_succesive_discounts(self): """Add global discounts to the sale order""" @@ -106,21 +121,17 @@ def test_01_global_sale_succesive_discounts(self): # according to it line by line with the core method self.assertAlmostEqual(self.sale.amount_global_discount, 50) self.assertAlmostEqual(self.sale.amount_untaxed, 199.99) - self.assertAlmostEqual( - self.sale.amount_untaxed_before_global_discounts, 249.99) + self.assertAlmostEqual(self.sale.amount_untaxed_before_global_discounts, 249.99) self.assertAlmostEqual(self.sale.amount_total, 239.99) - self.assertAlmostEqual( - self.sale.amount_total_before_global_discounts, 299.99) + self.assertAlmostEqual(self.sale.amount_total_before_global_discounts, 299.99) self.assertAlmostEqual(self.sale.amount_tax, 40) # Apply an additional 30% global discount self.sale.global_discount_ids += self.global_discount_2 self.assertAlmostEqual(self.sale.amount_global_discount, 110) self.assertAlmostEqual(self.sale.amount_untaxed, 139.99) - self.assertAlmostEqual( - self.sale.amount_untaxed_before_global_discounts, 249.99) + self.assertAlmostEqual(self.sale.amount_untaxed_before_global_discounts, 249.99) self.assertAlmostEqual(self.sale.amount_total, 167.99) - self.assertAlmostEqual( - self.sale.amount_total_before_global_discounts, 299.99) + self.assertAlmostEqual(self.sale.amount_total_before_global_discounts, 299.99) self.assertAlmostEqual(self.sale.amount_tax, 28) def test_02_global_sale_discounts_from_partner(self): @@ -130,11 +141,9 @@ def test_02_global_sale_discounts_from_partner(self): self.sale.onchange_partner_id() self.assertAlmostEqual(self.sale.amount_global_discount, 162.49) self.assertAlmostEqual(self.sale.amount_untaxed, 87.5) - self.assertAlmostEqual( - self.sale.amount_untaxed_before_global_discounts, 249.99) + self.assertAlmostEqual(self.sale.amount_untaxed_before_global_discounts, 249.99) self.assertAlmostEqual(self.sale.amount_total, 105.01) - self.assertAlmostEqual( - self.sale.amount_total_before_global_discounts, 299.99) + self.assertAlmostEqual(self.sale.amount_total_before_global_discounts, 299.99) self.assertAlmostEqual(self.sale.amount_tax, 17.51) def test_03_global_sale_discounts_to_invoice(self): @@ -145,19 +154,17 @@ def test_03_global_sale_discounts_to_invoice(self): self.sale.action_invoice_create() invoice = self.sale.invoice_ids self.assertEqual(len(invoice.invoice_global_discount_ids), 2) - line_tax_1 = invoice.tax_line_ids.filtered( - lambda x: x.tax_id == self.tax_1) - line_tax_2 = invoice.tax_line_ids.filtered( - lambda x: x.tax_id == self.tax_2) + line_tax_1 = invoice.tax_line_ids.filtered(lambda x: x.tax_id == self.tax_1) + line_tax_2 = invoice.tax_line_ids.filtered(lambda x: x.tax_id == self.tax_2) self.assertAlmostEqual(line_tax_1.base, 87.5) self.assertAlmostEqual(line_tax_2.base, 87.5) self.assertAlmostEqual(line_tax_1.amount, 13.13) self.assertAlmostEqual(line_tax_2.amount, 4.38) discount_amount = sum( - invoice.invoice_global_discount_ids.mapped('discount_amount')) + invoice.invoice_global_discount_ids.mapped("discount_amount") + ) self.assertAlmostEqual(discount_amount, 162.49) - self.assertAlmostEqual( - invoice.amount_untaxed_before_global_discounts, 249.99) + self.assertAlmostEqual(invoice.amount_untaxed_before_global_discounts, 249.99) self.assertAlmostEqual(invoice.amount_untaxed, 87.5) self.assertAlmostEqual(invoice.amount_total, 105.01) diff --git a/sale_global_discount/views/report_sale_order.xml b/sale_global_discount/views/report_sale_order.xml index 1995351e815..e9a6fcd047e 100644 --- a/sale_global_discount/views/report_sale_order.xml +++ b/sale_global_discount/views/report_sale_order.xml @@ -1,26 +1,38 @@ - + - -