From 6fcfeae5ed76ce76439fe138f5a5f9cfb7a15fc0 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 22 Aug 2019 14:24:14 +0200 Subject: [PATCH 01/34] [ADD][11.0] account_global_discount: New Module --- account_global_discount/README.rst | 96 +++++++ account_global_discount/__init__.py | 1 + account_global_discount/__manifest__.py | 23 ++ account_global_discount/i18n/.empty | 0 .../i18n/account_global_discount.pot | 153 +++++++++++ account_global_discount/models/__init__.py | 3 + .../models/account_invoice.py | 237 ++++++++++++++++++ .../models/account_move_line.py | 13 + .../models/global_discount.py | 24 ++ .../readme/CONTRIBUTORS.rst | 6 + .../readme/DESCRIPTION.rst | 1 + account_global_discount/readme/USAGE.rst | 14 ++ .../security/ir.model.access.csv | 3 + account_global_discount/security/security.xml | 16 ++ .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/icon.svg | 79 ++++++ .../static/description/index.html | 124 +++++++++ account_global_discount/tests/__init__.py | 1 + .../tests/test_global_discount.py | 131 ++++++++++ .../views/account_invoice_views.xml | 39 +++ .../views/global_discount_views.xml | 33 +++ 21 files changed, 997 insertions(+) create mode 100644 account_global_discount/README.rst create mode 100644 account_global_discount/__init__.py create mode 100644 account_global_discount/__manifest__.py create mode 100644 account_global_discount/i18n/.empty create mode 100644 account_global_discount/i18n/account_global_discount.pot create mode 100644 account_global_discount/models/__init__.py create mode 100644 account_global_discount/models/account_invoice.py create mode 100644 account_global_discount/models/account_move_line.py create mode 100644 account_global_discount/models/global_discount.py create mode 100644 account_global_discount/readme/CONTRIBUTORS.rst create mode 100644 account_global_discount/readme/DESCRIPTION.rst create mode 100644 account_global_discount/readme/USAGE.rst create mode 100644 account_global_discount/security/ir.model.access.csv create mode 100644 account_global_discount/security/security.xml create mode 100644 account_global_discount/static/description/icon.png create mode 100644 account_global_discount/static/description/icon.svg create mode 100644 account_global_discount/static/description/index.html create mode 100644 account_global_discount/tests/__init__.py create mode 100644 account_global_discount/tests/test_global_discount.py create mode 100644 account_global_discount/views/account_invoice_views.xml create mode 100644 account_global_discount/views/global_discount_views.xml diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst new file mode 100644 index 00000000000..c872aab27cf --- /dev/null +++ b/account_global_discount/README.rst @@ -0,0 +1,96 @@ +======================= +Account 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%2Faccount--invoicing-lightgray.png?logo=github + :target: https://github.com/OCA/account-invoicing/tree/11.0/account_global_discount + :alt: OCA/account-invoicing +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-invoicing-11-0/account-invoicing-11-0-account_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/95/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Apply global discounts to invoices + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +#. Go to *Settings > Parameters > Global Discounts* +#. Add a new discount that can be either by percentage or fixed amount. +#. 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. + +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 `_ + + * Pedro M. Baeza + * David Vidal + * Carlos Dauden + * Rafael Blasco + +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/account-invoicing `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_global_discount/__init__.py b/account_global_discount/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/account_global_discount/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py new file mode 100644 index 00000000000..6d159693144 --- /dev/null +++ b/account_global_discount/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2019 Tecnativa S.L. - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': 'Account Global Discount', + 'version': '11.0.1.0.0', + 'category': 'Accounting', + 'author': 'Tecnativa,' + 'Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/server-backend', + 'license': 'AGPL-3', + 'depends': [ + 'account', + 'base_global_discount', + ], + 'data': [ + 'security/ir.model.access.csv', + 'security/security.xml', + 'views/account_invoice_views.xml', + 'views/global_discount_views.xml', + ], + 'application': False, + 'installable': True, +} diff --git a/account_global_discount/i18n/.empty b/account_global_discount/i18n/.empty new file mode 100644 index 00000000000..e69de29bb2d diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot new file mode 100644 index 00000000000..ab367aef676 --- /dev/null +++ b/account_global_discount/i18n/account_global_discount.pot @@ -0,0 +1,153 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_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: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_id +msgid "Account" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_amount_untaxed_before_global_discounts +msgid "Amount Untaxed Before Discounts" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_analytic_id +msgid "Analytic account" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_base +msgid "Base discounted" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_company_id +msgid "Company" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_create_uid +msgid "Created by" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_create_date +msgid "Created on" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_currency_id +msgid "Currency" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount_display +msgid "Discount" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_name +msgid "Discount Name" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount_amount +msgid "Discounted Amount" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_base_discounted +msgid "Discounted amount" +msgstr "" + +#. module: account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Discounts..." +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_display_name +msgid "Display Name" +msgstr "" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_global_discount_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line_global_discount_id +msgid "Global Discount" +msgstr "" + +#. module: account_global_discount +#: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Global Discounts" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_id +msgid "ID" +msgstr "" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_invoice_id +msgid "Invoice" +msgstr "" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_invoice_global_discount_ids +msgid "Invoice Global Discount" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_ids +msgid "Invoice Global Discounts" +msgstr "" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount___last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_write_date +msgid "Last Updated on" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_amount_global_discount +msgid "Total Global Discounts" +msgstr "" + +#. module: account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Untaxed Amount Before Disc." +msgstr "" + diff --git a/account_global_discount/models/__init__.py b/account_global_discount/models/__init__.py new file mode 100644 index 00000000000..3c75f36efd7 --- /dev/null +++ b/account_global_discount/models/__init__.py @@ -0,0 +1,3 @@ +from . import account_invoice +from . import account_move_line +from . import global_discount diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_invoice.py new file mode 100644 index 00000000000..dc77d8b3d85 --- /dev/null +++ b/account_global_discount/models/account_invoice.py @@ -0,0 +1,237 @@ +# Copyright 2019 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models +from odoo.addons import decimal_precision as dp + + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + global_discount_ids = fields.Many2many( + comodel_name='global.discount', + column1='invoice_id', + column2='global_discount_id', + string='Invoice Global Discounts', + domain="[('discount_scope', 'in', {" + " 'out_invoice': ['sale'], " + " 'out_refund': ['sale'], " + " 'in_refund': ['purchase'], " + " 'in_invoice': ['purchase']" + "}.get(type, [])), ('account_id', '!=', False), '|', " + "('company_id', '=', company_id), ('company_id', '=', False)]", + ) + amount_global_discount = fields.Monetary( + string='Total Global Discounts', + compute='_compute_amount', + currency_field='currency_id', + readonly=True, + ) + amount_untaxed_before_global_discounts = fields.Monetary( + string='Amount Untaxed Before Discounts', + compute='_compute_amount', + currency_field='currency_id', + readonly=True, + ) + invoice_global_discount_ids = fields.One2many( + comodel_name='account.invoice.global.discount', + inverse_name='invoice_id', + ) + + def _set_global_discounts(self): + """Get global discounts in order and apply them in chain. They will be + fetched in their sequence order """ + for inv in self: + if inv.amount_untaxed_before_global_discounts: + base = inv.amount_untaxed_before_global_discounts + else: + base = inv.amount_untaxed + invoice_global_discounts = ( + self.env['account.invoice.global.discount']) + for global_discount in inv.global_discount_ids: + discount = global_discount._get_global_discount_vals(base) + invoice_global_discounts += invoice_global_discounts.new({ + 'name': global_discount.display_name, + 'invoice_id': inv.id, + 'global_discount_id': global_discount.id, + 'discount': global_discount.discount, + 'base': base, + 'base_discounted': discount['base_discounted'], + 'account_id': global_discount.account_id.id, + }) + base = discount['base_discounted'] + inv.invoice_global_discount_ids = invoice_global_discounts + # Recompute line taxes according to global discounts + taxes_grouped = inv.get_taxes_values() + tax_lines = inv.tax_line_ids.filtered('manual') + for tax in taxes_grouped.values(): + tax_lines += tax_lines.new(tax) + inv.tax_line_ids = tax_lines + + @api.onchange('invoice_line_ids') + def _onchange_invoice_line_ids(self): + self._set_global_discounts() + return super()._onchange_invoice_line_ids() + + @api.onchange('partner_id', 'company_id') + def _onchange_partner_id(self): + res = super()._onchange_partner_id() + if (self.type in ['out_invoice', 'out_refund'] and + self.partner_id.customer_global_discount_ids): + self.global_discount_ids = ( + self.partner_id.customer_global_discount_ids) + elif (self.type in ['in_refund', 'in_invoice'] and + self.partner_id.supplier_global_discount_ids): + self.global_discount_ids = ( + self.partner_id.supplier_global_discount_ids) + self._set_global_discounts() + return res + + @api.onchange('global_discount_ids') + def _onchange_global_discount_ids(self): + """Trigger global discount lines to recompute all""" + self._set_global_discounts() + return + + @api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount', + 'tax_line_ids.amount_rounding', 'currency_id', 'company_id', + 'date_invoice', 'type', + 'invoice_global_discount_ids', 'global_discount_ids') + def _compute_amount(self): + super()._compute_amount() + if not self.invoice_global_discount_ids: + return + round_curr = self.currency_id.round + self.amount_global_discount = sum( + round_curr(discount.discount_amount) * - 1 + for discount in self.invoice_global_discount_ids) + self.amount_untaxed_before_global_discounts = self.amount_untaxed + self.amount_untaxed = ( + self.amount_untaxed + self.amount_global_discount) + self.amount_total = self.amount_untaxed + self.amount_tax + amount_total_company_signed = self.amount_total + amount_untaxed_signed = self.amount_untaxed + if (self.currency_id and self.company_id and + self.currency_id != self.company_id.currency_id): + currency_id = self.currency_id.with_context(date=self.date_invoice) + amount_total_company_signed = currency_id.compute( + self.amount_total, self.company_id.currency_id) + amount_untaxed_signed = currency_id.compute( + self.amount_untaxed, self.company_id.currency_id) + sign = self.type in ['in_refund', 'out_refund'] and -1 or 1 + self.amount_total_company_signed = amount_total_company_signed * sign + self.amount_total_signed = self.amount_total * sign + self.amount_untaxed_signed = amount_untaxed_signed * sign + + def get_taxes_values(self): + round_curr = self.currency_id.round + tax_grouped = super().get_taxes_values() + for key in tax_grouped.keys(): + base = tax_grouped[key]['base'] + amount = tax_grouped[key]['amount'] + for discount in self.global_discount_ids: + base = discount._get_global_discount_vals( + base)['base_discounted'] + amount = discount._get_global_discount_vals( + amount)['base_discounted'] + tax_grouped[key]['base'] = round_curr(base) + tax_grouped[key]['amount'] = round_curr(amount) + return tax_grouped + + @api.model + def invoice_line_move_line_get(self): + """Append global discounts move lines""" + res = super().invoice_line_move_line_get() + for discount in self.invoice_global_discount_ids: + if not discount.discount: + continue + res.append({ + 'invoice_global_discount_id': discount.id, + 'global_discount_id': discount.global_discount_id.id, + 'type': 'global_discount', + 'name': discount.name, + 'price_unit': discount.discount_amount * -1, + 'quantity': 1, + 'price': discount.discount_amount * -1, + 'account_id': discount.account_id.id, + 'account_analytic_id': discount.account_analytic_id.id, + 'invoice_id': self.id, + }) + return res + + +class AccountInvoiceGlobalDiscount(models.Model): + _name = "account.invoice.global.discount" + _description = "Invoice Global Discount" + + name = fields.Char( + string='Discount Name', + required=True, + ) + invoice_id = fields.Many2one( + 'account.invoice', + string='Invoice', + ondelete='cascade', + index=True, + readonly=True, + ) + global_discount_id = fields.Many2one( + comodel_name='global.discount', + string='Global Discount', + readonly=True, + ) + discount = fields.Float( + string='Discount', + readonly=True, + ) + discount_display = fields.Char( + compute='_compute_discount_display', + readonly=True, + string="Discount", + ) + base = fields.Float( + string='Base discounted', + digits=dp.get_precision('Product Price'), + readonly=True, + ) + base_discounted = fields.Float( + string='Discounted amount', + digits=dp.get_precision('Product Price'), + readonly=True, + ) + currency_id = fields.Many2one( + related='invoice_id.currency_id', + readonly=True, + ) + discount_amount = fields.Monetary( + string='Discounted Amount', + compute='_compute_discount_amount', + currency_field='currency_id', + readonly=True, + ) + account_id = fields.Many2one( + comodel_name='account.account', + required=True, + string='Account', + domain="[('user_type_id.type', 'not in', ['receivable', 'payable'])]", + ) + account_analytic_id = fields.Many2one( + comodel_name='account.analytic.account', + string='Analytic account', + ) + company_id = fields.Many2one( + related='invoice_id.company_id', + readonly=True, + ) + + def _compute_discount_display(self): + """Given a discount type, we need to render a different symbol""" + for one in self: + precision = self.env['decimal.precision'].precision_get('Discount') + one.discount_display = '{0:.{1}f}%'.format( + one.discount * -1, precision) + + @api.depends('base', 'base_discounted') + def _compute_discount_amount(self): + """Compute the amount discounted""" + for one in self: + one.discount_amount = one.base - one.base_discounted diff --git a/account_global_discount/models/account_move_line.py b/account_global_discount/models/account_move_line.py new file mode 100644 index 00000000000..60fb6944c58 --- /dev/null +++ b/account_global_discount/models/account_move_line.py @@ -0,0 +1,13 @@ +# Copyright 2019 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class AccountMoveLine(models.Model): + _inherit = 'account.move.line' + + global_discount_id = fields.Many2one( + comodel_name='global.discount', + string='Global Discount', + ondelete='restrict', + ) diff --git a/account_global_discount/models/global_discount.py b/account_global_discount/models/global_discount.py new file mode 100644 index 00000000000..a36f9a6f1e7 --- /dev/null +++ b/account_global_discount/models/global_discount.py @@ -0,0 +1,24 @@ +# Copyright 2019 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class GlobalDiscount(models.Model): + _inherit = 'global.discount' + + account_id = fields.Many2one( + comodel_name='account.account', + string='Account', + domain="[('user_type_id.type', 'not in', ['receivable', 'payable'])]", + ) + account_analytic_id = fields.Many2one( + comodel_name='account.analytic.account', + string='Analytic account', + ) + + def _get_global_discount_vals(self, base, account_id=False, **kwargs): + """Return account as well if passed""" + res = super()._get_global_discount_vals(base) + if account_id: + res.update({'account_id': account_id}) + return res diff --git a/account_global_discount/readme/CONTRIBUTORS.rst b/account_global_discount/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..7b1dda35578 --- /dev/null +++ b/account_global_discount/readme/CONTRIBUTORS.rst @@ -0,0 +1,6 @@ +* `Tecnativa `_ + + * Pedro M. Baeza + * David Vidal + * Carlos Dauden + * Rafael Blasco diff --git a/account_global_discount/readme/DESCRIPTION.rst b/account_global_discount/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..1b2a3d1ac43 --- /dev/null +++ b/account_global_discount/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Apply global discounts to invoices diff --git a/account_global_discount/readme/USAGE.rst b/account_global_discount/readme/USAGE.rst new file mode 100644 index 00000000000..7ef8742387e --- /dev/null +++ b/account_global_discount/readme/USAGE.rst @@ -0,0 +1,14 @@ +To use this module, you need to: + +#. Go to *Settings > Parameters > Global Discounts* +#. Add a new discount that can be either by percentage or fixed amount. +#. 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/account_global_discount/security/ir.model.access.csv b/account_global_discount/security/ir.model.access.csv new file mode 100644 index 00000000000..8ace5a399ff --- /dev/null +++ b/account_global_discount/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_invoice_global_discount_user,Invoice Global Discount Users,model_account_invoice_global_discount,base.group_user,1,0,0,0 +access_invoice_global_discount_partner_manager,Invoice Global Discount Partner Manager,model_account_invoice_global_discount,account.group_account_invoice,1,1,1,1 diff --git a/account_global_discount/security/security.xml b/account_global_discount/security/security.xml new file mode 100644 index 00000000000..e2e0ca7b775 --- /dev/null +++ b/account_global_discount/security/security.xml @@ -0,0 +1,16 @@ + + + + + + Global Discount multi-company + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + + + + + diff --git a/account_global_discount/static/description/icon.png b/account_global_discount/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 zcmW++2RxMjAAjx~&dlBk9S+%}OXg)AGE&Cb*&}d0jUxM@u(PQx^-s)697TX`ehR4?GS^qbkof1cslKgkU)h65qZ9Oc=ml_0temigYLJfnz{IDzUf>bGs4N!v3=Z3jMq&A#7%rM5eQ#dc?k~! zVpnB`o+K7|Al`Q_U;eD$B zfJtP*jH`siUq~{KE)`jP2|#TUEFGRryE2`i0**z#*^6~AI|YzIWy$Cu#CSLW3q=GA z6`?GZymC;dCPk~rBS%eCb`5OLr;RUZ;D`}um=H)BfVIq%7VhiMr)_#G0N#zrNH|__ zc+blN2UAB0=617@>_u;MPHN;P;N#YoE=)R#i$k_`UAA>WWCcEVMh~L_ zj--gtp&|K1#58Yz*AHCTMziU1Jzt_jG0I@qAOHsk$2}yTmVkBp_eHuY$A9)>P6o~I z%aQ?!(GqeQ-Y+b0I(m9pwgi(IIZZzsbMv+9w{PFtd_<_(LA~0H(xz{=FhLB@(1&qHA5EJw1>>=%q2f&^X>IQ{!GJ4e9U z&KlB)z(84HmNgm2hg2C0>WM{E(DdPr+EeU_N@57;PC2&DmGFW_9kP&%?X4}+xWi)( z;)z%wI5>D4a*5XwD)P--sPkoY(a~WBw;E~AW`Yue4kFa^LM3X`8x|}ZUeMnqr}>kH zG%WWW>3ml$Yez?i%)2pbKPI7?5o?hydokgQyZsNEr{a|mLdt;X2TX(#B1j35xPnPW z*bMSSOauW>o;*=kO8ojw91VX!qoOQb)zHJ!odWB}d+*K?#sY_jqPdg{Sm2HdYzdEx zOGVPhVRTGPtv0o}RfVP;Nd(|CB)I;*t&QO8h zFfekr30S!-LHmV_Su-W+rEwYXJ^;6&3|L$mMC8*bQptyOo9;>Qb9Q9`ySe3%V$A*9 zeKEe+b0{#KWGp$F+tga)0RtI)nhMa-K@JS}2krK~n8vJ=Ngm?R!9G<~RyuU0d?nz# z-5EK$o(!F?hmX*2Yt6+coY`6jGbb7tF#6nHA zuKk=GGJ;ZwON1iAfG$E#Y7MnZVmrY|j0eVI(DN_MNFJmyZ|;w4tf@=CCDZ#5N_0K= z$;R~bbk?}TpfDjfB&aiQ$VA}s?P}xPERJG{kxk5~R`iRS(SK5d+Xs9swCozZISbnS zk!)I0>t=A<-^z(cmSFz3=jZ23u13X><0b)P)^1T_))Kr`e!-pb#q&J*Q`p+B6la%C zuVl&0duN<;uOsB3%T9Fp8t{ED108<+W(nOZd?gDnfNBC3>M8WE61$So|P zVvqH0SNtDTcsUdzaMDpT=Ty0pDHHNL@Z0w$Y`XO z2M-_r1S+GaH%pz#Uy0*w$Vdl=X=rQXEzO}d6J^R6zjM1u&c9vYLvLp?W7w(?np9x1 zE_0JSAJCPB%i7p*Wvg)pn5T`8k3-uR?*NT|J`eS#_#54p>!p(mLDvmc-3o0mX*mp_ zN*AeS<>#^-{S%W<*mz^!X$w_2dHWpcJ6^j64qFBft-o}o_Vx80o0>}Du;>kLts;$8 zC`7q$QI(dKYG`Wa8#wl@V4jVWBRGQ@1dr-hstpQL)Tl+aqVpGpbSfN>5i&QMXfiZ> zaA?T1VGe?rpQ@;+pkrVdd{klI&jVS@I5_iz!=UMpTsa~mBga?1r}aRBm1WS;TT*s0f0lY=JBl66Upy)-k4J}lh=P^8(SXk~0xW=T9v*B|gzIhN z>qsO7dFd~mgxAy4V?&)=5ieYq?zi?ZEoj)&2o)RLy=@hbCRcfT5jigwtQGE{L*8<@Yd{zg;CsL5mvzfDY}P-wos_6PfprFVaeqNE%h zKZhLtcQld;ZD+>=nqN~>GvROfueSzJD&BE*}XfU|H&(FssBqY=hPCt`d zH?@s2>I(|;fcW&YM6#V#!kUIP8$Nkdh0A(bEVj``-AAyYgwY~jB zT|I7Bf@%;7aL7Wf4dZ%VqF$eiaC38OV6oy3Z#TER2G+fOCd9Iaoy6aLYbPTN{XRPz z;U!V|vBf%H!}52L2gH_+j;`bTcQRXB+y9onc^wLm5wi3-Be}U>k_u>2Eg$=k!(l@I zcCg+flakT2Nej3i0yn+g+}%NYb?ta;R?(g5SnwsQ49U8Wng8d|{B+lyRcEDvR3+`O{zfmrmvFrL6acVP%yG98X zo&+VBg@px@i)%o?dG(`T;n*$S5*rnyiR#=wW}}GsAcfyQpE|>a{=$Hjg=-*_K;UtD z#z-)AXwSRY?OPefw^iI+ z)AXz#PfEjlwTes|_{sB?4(O@fg0AJ^g8gP}ex9Ucf*@_^J(s_5jJV}c)s$`Myn|Kd z$6>}#q^n{4vN@+Os$m7KV+`}c%4)4pv@06af4-x5#wj!KKb%caK{A&Y#Rfs z-po?Dcb1({W=6FKIUirH&(yg=*6aLCekcKwyfK^JN5{wcA3nhO(o}SK#!CINhI`-I z1)6&n7O&ZmyFMuNwvEic#IiOAwNkR=u5it{B9n2sAJV5pNhar=j5`*N!Na;c7g!l$ z3aYBqUkqqTJ=Re-;)s!EOeij=7SQZ3Hq}ZRds%IM*PtM$wV z@;rlc*NRK7i3y5BETSKuumEN`Xu_8GP1Ri=OKQ$@I^ko8>H6)4rjiG5{VBM>B|%`&&s^)jS|-_95&yc=GqjNo{zFkw%%HHhS~e=s zD#sfS+-?*t|J!+ozP6KvtOl!R)@@-z24}`9{QaVLD^9VCSR2b`b!KC#o;Ki<+wXB6 zx3&O0LOWcg4&rv4QG0)4yb}7BFSEg~=IR5#ZRj8kg}dS7_V&^%#Do==#`u zpy6{ox?jWuR(;pg+f@mT>#HGWHAJRRDDDv~@(IDw&R>9643kK#HN`!1vBJHnC+RM&yIh8{gG2q zA%e*U3|N0XSRa~oX-3EAneep)@{h2vvd3Xvy$7og(sayr@95+e6~Xvi1tUqnIxoIH zVWo*OwYElb#uyW{Imam6f2rGbjR!Y3`#gPqkv57dB6K^wRGxc9B(t|aYDGS=m$&S!NmCtrMMaUg(c zc2qC=2Z`EEFMW-me5B)24AqF*bV5Dr-M5ig(l-WPS%CgaPzs6p_gnCIvTJ=Y<6!gT zVt@AfYCzjjsMEGi=rDQHo0yc;HqoRNnNFeWZgcm?f;cp(6CNylj36DoL(?TS7eU#+ z7&mfr#y))+CJOXQKUMZ7QIdS9@#-}7y2K1{8)cCt0~-X0O!O?Qx#E4Og+;A2SjalQ zs7r?qn0H044=sDN$SRG$arw~n=+T_DNdSrarmu)V6@|?1-ZB#hRn`uilTGPJ@fqEy zGt(f0B+^JDP&f=r{#Y_wi#AVDf-y!RIXU^0jXsFpf>=Ji*TeqSY!H~AMbJdCGLhC) zn7Rx+sXw6uYj;WRYrLd^5IZq@6JI1C^YkgnedZEYy<&4(z%Q$5yv#Boo{AH8n$a zhb4Y3PWdr269&?V%uI$xMcUrMzl=;w<_nm*qr=c3Rl@i5wWB;e-`t7D&c-mcQl7x! zZWB`UGcw=Y2=}~wzrfLx=uet<;m3~=8I~ZRuzvMQUQdr+yTV|ATf1Uuomr__nDf=X zZ3WYJtHp_ri(}SQAPjv+Y+0=fH4krOP@S&=zZ-t1jW1o@}z;xk8 z(Nz1co&El^HK^NrhVHa-_;&88vTU>_J33=%{if;BEY*J#1n59=07jrGQ#IP>@u#3A z;!q+E1Rj3ZJ+!4bq9F8PXJ@yMgZL;>&gYA0%_Kbi8?S=XGM~dnQZQ!yBSgcZhY96H zrWnU;k)qy`rX&&xlDyA%(a1Hhi5CWkmg(`Gb%m(HKi-7Z!LKGRP_B8@`7&hdDy5n= z`OIxqxiVfX@OX1p(mQu>0Ai*v_cTMiw4qRt3~NBvr9oBy0)r>w3p~V0SCm=An6@3n)>@z!|o-$HvDK z|3D2ZMJkLE5loMKl6R^ez@Zz%S$&mbeoqH5`Bb){Ei21q&VP)hWS2tjShfFtGE+$z zzCR$P#uktu+#!w)cX!lWN1XU%K-r=s{|j?)Akf@q#3b#{6cZCuJ~gCxuMXRmI$nGtnH+-h z+GEi!*X=AP<|fG`1>MBdTb?28JYc=fGvAi2I<$B(rs$;eoJCyR6_bc~p!XR@O-+sD z=eH`-ye})I5ic1eL~TDmtfJ|8`0VJ*Yr=hNCd)G1p2MMz4C3^Mj?7;!w|Ly%JqmuW zlIEW^Ft%z?*|fpXda>Jr^1noFZEwFgVV%|*XhH@acv8rdGxeEX{M$(vG{Zw+x(ei@ zmfXb22}8-?Fi`vo-YVrTH*C?a8%M=Hv9MqVH7H^J$KsD?>!SFZ;ZsvnHr_gn=7acz z#W?0eCdVhVMWN12VV^$>WlQ?f;P^{(&pYTops|btm6aj>_Uz+hqpGwB)vWp0Cf5y< zft8-je~nn?W11plq}N)4A{l8I7$!ks_x$PXW-2XaRFswX_BnF{R#6YIwMhAgd5F9X zGmwdadS6(a^fjHtXg8=l?Rc0Sm%hk6E9!5cLVloEy4eh(=FwgP`)~I^5~pBEWo+F6 zSf2ncyMurJN91#cJTy_u8Y}@%!bq1RkGC~-bV@SXRd4F{R-*V`bS+6;W5vZ(&+I<9$;-V|eNfLa5n-6% z2(}&uGRF;p92eS*sE*oR$@pexaqr*meB)VhmIg@h{uzkk$9~qh#cHhw#>O%)b@+(| z^IQgqzuj~Sk(J;swEM-3TrJAPCq9k^^^`q{IItKBRXYe}e0Tdr=Huf7da3$l4PdpwWDop%^}n;dD#K4s#DYA8SHZ z&1!riV4W4R7R#C))JH1~axJ)RYnM$$lIR%6fIVA@zV{XVyx}C+a-Dt8Y9M)^KU0+H zR4IUb2CJ{Hg>CuaXtD50jB(_Tcx=Z$^WYu2u5kubqmwp%drJ6 z?Fo40g!Qd<-l=TQxqHEOuPX0;^z7iX?Ke^a%XT<13TA^5`4Xcw6D@Ur&VT&CUe0d} z1GjOVF1^L@>O)l@?bD~$wzgf(nxX1OGD8fEV?TdJcZc2KoUe|oP1#=$$7ee|xbY)A zDZq+cuTpc(fFdj^=!;{k03C69lMQ(|>uhRfRu%+!k&YOi-3|1QKB z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/account_global_discount/static/description/icon.svg b/account_global_discount/static/description/icon.svg new file mode 100644 index 00000000000..a7a26d0932a --- /dev/null +++ b/account_global_discount/static/description/icon.svg @@ -0,0 +1,79 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/account_global_discount/static/description/index.html b/account_global_discount/static/description/index.html new file mode 100644 index 00000000000..27f067420c2 --- /dev/null +++ b/account_global_discount/static/description/index.html @@ -0,0 +1,124 @@ +
+
+
+

Module name

+

This module was written to extend the functionality of ... to support ... and allow you to ...

+
+
+
+ +
+
+
+

Installation

+
+
+

To install this module, you need to: +

    +
  • ...
  • +
+

+
+
+
+ + + +
+
+
+
+ +
+
+
+

Configuration

+
+
+

To configure this module, you need to: +

    +
  • ...
  • +
+

+
+
+
+ + + +
+
+
+
+ +
+
+
+

Usage

+
+
+

To use this module, you need to: +

    +
  • ...
  • +
+

+

For further information, please visit: +

+

+
+
+
+ + + +
+
+
+
+ +
+
+
+

Known issues / Roadmap

+
+
+

+

    +
  • ...
  • +
+

+
+
+
+ + + +
+
+
+
+ +
+
+
+

Credits

+
+
+

Contributors

+ +
+
+

Maintainer

+

+ This module is maintained by the OCA.
+ 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.
+ To contribute to this module, please visit http://odoo-community.org.
+ +

+
+
+
diff --git a/account_global_discount/tests/__init__.py b/account_global_discount/tests/__init__.py new file mode 100644 index 00000000000..e9199cd7e61 --- /dev/null +++ b/account_global_discount/tests/__init__.py @@ -0,0 +1 @@ +from . import test_global_discount diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py new file mode 100644 index 00000000000..e794d5fbdd3 --- /dev/null +++ b/account_global_discount/tests/test_global_discount.py @@ -0,0 +1,131 @@ +# Copyright 2019 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.tests import common + + +class TestGlobalDiscount(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', + 'discount_scope': 'sale', + 'discount': 20, + 'account_id': cls.account.id, + }) + cls.global_discount_2 = cls.global_discount_obj.create({ + 'name': 'Test Discount 2', + 'discount_scope': 'purchase', + 'discount': 30, + 'account_id': cls.account.id, + }) + cls.global_discount_3 = cls.global_discount_obj.create({ + 'name': 'Test Discount 3', + 'discount_scope': 'purchase', + '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.supplier_global_discount_ids = cls.global_discount_2 + cls.tax = cls.env['account.tax'].create({ + 'name': 'TAX 15%', + 'amount_type': 'percent', + 'type_tax_use': 'purchase', + 'amount': 15.0, + }) + cls.invoice = cls.env['account.invoice'].create({ + 'name': "Test Customer Invoice", + 'journal_id': cls.env['account.journal'].search( + [('type', '=', 'sale')])[0].id, + 'partner_id': cls.partner_1.id, + 'account_id': cls.account.id, + 'type': 'in_invoice', + }) + cls.invoice_line = cls.env['account.invoice.line'] + cls.invoice_line1 = cls.invoice_line.create({ + 'invoice_id': cls.invoice.id, + 'name': 'Line 1', + 'price_unit': 200.0, + 'account_id': cls.account.id, + 'invoice_line_tax_ids': [(6, 0, [cls.tax.id])], + 'quantity': 1, + }) + cls.invoice._onchange_invoice_line_ids() + + def test_01_global_invoice_succesive_discounts(self): + """Add global discounts to the invoice""" + self.assertAlmostEqual(self.invoice.amount_total, 230) + self.assertAlmostEqual(self.invoice.tax_line_ids.base, 200.0) + self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 30.0) + # Global discounts are applied to the base and taxes are recomputed: + # 200 - 50% (global disc. 1) = 100 + self.invoice.global_discount_ids = self.global_discount_3 + self.invoice._onchange_global_discount_ids() + self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) + precision = self.env['decimal.precision'].precision_get('Discount') + self.assertEqual( + self.invoice.invoice_global_discount_ids.discount_display, + '-50.{}%'.format('0' * precision)) + self.assertAlmostEqual(self.invoice.tax_line_ids.base, 100.0) + self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 15.0) + self.assertAlmostEqual(self.invoice.amount_untaxed, 100.0) + self.assertAlmostEqual(self.invoice.amount_total, 115.0) + self.assertAlmostEqual(self.invoice.amount_global_discount, -100.0) + # Global discounts are computed succecively: + # 200 - 50% (global disc. 1) = 100 + # 100 - 30% (global disc. 2) = 70 + # The global discounts amount is then 200 - 70 = 130 + self.invoice.global_discount_ids += self.global_discount_2 + self.invoice._onchange_global_discount_ids() + self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) + self.assertAlmostEqual(self.invoice.tax_line_ids.base, 70.0) + self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 10.5) + self.assertAlmostEqual(self.invoice.amount_untaxed, 70.0) + self.assertAlmostEqual(self.invoice.amount_total, 80.5) + self.assertAlmostEqual(self.invoice.amount_global_discount, -130.0) + # Line discounts apply before global ones so: + # 200 - 20% (line discount) = 160 + # 160 - 50% (global disc. 1) = 80 + # 80 - 30% (global disc. 2) = 56 + # The global discounts amount is then 160 - 56 = 104 + self.invoice_line1.discount = 20 + self.invoice._onchange_invoice_line_ids() + self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) + self.assertAlmostEqual(self.invoice.tax_line_ids.base, 56.0) + self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 8.4) + self.assertAlmostEqual(self.invoice.amount_untaxed, 56.0) + self.assertAlmostEqual(self.invoice.amount_total, 64.4) + self.assertAlmostEqual(self.invoice.amount_global_discount, -104.0) + + def test_02_global_invoice_discounts_from_partner(self): + """Change the partner and his global discounts go to the invoice""" + self.assertAlmostEqual(self.invoice.amount_total, 230) + self.assertAlmostEqual(self.invoice.tax_line_ids.base, 200.0) + self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 30.0) + # When we change the parter, his global discounts are fetched depending + # on the type of the invoice. In this case, we fetch the supplier + # global discounts + self.invoice.partner_id = self.partner_2 + self.invoice._onchange_partner_id() + self.assertAlmostEqual(self.invoice.tax_line_ids.base, 140.0) + self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 21.0) + self.assertAlmostEqual(self.invoice.amount_untaxed, 140.0) + self.assertAlmostEqual(self.invoice.amount_total, 161.0) + self.assertAlmostEqual(self.invoice.amount_global_discount, -60.0) diff --git a/account_global_discount/views/account_invoice_views.xml b/account_global_discount/views/account_invoice_views.xml new file mode 100644 index 00000000000..180fb039815 --- /dev/null +++ b/account_global_discount/views/account_invoice_views.xml @@ -0,0 +1,39 @@ + + + + + + account.invoice + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/account_global_discount/views/global_discount_views.xml b/account_global_discount/views/global_discount_views.xml new file mode 100644 index 00000000000..18b91c0abb8 --- /dev/null +++ b/account_global_discount/views/global_discount_views.xml @@ -0,0 +1,33 @@ + + + + + + global.discount + + + + + + + + + + + global.discount + + + + + + + + + + + + From caccf0184388f9a7022a6263d7a3b15c413eb454 Mon Sep 17 00:00:00 2001 From: david Date: Thu, 9 Apr 2020 13:50:07 +0200 Subject: [PATCH 02/34] [FIX+IMP] account_global_discount: link line taxes to discount move line + Discounts in invoice report [IMP] account_global_discount: Discounts in invoice report Give discounts info in the invoice report. [FIX] account_global_discount: link line taxes to discount move line --- account_global_discount/README.rst | 7 + account_global_discount/__manifest__.py | 3 +- .../i18n/account_global_discount.pot | 10 ++ account_global_discount/i18n/es.po | 165 ++++++++++++++++++ .../models/account_invoice.py | 33 ++-- .../models/account_move_line.py | 4 + account_global_discount/readme/ROADMAP.rst | 3 + .../views/report_account_invoice.xml | 26 +++ 8 files changed, 237 insertions(+), 14 deletions(-) create mode 100644 account_global_discount/i18n/es.po create mode 100644 account_global_discount/readme/ROADMAP.rst create mode 100644 account_global_discount/views/report_account_invoice.xml diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst index c872aab27cf..921301286ac 100644 --- a/account_global_discount/README.rst +++ b/account_global_discount/README.rst @@ -50,6 +50,13 @@ You can assign global discounts to partners as well: #. In section purchase (if the partner is a supplier), you can set purchase discounts. +Known issues / Roadmap +====================== + +* Global Discount move lines are created for a common base amount. If that + wasn't the case, we should split discount move lines between bases and + assign proper taxes accordingly. + Bug Tracker =========== diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 6d159693144..3db98ebfdc1 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Account Global Discount', - 'version': '11.0.1.0.0', + 'version': '11.0.1.1.0', 'category': 'Accounting', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', @@ -17,6 +17,7 @@ 'security/security.xml', 'views/account_invoice_views.xml', 'views/global_discount_views.xml', + 'views/report_account_invoice.xml', ], 'application': False, 'installable': True, diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot index ab367aef676..7b87dfee84a 100644 --- a/account_global_discount/i18n/account_global_discount.pot +++ b/account_global_discount/i18n/account_global_discount.pot @@ -13,6 +13,16 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" +#. module: account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "Global Discounts
" +msgstr "" + +#. module: account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "Subtotal w/o disc." +msgstr "" + #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_id #: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_id diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po new file mode 100644 index 00000000000..362f9e2b3e5 --- /dev/null +++ b/account_global_discount/i18n/es.po @@ -0,0 +1,165 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_global_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-04-09 13:24+0000\n" +"PO-Revision-Date: 2020-04-09 13:24+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: account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "Global Discounts
" +msgstr "Descuento Global" + +#. module: account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "Subtotal w/o disc." +msgstr "Subtotal sin desc." + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_id +msgid "Account" +msgstr "Cuenta" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_amount_untaxed_before_global_discounts +msgid "Amount Untaxed Before Discounts" +msgstr "Base Imponible sin Descuentos" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_analytic_id +msgid "Analytic account" +msgstr "Cuenta analítica" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_base +msgid "Base discounted" +msgstr "Base descontada" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_company_id +msgid "Company" +msgstr "Compañía" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_create_date +msgid "Created on" +msgstr "Creado en" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_currency_id +msgid "Currency" +msgstr "Moneda" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount_display +msgid "Discount" +msgstr "Descuento" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_name +msgid "Discount Name" +msgstr "Nombre del descuento" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount_amount +msgid "Discounted Amount" +msgstr "Importe Descontado" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_base_discounted +msgid "Discounted amount" +msgstr "Importe Descontado" + +#. module: account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Discounts..." +msgstr "Descuentos..." + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_global_discount_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line_global_discount_id +msgid "Global Discount" +msgstr "Descuento Global" + +#. module: account_global_discount +#: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Global Discounts" +msgstr "Descuentos Globales" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_id +msgid "ID" +msgstr "ID (identificación)" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_invoice_id +msgid "Invoice" +msgstr "Factura" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_invoice_global_discount_ids +msgid "Invoice Global Discount" +msgstr "Descuento Global en Factura" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_ids +msgid "Invoice Global Discounts" +msgstr "Descuentos Globales en Factura" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_move_line +msgid "Journal Item" +msgstr "Apunte contable" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount___last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_write_uid +msgid "Last Updated by" +msgstr "Última actualización de" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_write_date +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_amount_global_discount +msgid "Total Global Discounts" +msgstr "Total Descuentos Globales" + +#. module: account_global_discount +#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Untaxed Amount Before Disc." +msgstr "Base Imponible Antes Desc." diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_invoice.py index dc77d8b3d85..11784e56aa9 100644 --- a/account_global_discount/models/account_invoice.py +++ b/account_global_discount/models/account_invoice.py @@ -37,29 +37,32 @@ class AccountInvoice(models.Model): inverse_name='invoice_id', ) - def _set_global_discounts(self): - """Get global discounts in order and apply them in chain. They will be - fetched in their sequence order """ - for inv in self: - if inv.amount_untaxed_before_global_discounts: - base = inv.amount_untaxed_before_global_discounts - else: - base = inv.amount_untaxed - invoice_global_discounts = ( - self.env['account.invoice.global.discount']) - for global_discount in inv.global_discount_ids: + def _set_global_discounts_by_tax(self): + """Create invoice global discount lines by tax and discount""" + self.ensure_one() + invoice_global_discounts = self.env['account.invoice.global.discount'] + for tax_line in self.tax_line_ids: + base = tax_line.base + for global_discount in self.global_discount_ids: discount = global_discount._get_global_discount_vals(base) invoice_global_discounts += invoice_global_discounts.new({ 'name': global_discount.display_name, - 'invoice_id': inv.id, + 'invoice_id': self.id, 'global_discount_id': global_discount.id, 'discount': global_discount.discount, 'base': base, 'base_discounted': discount['base_discounted'], 'account_id': global_discount.account_id.id, + 'tax_id': tax_line.tax_id.id, }) base = discount['base_discounted'] - inv.invoice_global_discount_ids = invoice_global_discounts + self.invoice_global_discount_ids = invoice_global_discounts + + def _set_global_discounts(self): + """Get global discounts in order and apply them in chain. They will be + fetched in their sequence order """ + for inv in self: + inv._set_global_discounts_by_tax() # Recompute line taxes according to global discounts taxes_grouped = inv.get_taxes_values() tax_lines = inv.tax_line_ids.filtered('manual') @@ -154,6 +157,7 @@ def invoice_line_move_line_get(self): 'price': discount.discount_amount * -1, 'account_id': discount.account_id.id, 'account_analytic_id': discount.account_analytic_id.id, + 'tax_ids': discount.tax_id.id, 'invoice_id': self.id, }) return res @@ -208,6 +212,9 @@ class AccountInvoiceGlobalDiscount(models.Model): currency_field='currency_id', readonly=True, ) + tax_id = fields.Many2one( + comodel_name='account.tax', + ) account_id = fields.Many2one( comodel_name='account.account', required=True, diff --git a/account_global_discount/models/account_move_line.py b/account_global_discount/models/account_move_line.py index 60fb6944c58..e21ce40ad51 100644 --- a/account_global_discount/models/account_move_line.py +++ b/account_global_discount/models/account_move_line.py @@ -11,3 +11,7 @@ class AccountMoveLine(models.Model): string='Global Discount', ondelete='restrict', ) + invoice_global_discount_id = fields.Many2one( + comodel_name='account.invoice.global.discount', + string='Invoice Global Discount', + ) diff --git a/account_global_discount/readme/ROADMAP.rst b/account_global_discount/readme/ROADMAP.rst new file mode 100644 index 00000000000..e16b0b610dc --- /dev/null +++ b/account_global_discount/readme/ROADMAP.rst @@ -0,0 +1,3 @@ +* Global Discount move lines are created for a common base amount. If that + wasn't the case, we should split discount move lines between bases and + assign proper taxes accordingly. diff --git a/account_global_discount/views/report_account_invoice.xml b/account_global_discount/views/report_account_invoice.xml new file mode 100644 index 00000000000..43150536a24 --- /dev/null +++ b/account_global_discount/views/report_account_invoice.xml @@ -0,0 +1,26 @@ + + + + + + From 0249f2dc65a932ee1924cabae25eb5f656f392ab Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Fri, 3 Jul 2020 11:53:32 +0200 Subject: [PATCH 03/34] [FIX] account_global_discount: More on global discount/taxes link - We need to take into account invoice lines with multiple taxes, so the link should be m2m. - Migration scripts for preserving the best information on existing installations. - Tests for checking new conditions. - Perform sanity check for not ending in an incompatible situation. - Some refactor done on onchanges for avoiding duplicating operations. - Adjust UI for not allowing to edit computed invoice global discounts. --- account_global_discount/README.rst | 7 +- account_global_discount/__manifest__.py | 3 +- .../migrations/11.0.2.0.0/post-migration.py | 55 +++++++++ .../migrations/11.0.2.0.0/pre-migration.py | 11 ++ .../models/account_invoice.py | 83 +++++++++---- .../models/account_move_line.py | 5 - account_global_discount/readme/ROADMAP.rst | 7 +- .../tests/test_global_discount.py | 109 ++++++++++++++++++ .../views/account_invoice_views.xml | 3 +- 9 files changed, 250 insertions(+), 33 deletions(-) create mode 100644 account_global_discount/migrations/11.0.2.0.0/post-migration.py create mode 100644 account_global_discount/migrations/11.0.2.0.0/pre-migration.py diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst index 921301286ac..9e2d63b57aa 100644 --- a/account_global_discount/README.rst +++ b/account_global_discount/README.rst @@ -53,9 +53,10 @@ You can assign global discounts to partners as well: Known issues / Roadmap ====================== -* Global Discount move lines are created for a common base amount. If that - wasn't the case, we should split discount move lines between bases and - assign proper taxes accordingly. +* Not all the taxes combination can be compatible with global discounts, as + the generated journal items won't be correct for taxes declarations. An error + is raised in that cases. +* Currently, taxes in invoice lines are mandatory with global discounts. Bug Tracker =========== diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 3db98ebfdc1..2a8400d9c23 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -1,8 +1,9 @@ # Copyright 2019 Tecnativa S.L. - David Vidal +# Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Account Global Discount', - 'version': '11.0.1.1.0', + 'version': '11.0.2.0.0', 'category': 'Accounting', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/account_global_discount/migrations/11.0.2.0.0/post-migration.py b/account_global_discount/migrations/11.0.2.0.0/post-migration.py new file mode 100644 index 00000000000..facd0c0500a --- /dev/null +++ b/account_global_discount/migrations/11.0.2.0.0/post-migration.py @@ -0,0 +1,55 @@ +# Copyright 2020 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade # pylint: disable=W7936 +from psycopg2 import sql + + +@openupgrade.migrate() +def migrate(env, version): + # Link the new field that points to the invoice global discount instead + # of the global discount definition + openupgrade.logged_query( + env.cr, sql.SQL(""" + UPDATE account_move_line aml + SET invoice_global_discount_id = aigd.id + FROM account_invoice_global_discount aigd + WHERE aigd.invoice_id = aml.invoice_id + AND aigd.global_discount_id = aml.{} + """).format( + sql.Identifier(openupgrade.get_legacy_name("global_discount_id")) + ) + ) + # Link to existing global discount records, all the invoice taxes as best + # effort + openupgrade.logged_query( + env.cr, """ + INSERT INTO account_invoice_global_discount_account_tax_rel + (account_invoice_global_discount_id, account_tax_id) + SELECT aigd.id, ailt.tax_id + FROM account_invoice_global_discount aigd + JOIN account_invoice_line ail ON aigd.invoice_id = ail.invoice_id + JOIN account_invoice_line_tax ailt ON ailt.invoice_line_id = ail.id + GROUP BY aigd.id, ailt.tax_id""" + ) + # Delete in prevention of manual manipulations existing tax lines linked + # to global discount journal items + openupgrade.logged_query( + env.cr, """ + DELETE FROM account_move_line_account_tax_rel rel + USING account_move_line aml + WHERE rel.account_move_line_id = aml.id + AND aml.invoice_global_discount_id IS NOT NULL""" + ) + # Link all invoice taxes in global discount existing journal items as best + # effort + openupgrade.logged_query( + env.cr, """ + INSERT INTO account_move_line_account_tax_rel + (account_move_line_id, account_tax_id) + SELECT aml.id, rel.account_tax_id + FROM account_move_line aml + JOIN account_invoice_global_discount_account_tax_rel rel + ON rel.account_invoice_global_discount_id = + aml.invoice_global_discount_id""" + ) diff --git a/account_global_discount/migrations/11.0.2.0.0/pre-migration.py b/account_global_discount/migrations/11.0.2.0.0/pre-migration.py new file mode 100644 index 00000000000..d3b95329df0 --- /dev/null +++ b/account_global_discount/migrations/11.0.2.0.0/pre-migration.py @@ -0,0 +1,11 @@ +# Copyright 2020 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade # pylint: disable=W7936 + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.rename_columns( + env.cr, {"account_move_line": [("global_discount_id", None)]} + ) diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_invoice.py index 11784e56aa9..f026a35945a 100644 --- a/account_global_discount/models/account_invoice.py +++ b/account_global_discount/models/account_invoice.py @@ -1,6 +1,7 @@ # Copyright 2019 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, fields, models +from odoo import _, api, exceptions, fields, models from odoo.addons import decimal_precision as dp @@ -35,14 +36,45 @@ class AccountInvoice(models.Model): invoice_global_discount_ids = fields.One2many( comodel_name='account.invoice.global.discount', inverse_name='invoice_id', + readonly=True, ) def _set_global_discounts_by_tax(self): - """Create invoice global discount lines by tax and discount""" + """Create invoice global discount lines by taxes combinations and + discounts. + """ self.ensure_one() + if not self.global_discount_ids: + return invoice_global_discounts = self.env['account.invoice.global.discount'] + taxes_keys = {} + # Perform a sanity check for discarding cases that will lead to + # incorrect data in discounts + for inv_line in self.invoice_line_ids: + if not inv_line.invoice_line_tax_ids: + raise exceptions.UserError(_( + "With global discounts, taxes in lines are required." + )) + for key in taxes_keys: + if key == inv_line.invoice_line_tax_ids: + break + elif key & inv_line.invoice_line_tax_ids: + raise exceptions.UserError(_( + "Incompatible taxes found for global discounts." + )) + else: + taxes_keys[inv_line.invoice_line_tax_ids] = True for tax_line in self.tax_line_ids: - base = tax_line.base + key = [] + to_create = True + for key in taxes_keys: + if tax_line.tax_id in key: + to_create = taxes_keys[key] + taxes_keys[key] = False # mark for not duplicating + break # we leave in key variable the proper taxes value + if not to_create: + continue + base = tax_line.base_before_global_discounts or tax_line.base for global_discount in self.global_discount_ids: discount = global_discount._get_global_discount_vals(base) invoice_global_discounts += invoice_global_discounts.new({ @@ -53,7 +85,7 @@ def _set_global_discounts_by_tax(self): 'base': base, 'base_discounted': discount['base_discounted'], 'account_id': global_discount.account_id.id, - 'tax_id': tax_line.tax_id.id, + 'tax_ids': [(4, x.id) for x in key], }) base = discount['base_discounted'] self.invoice_global_discount_ids = invoice_global_discounts @@ -63,17 +95,12 @@ def _set_global_discounts(self): fetched in their sequence order """ for inv in self: inv._set_global_discounts_by_tax() - # Recompute line taxes according to global discounts - taxes_grouped = inv.get_taxes_values() - tax_lines = inv.tax_line_ids.filtered('manual') - for tax in taxes_grouped.values(): - tax_lines += tax_lines.new(tax) - inv.tax_line_ids = tax_lines @api.onchange('invoice_line_ids') def _onchange_invoice_line_ids(self): + res = super()._onchange_invoice_line_ids() self._set_global_discounts() - return super()._onchange_invoice_line_ids() + return res @api.onchange('partner_id', 'company_id') def _onchange_partner_id(self): @@ -86,14 +113,12 @@ def _onchange_partner_id(self): self.partner_id.supplier_global_discount_ids): self.global_discount_ids = ( self.partner_id.supplier_global_discount_ids) - self._set_global_discounts() return res @api.onchange('global_discount_ids') def _onchange_global_discount_ids(self): """Trigger global discount lines to recompute all""" - self._set_global_discounts() - return + return self._onchange_invoice_line_ids() @api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount', 'tax_line_ids.amount_rounding', 'currency_id', 'company_id', @@ -126,10 +151,12 @@ def _compute_amount(self): self.amount_untaxed_signed = amount_untaxed_signed * sign def get_taxes_values(self): + """Override this computation for adding global discount to taxes.""" round_curr = self.currency_id.round tax_grouped = super().get_taxes_values() for key in tax_grouped.keys(): base = tax_grouped[key]['base'] + tax_grouped[key]['base_before_global_discounts'] = base amount = tax_grouped[key]['amount'] for discount in self.global_discount_ids: base = discount._get_global_discount_vals( @@ -147,22 +174,38 @@ def invoice_line_move_line_get(self): for discount in self.invoice_global_discount_ids: if not discount.discount: continue - res.append({ + # Traverse upstream result for taking existing dictionary vals + inv_lines = self.invoice_line_ids.filtered( + lambda x: x.invoice_line_tax_ids == discount.tax_ids) + discount_dict = {} + for move_line_dict in res: + if move_line_dict.get("invl_id", 0) in inv_lines.ids: + discount_dict.update(move_line_dict) + # Change needed values for the global discount + discount_dict.update({ 'invoice_global_discount_id': discount.id, - 'global_discount_id': discount.global_discount_id.id, 'type': 'global_discount', - 'name': discount.name, + 'name': "%s - %s" % ( + discount.name, ", ".join(discount.tax_ids.mapped("name"))), 'price_unit': discount.discount_amount * -1, 'quantity': 1, 'price': discount.discount_amount * -1, 'account_id': discount.account_id.id, 'account_analytic_id': discount.account_analytic_id.id, - 'tax_ids': discount.tax_id.id, - 'invoice_id': self.id, }) + res.append(discount_dict) return res +class AccountInvoiceTax(models.Model): + _inherit = "account.invoice.tax" + + base_before_global_discounts = fields.Monetary( + string='Amount Untaxed Before Discounts', + readonly=True, + ) + + class AccountInvoiceGlobalDiscount(models.Model): _name = "account.invoice.global.discount" _description = "Invoice Global Discount" @@ -212,7 +255,7 @@ class AccountInvoiceGlobalDiscount(models.Model): currency_field='currency_id', readonly=True, ) - tax_id = fields.Many2one( + tax_ids = fields.Many2many( comodel_name='account.tax', ) account_id = fields.Many2one( diff --git a/account_global_discount/models/account_move_line.py b/account_global_discount/models/account_move_line.py index e21ce40ad51..5116ebd8cdb 100644 --- a/account_global_discount/models/account_move_line.py +++ b/account_global_discount/models/account_move_line.py @@ -6,11 +6,6 @@ class AccountMoveLine(models.Model): _inherit = 'account.move.line' - global_discount_id = fields.Many2one( - comodel_name='global.discount', - string='Global Discount', - ondelete='restrict', - ) invoice_global_discount_id = fields.Many2one( comodel_name='account.invoice.global.discount', string='Invoice Global Discount', diff --git a/account_global_discount/readme/ROADMAP.rst b/account_global_discount/readme/ROADMAP.rst index e16b0b610dc..68997140579 100644 --- a/account_global_discount/readme/ROADMAP.rst +++ b/account_global_discount/readme/ROADMAP.rst @@ -1,3 +1,4 @@ -* Global Discount move lines are created for a common base amount. If that - wasn't the case, we should split discount move lines between bases and - assign proper taxes accordingly. +* Not all the taxes combination can be compatible with global discounts, as + the generated journal items won't be correct for taxes declarations. An error + is raised in that cases. +* Currently, taxes in invoice lines are mandatory with global discounts. diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index e794d5fbdd3..a51b21f0405 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -1,5 +1,7 @@ # Copyright 2019 Tecnativa - David Vidal +# Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import exceptions from odoo.tests import common @@ -123,9 +125,116 @@ def test_02_global_invoice_discounts_from_partner(self): # on the type of the invoice. In this case, we fetch the supplier # global discounts self.invoice.partner_id = self.partner_2 + # trigger onchanges mimicking UI self.invoice._onchange_partner_id() + self.invoice._onchange_global_discount_ids() self.assertAlmostEqual(self.invoice.tax_line_ids.base, 140.0) self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 21.0) self.assertAlmostEqual(self.invoice.amount_untaxed, 140.0) self.assertAlmostEqual(self.invoice.amount_total, 161.0) self.assertAlmostEqual(self.invoice.amount_global_discount, -60.0) + + def test_03_multiple_taxes_multi_line(self): + tax2 = self.env['account.tax'].create({ + 'name': 'TAX 20%', + 'amount_type': 'percent', + 'type_tax_use': 'purchase', + 'amount': 20.0, + }) + self.invoice_line.create({ + 'invoice_id': self.invoice.id, + 'name': 'Line 2', + 'price_unit': 100.0, + 'account_id': self.account.id, + 'invoice_line_tax_ids': [(6, 0, [tax2.id])], + 'quantity': 1, + }) + self.invoice.global_discount_ids = self.global_discount_1 + self.invoice._onchange_global_discount_ids() + # Global discounts are applied to the base and taxes are recomputed: + # 300 - 20% (global disc. 1) = 240 + self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) + discount_tax_15 = self.invoice.invoice_global_discount_ids.filtered( + lambda x: x.tax_ids == self.tax) + discount_tax_20 = self.invoice.invoice_global_discount_ids.filtered( + lambda x: x.tax_ids == tax2) + self.assertAlmostEqual(discount_tax_15.discount_amount, 40) + self.assertAlmostEqual(discount_tax_20.discount_amount, 20) + tax_line_15 = self.invoice.tax_line_ids.filtered( + lambda x: x.tax_id == self.tax) + tax_line_20 = self.invoice.tax_line_ids.filtered( + lambda x: x.tax_id == tax2) + self.assertAlmostEqual(tax_line_15.base, 160) + self.assertAlmostEqual(tax_line_15.amount, 24) + self.assertAlmostEqual(tax_line_20.base, 80.0) + self.assertAlmostEqual(tax_line_20.amount, 16) + self.assertAlmostEqual(self.invoice.amount_untaxed, 240.0) + self.assertAlmostEqual(self.invoice.amount_total, 280) + self.assertAlmostEqual(self.invoice.amount_global_discount, -60.0) + # Validate invoice for seeing result + self.invoice.action_invoice_open() + self.assertEqual(len(self.invoice.move_id.line_ids), 7) + + def test_04_multiple_taxes_same_line(self): + tax2 = self.env['account.tax'].create({ + 'name': 'Retention 20%', + 'amount_type': 'percent', + 'type_tax_use': 'purchase', + 'amount': -20.0, # negative for testing more use cases + }) + self.invoice_line1.invoice_line_tax_ids = [(4, tax2.id)] + self.invoice.global_discount_ids = self.global_discount_1 + self.invoice._onchange_global_discount_ids() + # Global discounts are applied to the base and taxes are recomputed: + # 300 - 20% (global disc. 1) = 240 + self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) + self.assertAlmostEqual( + self.invoice.invoice_global_discount_ids.discount_amount, 40) + self.assertEqual( + self.invoice.invoice_global_discount_ids.tax_ids, self.tax + tax2) + tax_line_15 = self.invoice.tax_line_ids.filtered( + lambda x: x.tax_id == self.tax) + tax_line_20 = self.invoice.tax_line_ids.filtered( + lambda x: x.tax_id == tax2) + self.assertAlmostEqual(tax_line_15.base, 160) + self.assertAlmostEqual(tax_line_15.amount, 24) + self.assertAlmostEqual(tax_line_20.base, 160.0) + self.assertAlmostEqual(tax_line_20.amount, -32) + self.assertAlmostEqual(self.invoice.amount_untaxed, 160.0) + self.assertAlmostEqual(self.invoice.amount_total, 152) + self.assertAlmostEqual(self.invoice.amount_global_discount, -40.0) + # Validate invoice for seeing result + self.invoice.action_invoice_open() + move = self.invoice.move_id + self.assertEqual(len(move.line_ids), 5) + line = move.line_ids.filtered(lambda x: "Test Discount 1" in x.name) + self.assertEqual(line.tax_ids, self.tax + tax2) + + def test_05_incompatible_taxes(self): + # Line 1 with tax and tax2 + # Line 2 with only tax2 + tax2 = self.env['account.tax'].create({ + 'name': 'Retention 20%', + 'amount_type': 'percent', + 'type_tax_use': 'purchase', + 'amount': -20.0, # negative for testing more use cases + }) + self.invoice_line1.invoice_line_tax_ids = [ + (4, tax2.id), (4, self.tax.id)] + self.invoice_line.create({ + 'invoice_id': self.invoice.id, + 'name': 'Line 2', + 'price_unit': 100.0, + 'account_id': self.account.id, + 'invoice_line_tax_ids': [(6, 0, [tax2.id])], + 'quantity': 1, + }) + self.invoice.global_discount_ids = self.global_discount_1 + with self.assertRaises(exceptions.UserError): + self.invoice._onchange_global_discount_ids() + + def test_06_no_taxes(self): + self.invoice_line1.invoice_line_tax_ids = False + self.invoice.global_discount_ids = self.global_discount_1 + with self.assertRaises(exceptions.UserError): + self.invoice._onchange_global_discount_ids() diff --git a/account_global_discount/views/account_invoice_views.xml b/account_global_discount/views/account_invoice_views.xml index 180fb039815..a65bc43b109 100644 --- a/account_global_discount/views/account_invoice_views.xml +++ b/account_global_discount/views/account_invoice_views.xml @@ -16,7 +16,7 @@ - + @@ -27,6 +27,7 @@ + From 7914a75b6a2621db57295258f40f6f4fb162da07 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Wed, 8 Jul 2020 11:07:47 +0200 Subject: [PATCH 04/34] [MIG] acount_global_discount: Migration to 12.0 - Standard procedure - Make test more resilient --- account_global_discount/README.rst | 10 +- account_global_discount/__manifest__.py | 2 +- .../i18n/account_global_discount.pot | 91 ++- account_global_discount/i18n/es.po | 100 +-- .../models/account_invoice.py | 2 +- .../static/description/index.html | 578 ++++++++++++++---- .../tests/test_global_discount.py | 8 +- 7 files changed, 591 insertions(+), 200 deletions(-) diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst index 9e2d63b57aa..b815bac018d 100644 --- a/account_global_discount/README.rst +++ b/account_global_discount/README.rst @@ -14,13 +14,13 @@ Account 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%2Faccount--invoicing-lightgray.png?logo=github - :target: https://github.com/OCA/account-invoicing/tree/11.0/account_global_discount + :target: https://github.com/OCA/account-invoicing/tree/12.0/account_global_discount :alt: OCA/account-invoicing .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-invoicing-11-0/account-invoicing-11-0-account_global_discount + :target: https://translation.odoo-community.org/projects/account-invoicing-12-0/account-invoicing-12-0-account_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/95/11.0 + :target: https://runbot.odoo-community.org/runbot/95/12.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -64,7 +64,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. @@ -99,6 +99,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/account-invoicing `_ project on GitHub. +This module is part of the `OCA/account-invoicing `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 2a8400d9c23..a9f76e9e0d5 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Account Global Discount', - 'version': '11.0.2.0.0', + 'version': '12.0.1.0.0', 'category': 'Accounting', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot index 7b87dfee84a..fd7f4d3e2ec 100644 --- a/account_global_discount/i18n/account_global_discount.pot +++ b/account_global_discount/i18n/account_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,150 +14,177 @@ msgstr "" "Plural-Forms: \n" #. module: account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.report_invoice_document +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document msgid "Global Discounts
" msgstr "" #. module: account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.report_invoice_document +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document msgid "Subtotal w/o disc." msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_id -#: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_id msgid "Account" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_tax__base_before_global_discounts msgid "Amount Untaxed Before Discounts" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_analytic_id -#: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_analytic_id msgid "Analytic account" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_base +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base msgid "Base discounted" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_company_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id msgid "Company" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_create_uid +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_create_date +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_date msgid "Created on" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_currency_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__currency_id msgid "Currency" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount_display +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_display msgid "Discount" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount +msgid "Discount (number)" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__name msgid "Discount Name" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount_amount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_amount msgid "Discounted Amount" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_base_discounted +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted msgid "Discounted amount" msgstr "" #. module: account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view msgid "Discounts..." msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_display_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__display_name msgid "Display Name" msgstr "" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_global_discount_id -#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line_global_discount_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__global_discount_id msgid "Global Discount" msgstr "" #. module: account_global_discount #: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view msgid "Global Discounts" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__id msgid "ID" msgstr "" +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_invoice.py:62 +#, python-format +msgid "Incompatible taxes found for global discounts." +msgstr "" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_invoice_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id msgid "Invoice" msgstr "" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id msgid "Invoice Global Discount" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__global_discount_ids msgid "Invoice Global Discounts" msgstr "" +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_tax +msgid "Invoice Tax" +msgstr "" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move_line msgid "Journal Item" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount___last_update +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount____last_update msgid "Last Modified on" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_write_uid +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_uid msgid "Last Updated by" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_write_date +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_date msgid "Last Updated on" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_amount_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids +msgid "Tax" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_global_discount msgid "Total Global Discounts" msgstr "" #. module: account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view msgid "Untaxed Amount Before Disc." msgstr "" +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_invoice.py:55 +#, python-format +msgid "With global discounts, taxes in lines are required." +msgstr "" + diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po index 362f9e2b3e5..cfcdd14adf8 100644 --- a/account_global_discount/i18n/es.po +++ b/account_global_discount/i18n/es.po @@ -6,160 +6,190 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-09 13:24+0000\n" -"PO-Revision-Date: 2020-04-09 13:24+0000\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2020-07-08 11:07+0200\n" "Last-Translator: <>\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" +"Content-Transfer-Encoding: 8bit\n" "Plural-Forms: \n" +"X-Generator: Poedit 2.3\n" #. module: account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.report_invoice_document +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document msgid "Global Discounts
" msgstr "Descuento Global" #. module: account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.report_invoice_document +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document msgid "Subtotal w/o disc." msgstr "Subtotal sin desc." #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_id -#: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_id msgid "Account" msgstr "Cuenta" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_tax__base_before_global_discounts msgid "Amount Untaxed Before Discounts" msgstr "Base Imponible sin Descuentos" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_account_analytic_id -#: model:ir.model.fields,field_description:account_global_discount.field_global_discount_account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_analytic_id msgid "Analytic account" msgstr "Cuenta analítica" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_base +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base msgid "Base discounted" msgstr "Base descontada" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_company_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id msgid "Company" msgstr "Compañía" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_create_uid +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" msgstr "Creado por" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_create_date +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_date msgid "Created on" msgstr "Creado en" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_currency_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__currency_id msgid "Currency" msgstr "Moneda" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount_display +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_display msgid "Discount" msgstr "Descuento" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount +msgid "Discount (number)" +msgstr "Descuento (número)" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__name msgid "Discount Name" msgstr "Nombre del descuento" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_discount_amount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_amount msgid "Discounted Amount" msgstr "Importe Descontado" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_base_discounted +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted msgid "Discounted amount" msgstr "Importe Descontado" #. module: account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view msgid "Discounts..." msgstr "Descuentos..." #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_display_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__display_name msgid "Display Name" msgstr "Nombre mostrado" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_global_discount_id -#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line_global_discount_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__global_discount_id msgid "Global Discount" msgstr "Descuento Global" #. module: account_global_discount #: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view msgid "Global Discounts" msgstr "Descuentos Globales" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__id msgid "ID" msgstr "ID (identificación)" +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_invoice.py:62 +#, python-format +msgid "Incompatible taxes found for global discounts." +msgstr "" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_invoice_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id msgid "Invoice" msgstr "Factura" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id msgid "Invoice Global Discount" msgstr "Descuento Global en Factura" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__global_discount_ids msgid "Invoice Global Discounts" msgstr "Descuentos Globales en Factura" +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_tax +#, fuzzy +#| msgid "Invoice" +msgid "Invoice Tax" +msgstr "Factura" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move_line msgid "Journal Item" msgstr "Apunte contable" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount___last_update +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount____last_update msgid "Last Modified on" msgstr "Última modificación en" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_write_uid +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_uid msgid "Last Updated by" msgstr "Última actualización de" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount_write_date +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_date msgid "Last Updated on" msgstr "Última actualización en" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_amount_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids +msgid "Tax" +msgstr "" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_global_discount msgid "Total Global Discounts" msgstr "Total Descuentos Globales" #. module: account_global_discount -#: model:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view msgid "Untaxed Amount Before Disc." msgstr "Base Imponible Antes Desc." + +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_invoice.py:55 +#, python-format +msgid "With global discounts, taxes in lines are required." +msgstr "" diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_invoice.py index f026a35945a..df24247c1a8 100644 --- a/account_global_discount/models/account_invoice.py +++ b/account_global_discount/models/account_invoice.py @@ -227,7 +227,7 @@ class AccountInvoiceGlobalDiscount(models.Model): readonly=True, ) discount = fields.Float( - string='Discount', + string='Discount (number)', readonly=True, ) discount_display = fields.Char( diff --git a/account_global_discount/static/description/index.html b/account_global_discount/static/description/index.html index 27f067420c2..e7b6d9d7b93 100644 --- a/account_global_discount/static/description/index.html +++ b/account_global_discount/static/description/index.html @@ -1,124 +1,454 @@ -
-
-
-

Module name

-

This module was written to extend the functionality of ... to support ... and allow you to ...

-
-
-
- -
-
-
-

Installation

-
-
-

To install this module, you need to: -

    -
  • ...
  • -
-

-
-
-
- - - -
-
-
-
- -
-
-
-

Configuration

-
-
-

To configure this module, you need to: -

    -
  • ...
  • -
-

-
-
-
- - - -
-
-
-
- -
-
-
-

Usage

-
-
-

To use this module, you need to: -

    -
  • ...
  • -
-

-

For further information, please visit: -

-

-
-
-
- - - -
-
-
-
- -
-
-
-

Known issues / Roadmap

-
-
-

-

    -
  • ...
  • -
-

-
-
-
- - - -
-
-
-
- -
-
-
-

Credits

-
-
-

Contributors

- -
-
-

Maintainer

-

- This module is maintained by the OCA.
- 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.
- To contribute to this module, please visit http://odoo-community.org.
- -

-
-
-
+ + + + + + +Account Global Discount + + + +
+

Account Global Discount

+ + +

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runbot

+

Apply global discounts to invoices

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  1. Go to Settings > Parameters > Global Discounts
  2. +
  3. Add a new discount that can be either by percentage or fixed amount.
  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. +
+
+
+

Known issues / Roadmap

+
    +
  • Not all the taxes combination can be compatible with global discounts, as +the generated journal items won’t be correct for taxes declarations. An error +is raised in that cases.
  • +
  • Currently, taxes in invoice lines are mandatory with global discounts.
  • +
+
+
+

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
      +
    • Pedro M. Baeza
    • +
    • David Vidal
    • +
    • Carlos Dauden
    • +
    • Rafael Blasco
    • +
    +
  • +
+
+
+

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/account-invoicing project on GitHub.

+

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

+
+
+
+ + diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index a51b21f0405..f55ffab849d 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -52,10 +52,14 @@ def setUpClass(cls): 'type_tax_use': 'purchase', 'amount': 15.0, }) + cls.journal = cls.env["account.journal"].create({ + "name": "Test purchase journal", + "code": "TPUR", + "type": "purchase", + }) cls.invoice = cls.env['account.invoice'].create({ 'name': "Test Customer Invoice", - 'journal_id': cls.env['account.journal'].search( - [('type', '=', 'sale')])[0].id, + 'journal_id': cls.journal.id, 'partner_id': cls.partner_1.id, 'account_id': cls.account.id, 'type': 'in_invoice', From f73f05afb4542d31e70d670e97f96c0ed172e918 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Mon, 20 Jul 2020 09:56:49 +0200 Subject: [PATCH 05/34] [IMP] account_global_discount: Deprecated use of currency compute Replaced by more explicit `_convert` method. --- account_global_discount/__manifest__.py | 2 +- account_global_discount/i18n/es.po | 1 - account_global_discount/models/account_invoice.py | 12 +++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index a9f76e9e0d5..5cb2c4c82e3 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Account Global Discount', - 'version': '12.0.1.0.0', + 'version': '12.0.1.0.1', 'category': 'Accounting', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po index cfcdd14adf8..f098d912589 100644 --- a/account_global_discount/i18n/es.po +++ b/account_global_discount/i18n/es.po @@ -149,7 +149,6 @@ msgstr "Descuentos Globales en Factura" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice_tax #, fuzzy -#| msgid "Invoice" msgid "Invoice Tax" msgstr "Factura" diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_invoice.py index df24247c1a8..2eb41baf17a 100644 --- a/account_global_discount/models/account_invoice.py +++ b/account_global_discount/models/account_invoice.py @@ -140,11 +140,13 @@ def _compute_amount(self): amount_untaxed_signed = self.amount_untaxed if (self.currency_id and self.company_id and self.currency_id != self.company_id.currency_id): - currency_id = self.currency_id.with_context(date=self.date_invoice) - amount_total_company_signed = currency_id.compute( - self.amount_total, self.company_id.currency_id) - amount_untaxed_signed = currency_id.compute( - self.amount_untaxed, self.company_id.currency_id) + date = self.date_invoice or fields.Date.today() + amount_total_company_signed = self.currency_id._convert( + self.amount_total, self.company_id.currency_id, + self.company_id, date) + amount_untaxed_signed = self.currency_id._convert( + self.amount_untaxed, self.company_id.currency_id, + self.company_id, date) sign = self.type in ['in_refund', 'out_refund'] and -1 or 1 self.amount_total_company_signed = amount_total_company_signed * sign self.amount_total_signed = self.amount_total * sign From 4ae9a07f3320a23fcd3ea1a157a7fe679d5e3480 Mon Sep 17 00:00:00 2001 From: Pedro Castro Silva Date: Thu, 17 Sep 2020 10:14:37 +0000 Subject: [PATCH 06/34] Added translation using Weblate (Portuguese) --- account_global_discount/i18n/pt.po | 192 +++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 account_global_discount/i18n/pt.po diff --git a/account_global_discount/i18n/pt.po b/account_global_discount/i18n/pt.po new file mode 100644 index 00000000000..25d875e26e0 --- /dev/null +++ b/account_global_discount/i18n/pt.po @@ -0,0 +1,192 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_global_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2020-09-17 13: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: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "Global Discounts
" +msgstr "Descontos Globais
" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "Subtotal w/o disc." +msgstr "Subtotal s/ Desc." + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_id +msgid "Account" +msgstr "Conta" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_tax__base_before_global_discounts +msgid "Amount Untaxed Before Discounts" +msgstr "Montante sem Impostos antes de Descontos" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_analytic_id +msgid "Analytic account" +msgstr "Conta analítica" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base +msgid "Base discounted" +msgstr "Base com desconto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id +msgid "Company" +msgstr "Empresa" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid +msgid "Created by" +msgstr "Criado por" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_date +msgid "Created on" +msgstr "Criado em" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__currency_id +msgid "Currency" +msgstr "Moeda" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_display +msgid "Discount" +msgstr "Desconto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount +msgid "Discount (number)" +msgstr "Desconto (número)" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__name +msgid "Discount Name" +msgstr "Nome do Desconto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_amount +msgid "Discounted Amount" +msgstr "Valor Descontado" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted +msgid "Discounted amount" +msgstr "Montante descontado" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Discounts..." +msgstr "Descontos..." + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__display_name +msgid "Display Name" +msgstr "Nome a Apresentar" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__global_discount_id +msgid "Global Discount" +msgstr "Desconto Global" + +#. module: account_global_discount +#: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Global Discounts" +msgstr "Descontos Globais" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__id +msgid "ID" +msgstr "ID" + +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_invoice.py:62 +#, python-format +msgid "Incompatible taxes found for global discounts." +msgstr "Foram encontrados impostos incompatíveis nos descontos globais." + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id +msgid "Invoice" +msgstr "Fatura" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id +msgid "Invoice Global Discount" +msgstr "Desconto Global de Fatura" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__global_discount_ids +msgid "Invoice Global Discounts" +msgstr "Descontos Globais de Fatura" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_tax +msgid "Invoice Tax" +msgstr "Imposto de Fatura" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_move_line +msgid "Journal Item" +msgstr "Item do Diário" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount____last_update +msgid "Last Modified on" +msgstr "Última Modificação Em" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_uid +msgid "Last Updated by" +msgstr "Atualizado pela última vez por" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_date +msgid "Last Updated on" +msgstr "Atualizado pela última vez em" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids +msgid "Tax" +msgstr "Imposto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_global_discount +msgid "Total Global Discounts" +msgstr "Descontos Globais Totais" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +msgid "Untaxed Amount Before Disc." +msgstr "Montante sem Impostos antes de Desc." + +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_invoice.py:55 +#, python-format +msgid "With global discounts, taxes in lines are required." +msgstr "Com descontos globais, são requeridos impostos nas linhas." From 95f6eddd18f42e8b141f2680932ff77c6e4e6241 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Fri, 25 Sep 2020 19:49:13 +0200 Subject: [PATCH 07/34] [FIX] account_global_discount: Fix singleton error in _compute_amount For reducing diff, we rename the method and call it record per record. Fixes #788 --- account_global_discount/__manifest__.py | 2 +- .../models/account_invoice.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 5cb2c4c82e3..1c9d94492aa 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Account Global Discount', - 'version': '12.0.1.0.1', + 'version': '12.0.1.0.2', 'category': 'Accounting', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_invoice.py index 2eb41baf17a..34db939ed2d 100644 --- a/account_global_discount/models/account_invoice.py +++ b/account_global_discount/models/account_invoice.py @@ -120,12 +120,7 @@ def _onchange_global_discount_ids(self): """Trigger global discount lines to recompute all""" return self._onchange_invoice_line_ids() - @api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount', - 'tax_line_ids.amount_rounding', 'currency_id', 'company_id', - 'date_invoice', 'type', - 'invoice_global_discount_ids', 'global_discount_ids') - def _compute_amount(self): - super()._compute_amount() + def _compute_amount_one(self): if not self.invoice_global_discount_ids: return round_curr = self.currency_id.round @@ -152,6 +147,15 @@ def _compute_amount(self): self.amount_total_signed = self.amount_total * sign self.amount_untaxed_signed = amount_untaxed_signed * sign + @api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount', + 'tax_line_ids.amount_rounding', 'currency_id', 'company_id', + 'date_invoice', 'type', + 'invoice_global_discount_ids', 'global_discount_ids') + def _compute_amount(self): + super()._compute_amount() + for record in self: + record._compute_amount_one() + def get_taxes_values(self): """Override this computation for adding global discount to taxes.""" round_curr = self.currency_id.round From d2a615ace7d7cecf897c18ad672f3b8def7469c8 Mon Sep 17 00:00:00 2001 From: pedrocasi Date: Tue, 13 Oct 2020 19:14:28 +0100 Subject: [PATCH 08/34] [FIX][12.0] account_global_discount: sale order templates compatibility --- account_global_discount/__manifest__.py | 2 +- account_global_discount/i18n/account_global_discount.pot | 4 ++-- account_global_discount/i18n/es.po | 4 ++-- account_global_discount/i18n/pt.po | 6 +++--- account_global_discount/models/account_invoice.py | 3 ++- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 1c9d94492aa..14ae4c816c2 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Account Global Discount', - 'version': '12.0.1.0.2', + 'version': '12.0.1.0.3', 'category': 'Accounting', 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot index fd7f4d3e2ec..025db9ff780 100644 --- a/account_global_discount/i18n/account_global_discount.pot +++ b/account_global_discount/i18n/account_global_discount.pot @@ -119,7 +119,7 @@ msgid "ID" msgstr "" #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:62 +#: code:addons/account_global_discount/models/account_invoice.py:63 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" @@ -183,7 +183,7 @@ msgid "Untaxed Amount Before Disc." msgstr "" #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:55 +#: code:addons/account_global_discount/models/account_invoice.py:56 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po index f098d912589..e90b2312e4d 100644 --- a/account_global_discount/i18n/es.po +++ b/account_global_discount/i18n/es.po @@ -123,7 +123,7 @@ msgid "ID" msgstr "ID (identificación)" #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:62 +#: code:addons/account_global_discount/models/account_invoice.py:63 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" @@ -188,7 +188,7 @@ msgid "Untaxed Amount Before Disc." msgstr "Base Imponible Antes Desc." #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:55 +#: code:addons/account_global_discount/models/account_invoice.py:56 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" diff --git a/account_global_discount/i18n/pt.po b/account_global_discount/i18n/pt.po index 25d875e26e0..7fbf6ea99df 100644 --- a/account_global_discount/i18n/pt.po +++ b/account_global_discount/i18n/pt.po @@ -1,6 +1,6 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * account_global_discount +# * account_global_discount # msgid "" msgstr "" @@ -122,7 +122,7 @@ msgid "ID" msgstr "ID" #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:62 +#: code:addons/account_global_discount/models/account_invoice.py:63 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "Foram encontrados impostos incompatíveis nos descontos globais." @@ -186,7 +186,7 @@ msgid "Untaxed Amount Before Disc." msgstr "Montante sem Impostos antes de Desc." #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:55 +#: code:addons/account_global_discount/models/account_invoice.py:56 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "Com descontos globais, são requeridos impostos nas linhas." diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_invoice.py index 34db939ed2d..27617ad089f 100644 --- a/account_global_discount/models/account_invoice.py +++ b/account_global_discount/models/account_invoice.py @@ -50,7 +50,8 @@ def _set_global_discounts_by_tax(self): taxes_keys = {} # Perform a sanity check for discarding cases that will lead to # incorrect data in discounts - for inv_line in self.invoice_line_ids: + for inv_line in self.invoice_line_ids.filtered( + lambda l: not l.display_type): if not inv_line.invoice_line_tax_ids: raise exceptions.UserError(_( "With global discounts, taxes in lines are required." From ad66b26997a626935dc2bda27773734b6a3587ac Mon Sep 17 00:00:00 2001 From: Ernesto Tejeda Date: Tue, 3 Nov 2020 09:43:54 -0500 Subject: [PATCH 09/34] [IMP] account_global_discount: black, isort, prettier --- account_global_discount/__manifest__.py | 34 +-- .../migrations/11.0.2.0.0/post-migration.py | 24 +- .../models/account_invoice.py | 256 +++++++++--------- .../models/account_move_line.py | 6 +- .../models/global_discount.py | 11 +- account_global_discount/security/security.xml | 21 +- .../tests/test_global_discount.py | 242 +++++++++-------- .../views/account_invoice_views.xml | 74 +++-- .../views/global_discount_views.xml | 32 ++- .../views/report_account_invoice.xml | 29 +- 10 files changed, 395 insertions(+), 334 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 14ae4c816c2..cdc14a865f3 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -2,24 +2,20 @@ # Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { - 'name': 'Account Global Discount', - 'version': '12.0.1.0.3', - 'category': 'Accounting', - 'author': 'Tecnativa,' - 'Odoo Community Association (OCA)', - 'website': 'https://github.com/OCA/server-backend', - 'license': 'AGPL-3', - 'depends': [ - 'account', - 'base_global_discount', + "name": "Account Global Discount", + "version": "12.0.1.0.3", + "category": "Accounting", + "author": "Tecnativa," "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-backend", + "license": "AGPL-3", + "depends": ["account", "base_global_discount",], + "data": [ + "security/ir.model.access.csv", + "security/security.xml", + "views/account_invoice_views.xml", + "views/global_discount_views.xml", + "views/report_account_invoice.xml", ], - 'data': [ - 'security/ir.model.access.csv', - 'security/security.xml', - 'views/account_invoice_views.xml', - 'views/global_discount_views.xml', - 'views/report_account_invoice.xml', - ], - 'application': False, - 'installable': True, + "application": False, + "installable": True, } diff --git a/account_global_discount/migrations/11.0.2.0.0/post-migration.py b/account_global_discount/migrations/11.0.2.0.0/post-migration.py index facd0c0500a..60fe1c343c6 100644 --- a/account_global_discount/migrations/11.0.2.0.0/post-migration.py +++ b/account_global_discount/migrations/11.0.2.0.0/post-migration.py @@ -10,46 +10,50 @@ def migrate(env, version): # Link the new field that points to the invoice global discount instead # of the global discount definition openupgrade.logged_query( - env.cr, sql.SQL(""" + env.cr, + sql.SQL( + """ UPDATE account_move_line aml SET invoice_global_discount_id = aigd.id FROM account_invoice_global_discount aigd WHERE aigd.invoice_id = aml.invoice_id AND aigd.global_discount_id = aml.{} - """).format( - sql.Identifier(openupgrade.get_legacy_name("global_discount_id")) - ) + """ + ).format(sql.Identifier(openupgrade.get_legacy_name("global_discount_id"))), ) # Link to existing global discount records, all the invoice taxes as best # effort openupgrade.logged_query( - env.cr, """ + env.cr, + """ INSERT INTO account_invoice_global_discount_account_tax_rel (account_invoice_global_discount_id, account_tax_id) SELECT aigd.id, ailt.tax_id FROM account_invoice_global_discount aigd JOIN account_invoice_line ail ON aigd.invoice_id = ail.invoice_id JOIN account_invoice_line_tax ailt ON ailt.invoice_line_id = ail.id - GROUP BY aigd.id, ailt.tax_id""" + GROUP BY aigd.id, ailt.tax_id""", ) # Delete in prevention of manual manipulations existing tax lines linked # to global discount journal items openupgrade.logged_query( - env.cr, """ + env.cr, + """ DELETE FROM account_move_line_account_tax_rel rel USING account_move_line aml WHERE rel.account_move_line_id = aml.id - AND aml.invoice_global_discount_id IS NOT NULL""" + AND aml.invoice_global_discount_id IS NOT NULL""", ) # Link all invoice taxes in global discount existing journal items as best # effort openupgrade.logged_query( - env.cr, """ + env.cr, + """ INSERT INTO account_move_line_account_tax_rel (account_move_line_id, account_tax_id) SELECT aml.id, rel.account_tax_id FROM account_move_line aml JOIN account_invoice_global_discount_account_tax_rel rel ON rel.account_invoice_global_discount_id = - aml.invoice_global_discount_id""" + aml.invoice_global_discount_id""", ) diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_invoice.py index 27617ad089f..6c4a88a1b94 100644 --- a/account_global_discount/models/account_invoice.py +++ b/account_global_discount/models/account_invoice.py @@ -2,40 +2,41 @@ # 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 + from odoo.addons import decimal_precision as dp class AccountInvoice(models.Model): - _inherit = 'account.invoice' + _inherit = "account.invoice" global_discount_ids = fields.Many2many( - comodel_name='global.discount', - column1='invoice_id', - column2='global_discount_id', - string='Invoice Global Discounts', + comodel_name="global.discount", + column1="invoice_id", + column2="global_discount_id", + string="Invoice Global Discounts", domain="[('discount_scope', 'in', {" - " 'out_invoice': ['sale'], " - " 'out_refund': ['sale'], " - " 'in_refund': ['purchase'], " - " 'in_invoice': ['purchase']" - "}.get(type, [])), ('account_id', '!=', False), '|', " - "('company_id', '=', company_id), ('company_id', '=', False)]", + " 'out_invoice': ['sale'], " + " 'out_refund': ['sale'], " + " 'in_refund': ['purchase'], " + " 'in_invoice': ['purchase']" + "}.get(type, [])), ('account_id', '!=', False), '|', " + "('company_id', '=', company_id), ('company_id', '=', False)]", ) amount_global_discount = fields.Monetary( - string='Total Global Discounts', - compute='_compute_amount', - currency_field='currency_id', + string="Total Global Discounts", + compute="_compute_amount", + currency_field="currency_id", readonly=True, ) amount_untaxed_before_global_discounts = fields.Monetary( - string='Amount Untaxed Before Discounts', - compute='_compute_amount', - currency_field='currency_id', + string="Amount Untaxed Before Discounts", + compute="_compute_amount", + currency_field="currency_id", readonly=True, ) invoice_global_discount_ids = fields.One2many( - comodel_name='account.invoice.global.discount', - inverse_name='invoice_id', + comodel_name="account.invoice.global.discount", + inverse_name="invoice_id", readonly=True, ) @@ -46,23 +47,22 @@ def _set_global_discounts_by_tax(self): self.ensure_one() if not self.global_discount_ids: return - invoice_global_discounts = self.env['account.invoice.global.discount'] + invoice_global_discounts = self.env["account.invoice.global.discount"] taxes_keys = {} # Perform a sanity check for discarding cases that will lead to # incorrect data in discounts - for inv_line in self.invoice_line_ids.filtered( - lambda l: not l.display_type): + for inv_line in self.invoice_line_ids.filtered(lambda l: not l.display_type): if not inv_line.invoice_line_tax_ids: - 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 == inv_line.invoice_line_tax_ids: break elif key & inv_line.invoice_line_tax_ids: - raise exceptions.UserError(_( - "Incompatible taxes found for global discounts." - )) + raise exceptions.UserError( + _("Incompatible taxes found for global discounts.") + ) else: taxes_keys[inv_line.invoice_line_tax_ids] = True for tax_line in self.tax_line_ids: @@ -78,17 +78,19 @@ def _set_global_discounts_by_tax(self): base = tax_line.base_before_global_discounts or tax_line.base for global_discount in self.global_discount_ids: discount = global_discount._get_global_discount_vals(base) - invoice_global_discounts += invoice_global_discounts.new({ - 'name': global_discount.display_name, - 'invoice_id': self.id, - 'global_discount_id': global_discount.id, - 'discount': global_discount.discount, - 'base': base, - 'base_discounted': discount['base_discounted'], - 'account_id': global_discount.account_id.id, - 'tax_ids': [(4, x.id) for x in key], - }) - base = discount['base_discounted'] + invoice_global_discounts += invoice_global_discounts.new( + { + "name": global_discount.display_name, + "invoice_id": self.id, + "global_discount_id": global_discount.id, + "discount": global_discount.discount, + "base": base, + "base_discounted": discount["base_discounted"], + "account_id": global_discount.account_id.id, + "tax_ids": [(4, x.id) for x in key], + } + ) + base = discount["base_discounted"] self.invoice_global_discount_ids = invoice_global_discounts def _set_global_discounts(self): @@ -97,26 +99,28 @@ def _set_global_discounts(self): for inv in self: inv._set_global_discounts_by_tax() - @api.onchange('invoice_line_ids') + @api.onchange("invoice_line_ids") def _onchange_invoice_line_ids(self): res = super()._onchange_invoice_line_ids() self._set_global_discounts() return res - @api.onchange('partner_id', 'company_id') + @api.onchange("partner_id", "company_id") def _onchange_partner_id(self): res = super()._onchange_partner_id() - if (self.type in ['out_invoice', 'out_refund'] and - self.partner_id.customer_global_discount_ids): - self.global_discount_ids = ( - self.partner_id.customer_global_discount_ids) - elif (self.type in ['in_refund', 'in_invoice'] and - self.partner_id.supplier_global_discount_ids): - self.global_discount_ids = ( - self.partner_id.supplier_global_discount_ids) + if ( + self.type in ["out_invoice", "out_refund"] + and self.partner_id.customer_global_discount_ids + ): + self.global_discount_ids = self.partner_id.customer_global_discount_ids + elif ( + self.type in ["in_refund", "in_invoice"] + and self.partner_id.supplier_global_discount_ids + ): + self.global_discount_ids = self.partner_id.supplier_global_discount_ids return res - @api.onchange('global_discount_ids') + @api.onchange("global_discount_ids") def _onchange_global_discount_ids(self): """Trigger global discount lines to recompute all""" return self._onchange_invoice_line_ids() @@ -126,32 +130,42 @@ def _compute_amount_one(self): return round_curr = self.currency_id.round self.amount_global_discount = sum( - round_curr(discount.discount_amount) * - 1 - for discount in self.invoice_global_discount_ids) + round_curr(discount.discount_amount) * -1 + for discount in self.invoice_global_discount_ids + ) self.amount_untaxed_before_global_discounts = self.amount_untaxed - self.amount_untaxed = ( - self.amount_untaxed + self.amount_global_discount) + self.amount_untaxed = self.amount_untaxed + self.amount_global_discount self.amount_total = self.amount_untaxed + self.amount_tax amount_total_company_signed = self.amount_total amount_untaxed_signed = self.amount_untaxed - if (self.currency_id and self.company_id and - self.currency_id != self.company_id.currency_id): + if ( + self.currency_id + and self.company_id + and self.currency_id != self.company_id.currency_id + ): date = self.date_invoice or fields.Date.today() amount_total_company_signed = self.currency_id._convert( - self.amount_total, self.company_id.currency_id, - self.company_id, date) + self.amount_total, self.company_id.currency_id, self.company_id, date + ) amount_untaxed_signed = self.currency_id._convert( - self.amount_untaxed, self.company_id.currency_id, - self.company_id, date) - sign = self.type in ['in_refund', 'out_refund'] and -1 or 1 + self.amount_untaxed, self.company_id.currency_id, self.company_id, date + ) + sign = self.type in ["in_refund", "out_refund"] and -1 or 1 self.amount_total_company_signed = amount_total_company_signed * sign self.amount_total_signed = self.amount_total * sign self.amount_untaxed_signed = amount_untaxed_signed * sign - @api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount', - 'tax_line_ids.amount_rounding', 'currency_id', 'company_id', - 'date_invoice', 'type', - 'invoice_global_discount_ids', 'global_discount_ids') + @api.depends( + "invoice_line_ids.price_subtotal", + "tax_line_ids.amount", + "tax_line_ids.amount_rounding", + "currency_id", + "company_id", + "date_invoice", + "type", + "invoice_global_discount_ids", + "global_discount_ids", + ) def _compute_amount(self): super()._compute_amount() for record in self: @@ -162,16 +176,14 @@ def get_taxes_values(self): round_curr = self.currency_id.round tax_grouped = super().get_taxes_values() for key in tax_grouped.keys(): - base = tax_grouped[key]['base'] - tax_grouped[key]['base_before_global_discounts'] = base - amount = tax_grouped[key]['amount'] + base = tax_grouped[key]["base"] + tax_grouped[key]["base_before_global_discounts"] = base + amount = tax_grouped[key]["amount"] for discount in self.global_discount_ids: - base = discount._get_global_discount_vals( - base)['base_discounted'] - amount = discount._get_global_discount_vals( - amount)['base_discounted'] - tax_grouped[key]['base'] = round_curr(base) - tax_grouped[key]['amount'] = round_curr(amount) + base = discount._get_global_discount_vals(base)["base_discounted"] + amount = discount._get_global_discount_vals(amount)["base_discounted"] + tax_grouped[key]["base"] = round_curr(base) + tax_grouped[key]["amount"] = round_curr(amount) return tax_grouped @api.model @@ -183,23 +195,26 @@ def invoice_line_move_line_get(self): continue # Traverse upstream result for taking existing dictionary vals inv_lines = self.invoice_line_ids.filtered( - lambda x: x.invoice_line_tax_ids == discount.tax_ids) + lambda x: x.invoice_line_tax_ids == discount.tax_ids + ) discount_dict = {} for move_line_dict in res: if move_line_dict.get("invl_id", 0) in inv_lines.ids: discount_dict.update(move_line_dict) # Change needed values for the global discount - discount_dict.update({ - 'invoice_global_discount_id': discount.id, - 'type': 'global_discount', - 'name': "%s - %s" % ( - discount.name, ", ".join(discount.tax_ids.mapped("name"))), - 'price_unit': discount.discount_amount * -1, - 'quantity': 1, - 'price': discount.discount_amount * -1, - 'account_id': discount.account_id.id, - 'account_analytic_id': discount.account_analytic_id.id, - }) + discount_dict.update( + { + "invoice_global_discount_id": discount.id, + "type": "global_discount", + "name": "%s - %s" + % (discount.name, ", ".join(discount.tax_ids.mapped("name"))), + "price_unit": discount.discount_amount * -1, + "quantity": 1, + "price": discount.discount_amount * -1, + "account_id": discount.account_id.id, + "account_analytic_id": discount.account_analytic_id.id, + } + ) res.append(discount_dict) return res @@ -208,8 +223,7 @@ class AccountInvoiceTax(models.Model): _inherit = "account.invoice.tax" base_before_global_discounts = fields.Monetary( - string='Amount Untaxed Before Discounts', - readonly=True, + string="Amount Untaxed Before Discounts", readonly=True, ) @@ -217,77 +231,57 @@ class AccountInvoiceGlobalDiscount(models.Model): _name = "account.invoice.global.discount" _description = "Invoice Global Discount" - name = fields.Char( - string='Discount Name', - required=True, - ) + name = fields.Char(string="Discount Name", required=True,) invoice_id = fields.Many2one( - 'account.invoice', - string='Invoice', - ondelete='cascade', + "account.invoice", + string="Invoice", + ondelete="cascade", index=True, readonly=True, ) global_discount_id = fields.Many2one( - comodel_name='global.discount', - string='Global Discount', - readonly=True, - ) - discount = fields.Float( - string='Discount (number)', - readonly=True, + comodel_name="global.discount", string="Global Discount", readonly=True, ) + discount = fields.Float(string="Discount (number)", readonly=True,) discount_display = fields.Char( - compute='_compute_discount_display', - readonly=True, - string="Discount", + compute="_compute_discount_display", readonly=True, string="Discount", ) base = fields.Float( - string='Base discounted', - digits=dp.get_precision('Product Price'), + string="Base discounted", + digits=dp.get_precision("Product Price"), readonly=True, ) base_discounted = fields.Float( - string='Discounted amount', - digits=dp.get_precision('Product Price'), - readonly=True, - ) - currency_id = fields.Many2one( - related='invoice_id.currency_id', + string="Discounted amount", + digits=dp.get_precision("Product Price"), readonly=True, ) + currency_id = fields.Many2one(related="invoice_id.currency_id", readonly=True,) discount_amount = fields.Monetary( - string='Discounted Amount', - compute='_compute_discount_amount', - currency_field='currency_id', + string="Discounted Amount", + compute="_compute_discount_amount", + currency_field="currency_id", readonly=True, ) - tax_ids = fields.Many2many( - comodel_name='account.tax', - ) + tax_ids = fields.Many2many(comodel_name="account.tax",) account_id = fields.Many2one( - comodel_name='account.account', + comodel_name="account.account", required=True, - string='Account', + string="Account", domain="[('user_type_id.type', 'not in', ['receivable', 'payable'])]", ) account_analytic_id = fields.Many2one( - comodel_name='account.analytic.account', - string='Analytic account', - ) - company_id = fields.Many2one( - related='invoice_id.company_id', - readonly=True, + comodel_name="account.analytic.account", string="Analytic account", ) + company_id = fields.Many2one(related="invoice_id.company_id", readonly=True,) def _compute_discount_display(self): """Given a discount type, we need to render a different symbol""" for one in self: - precision = self.env['decimal.precision'].precision_get('Discount') - one.discount_display = '{0:.{1}f}%'.format( - one.discount * -1, precision) + precision = self.env["decimal.precision"].precision_get("Discount") + one.discount_display = "{0:.{1}f}%".format(one.discount * -1, precision) - @api.depends('base', 'base_discounted') + @api.depends("base", "base_discounted") def _compute_discount_amount(self): """Compute the amount discounted""" for one in self: diff --git a/account_global_discount/models/account_move_line.py b/account_global_discount/models/account_move_line.py index 5116ebd8cdb..90f0f72fb7a 100644 --- a/account_global_discount/models/account_move_line.py +++ b/account_global_discount/models/account_move_line.py @@ -4,9 +4,9 @@ class AccountMoveLine(models.Model): - _inherit = 'account.move.line' + _inherit = "account.move.line" invoice_global_discount_id = fields.Many2one( - comodel_name='account.invoice.global.discount', - string='Invoice Global Discount', + comodel_name="account.invoice.global.discount", + string="Invoice Global Discount", ) diff --git a/account_global_discount/models/global_discount.py b/account_global_discount/models/global_discount.py index a36f9a6f1e7..8634d5476ac 100644 --- a/account_global_discount/models/global_discount.py +++ b/account_global_discount/models/global_discount.py @@ -4,21 +4,20 @@ class GlobalDiscount(models.Model): - _inherit = 'global.discount' + _inherit = "global.discount" account_id = fields.Many2one( - comodel_name='account.account', - string='Account', + comodel_name="account.account", + string="Account", domain="[('user_type_id.type', 'not in', ['receivable', 'payable'])]", ) account_analytic_id = fields.Many2one( - comodel_name='account.analytic.account', - string='Analytic account', + comodel_name="account.analytic.account", string="Analytic account", ) def _get_global_discount_vals(self, base, account_id=False, **kwargs): """Return account as well if passed""" res = super()._get_global_discount_vals(base) if account_id: - res.update({'account_id': account_id}) + res.update({"account_id": account_id}) return res diff --git a/account_global_discount/security/security.xml b/account_global_discount/security/security.xml index e2e0ca7b775..c7ee7509861 100644 --- a/account_global_discount/security/security.xml +++ b/account_global_discount/security/security.xml @@ -1,16 +1,19 @@ - + - Global Discount multi-company - - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] - - - - + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + + - diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index f55ffab849d..3912ea1c2d1 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -6,73 +6,79 @@ class TestGlobalDiscount(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', - 'discount_scope': 'sale', - 'discount': 20, - 'account_id': cls.account.id, - }) - cls.global_discount_2 = cls.global_discount_obj.create({ - 'name': 'Test Discount 2', - 'discount_scope': 'purchase', - 'discount': 30, - 'account_id': cls.account.id, - }) - cls.global_discount_3 = cls.global_discount_obj.create({ - 'name': 'Test Discount 3', - 'discount_scope': 'purchase', - '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", + "discount_scope": "sale", + "discount": 20, + "account_id": cls.account.id, + } + ) + cls.global_discount_2 = cls.global_discount_obj.create( + { + "name": "Test Discount 2", + "discount_scope": "purchase", + "discount": 30, + "account_id": cls.account.id, + } + ) + cls.global_discount_3 = cls.global_discount_obj.create( + { + "name": "Test Discount 3", + "discount_scope": "purchase", + "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.supplier_global_discount_ids = cls.global_discount_2 - cls.tax = cls.env['account.tax'].create({ - 'name': 'TAX 15%', - 'amount_type': 'percent', - 'type_tax_use': 'purchase', - 'amount': 15.0, - }) - cls.journal = cls.env["account.journal"].create({ - "name": "Test purchase journal", - "code": "TPUR", - "type": "purchase", - }) - cls.invoice = cls.env['account.invoice'].create({ - 'name': "Test Customer Invoice", - 'journal_id': cls.journal.id, - 'partner_id': cls.partner_1.id, - 'account_id': cls.account.id, - 'type': 'in_invoice', - }) - cls.invoice_line = cls.env['account.invoice.line'] - cls.invoice_line1 = cls.invoice_line.create({ - 'invoice_id': cls.invoice.id, - 'name': 'Line 1', - 'price_unit': 200.0, - 'account_id': cls.account.id, - 'invoice_line_tax_ids': [(6, 0, [cls.tax.id])], - 'quantity': 1, - }) + cls.tax = cls.env["account.tax"].create( + { + "name": "TAX 15%", + "amount_type": "percent", + "type_tax_use": "purchase", + "amount": 15.0, + } + ) + cls.journal = cls.env["account.journal"].create( + {"name": "Test purchase journal", "code": "TPUR", "type": "purchase",} + ) + cls.invoice = cls.env["account.invoice"].create( + { + "name": "Test Customer Invoice", + "journal_id": cls.journal.id, + "partner_id": cls.partner_1.id, + "account_id": cls.account.id, + "type": "in_invoice", + } + ) + cls.invoice_line = cls.env["account.invoice.line"] + cls.invoice_line1 = cls.invoice_line.create( + { + "invoice_id": cls.invoice.id, + "name": "Line 1", + "price_unit": 200.0, + "account_id": cls.account.id, + "invoice_line_tax_ids": [(6, 0, [cls.tax.id])], + "quantity": 1, + } + ) cls.invoice._onchange_invoice_line_ids() def test_01_global_invoice_succesive_discounts(self): @@ -85,10 +91,11 @@ def test_01_global_invoice_succesive_discounts(self): self.invoice.global_discount_ids = self.global_discount_3 self.invoice._onchange_global_discount_ids() self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) - precision = self.env['decimal.precision'].precision_get('Discount') + precision = self.env["decimal.precision"].precision_get("Discount") self.assertEqual( self.invoice.invoice_global_discount_ids.discount_display, - '-50.{}%'.format('0' * precision)) + "-50.{}%".format("0" * precision), + ) self.assertAlmostEqual(self.invoice.tax_line_ids.base, 100.0) self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 15.0) self.assertAlmostEqual(self.invoice.amount_untaxed, 100.0) @@ -139,35 +146,39 @@ def test_02_global_invoice_discounts_from_partner(self): self.assertAlmostEqual(self.invoice.amount_global_discount, -60.0) def test_03_multiple_taxes_multi_line(self): - tax2 = self.env['account.tax'].create({ - 'name': 'TAX 20%', - 'amount_type': 'percent', - 'type_tax_use': 'purchase', - 'amount': 20.0, - }) - self.invoice_line.create({ - 'invoice_id': self.invoice.id, - 'name': 'Line 2', - 'price_unit': 100.0, - 'account_id': self.account.id, - 'invoice_line_tax_ids': [(6, 0, [tax2.id])], - 'quantity': 1, - }) + tax2 = self.env["account.tax"].create( + { + "name": "TAX 20%", + "amount_type": "percent", + "type_tax_use": "purchase", + "amount": 20.0, + } + ) + self.invoice_line.create( + { + "invoice_id": self.invoice.id, + "name": "Line 2", + "price_unit": 100.0, + "account_id": self.account.id, + "invoice_line_tax_ids": [(6, 0, [tax2.id])], + "quantity": 1, + } + ) self.invoice.global_discount_ids = self.global_discount_1 self.invoice._onchange_global_discount_ids() # Global discounts are applied to the base and taxes are recomputed: # 300 - 20% (global disc. 1) = 240 self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) discount_tax_15 = self.invoice.invoice_global_discount_ids.filtered( - lambda x: x.tax_ids == self.tax) + lambda x: x.tax_ids == self.tax + ) discount_tax_20 = self.invoice.invoice_global_discount_ids.filtered( - lambda x: x.tax_ids == tax2) + lambda x: x.tax_ids == tax2 + ) self.assertAlmostEqual(discount_tax_15.discount_amount, 40) self.assertAlmostEqual(discount_tax_20.discount_amount, 20) - tax_line_15 = self.invoice.tax_line_ids.filtered( - lambda x: x.tax_id == self.tax) - tax_line_20 = self.invoice.tax_line_ids.filtered( - lambda x: x.tax_id == tax2) + tax_line_15 = self.invoice.tax_line_ids.filtered(lambda x: x.tax_id == self.tax) + tax_line_20 = self.invoice.tax_line_ids.filtered(lambda x: x.tax_id == tax2) self.assertAlmostEqual(tax_line_15.base, 160) self.assertAlmostEqual(tax_line_15.amount, 24) self.assertAlmostEqual(tax_line_20.base, 80.0) @@ -180,12 +191,14 @@ def test_03_multiple_taxes_multi_line(self): self.assertEqual(len(self.invoice.move_id.line_ids), 7) def test_04_multiple_taxes_same_line(self): - tax2 = self.env['account.tax'].create({ - 'name': 'Retention 20%', - 'amount_type': 'percent', - 'type_tax_use': 'purchase', - 'amount': -20.0, # negative for testing more use cases - }) + tax2 = self.env["account.tax"].create( + { + "name": "Retention 20%", + "amount_type": "percent", + "type_tax_use": "purchase", + "amount": -20.0, # negative for testing more use cases + } + ) self.invoice_line1.invoice_line_tax_ids = [(4, tax2.id)] self.invoice.global_discount_ids = self.global_discount_1 self.invoice._onchange_global_discount_ids() @@ -193,13 +206,13 @@ def test_04_multiple_taxes_same_line(self): # 300 - 20% (global disc. 1) = 240 self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) self.assertAlmostEqual( - self.invoice.invoice_global_discount_ids.discount_amount, 40) + self.invoice.invoice_global_discount_ids.discount_amount, 40 + ) self.assertEqual( - self.invoice.invoice_global_discount_ids.tax_ids, self.tax + tax2) - tax_line_15 = self.invoice.tax_line_ids.filtered( - lambda x: x.tax_id == self.tax) - tax_line_20 = self.invoice.tax_line_ids.filtered( - lambda x: x.tax_id == tax2) + self.invoice.invoice_global_discount_ids.tax_ids, self.tax + tax2 + ) + tax_line_15 = self.invoice.tax_line_ids.filtered(lambda x: x.tax_id == self.tax) + tax_line_20 = self.invoice.tax_line_ids.filtered(lambda x: x.tax_id == tax2) self.assertAlmostEqual(tax_line_15.base, 160) self.assertAlmostEqual(tax_line_15.amount, 24) self.assertAlmostEqual(tax_line_20.base, 160.0) @@ -217,22 +230,25 @@ def test_04_multiple_taxes_same_line(self): def test_05_incompatible_taxes(self): # Line 1 with tax and tax2 # Line 2 with only tax2 - tax2 = self.env['account.tax'].create({ - 'name': 'Retention 20%', - 'amount_type': 'percent', - 'type_tax_use': 'purchase', - 'amount': -20.0, # negative for testing more use cases - }) - self.invoice_line1.invoice_line_tax_ids = [ - (4, tax2.id), (4, self.tax.id)] - self.invoice_line.create({ - 'invoice_id': self.invoice.id, - 'name': 'Line 2', - 'price_unit': 100.0, - 'account_id': self.account.id, - 'invoice_line_tax_ids': [(6, 0, [tax2.id])], - 'quantity': 1, - }) + tax2 = self.env["account.tax"].create( + { + "name": "Retention 20%", + "amount_type": "percent", + "type_tax_use": "purchase", + "amount": -20.0, # negative for testing more use cases + } + ) + self.invoice_line1.invoice_line_tax_ids = [(4, tax2.id), (4, self.tax.id)] + self.invoice_line.create( + { + "invoice_id": self.invoice.id, + "name": "Line 2", + "price_unit": 100.0, + "account_id": self.account.id, + "invoice_line_tax_ids": [(6, 0, [tax2.id])], + "quantity": 1, + } + ) self.invoice.global_discount_ids = self.global_discount_1 with self.assertRaises(exceptions.UserError): self.invoice._onchange_global_discount_ids() diff --git a/account_global_discount/views/account_invoice_views.xml b/account_global_discount/views/account_invoice_views.xml index a65bc43b109..deaa17a6e3a 100644 --- a/account_global_discount/views/account_invoice_views.xml +++ b/account_global_discount/views/account_invoice_views.xml @@ -1,40 +1,74 @@ - + - account.invoice - + - + - - + + - + - - - - - - - - - - - - + + + + + + + + + + + + - diff --git a/account_global_discount/views/global_discount_views.xml b/account_global_discount/views/global_discount_views.xml index 18b91c0abb8..60c9b59bc2c 100644 --- a/account_global_discount/views/global_discount_views.xml +++ b/account_global_discount/views/global_discount_views.xml @@ -1,33 +1,39 @@ - + - global.discount - + - - + + - global.discount - + - - + + - - - + parent="account.account_management_menu" + /> diff --git a/account_global_discount/views/report_account_invoice.xml b/account_global_discount/views/report_account_invoice.xml index 43150536a24..7ff1793c95f 100644 --- a/account_global_discount/views/report_account_invoice.xml +++ b/account_global_discount/views/report_account_invoice.xml @@ -1,26 +1,35 @@ - + - - From 1e76414c378f5a7f53abde762ebae086682133b8 Mon Sep 17 00:00:00 2001 From: Ernesto Tejeda Date: Thu, 5 Nov 2020 15:22:50 -0500 Subject: [PATCH 10/34] [MIG] account_global_discount: Migration to 13.0 [UPD] Update account_global_discount.pot Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: account-invoicing-13.0/account-invoicing-13.0-account_global_discount Translate-URL: https://translation.odoo-community.org/projects/account-invoicing-13-0/account-invoicing-13-0-account_global_discount/ --- account_global_discount/README.rst | 46 +++- account_global_discount/__manifest__.py | 6 +- .../i18n/account_global_discount.pot | 46 ++-- account_global_discount/i18n/es.po | 51 ++-- account_global_discount/i18n/pt.po | 49 ++-- .../migrations/11.0.2.0.0/post-migration.py | 59 ----- .../migrations/11.0.2.0.0/pre-migration.py | 11 - .../migrations/13.0.1.0.0/post-migration.py | 18 ++ .../migrations/13.0.1.0.0/pre-migration.py | 40 ++++ account_global_discount/models/__init__.py | 3 +- .../{account_invoice.py => account_move.py} | 220 +++++++++-------- .../models/account_move_line.py | 12 - account_global_discount/readme/CONFIGURE.rst | 13 + .../readme/CONTRIBUTORS.rst | 1 + account_global_discount/readme/USAGE.rst | 26 +- account_global_discount/security/security.xml | 2 +- .../static/description/index.html | 69 ++++-- .../tests/test_global_discount.py | 226 ++++++++++-------- .../views/account_invoice_views.xml | 24 +- 19 files changed, 534 insertions(+), 388 deletions(-) delete mode 100644 account_global_discount/migrations/11.0.2.0.0/post-migration.py delete mode 100644 account_global_discount/migrations/11.0.2.0.0/pre-migration.py create mode 100644 account_global_discount/migrations/13.0.1.0.0/post-migration.py create mode 100644 account_global_discount/migrations/13.0.1.0.0/pre-migration.py rename account_global_discount/models/{account_invoice.py => account_move.py} (60%) delete mode 100644 account_global_discount/models/account_move_line.py create mode 100644 account_global_discount/readme/CONFIGURE.rst diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst index b815bac018d..ac9091ca121 100644 --- a/account_global_discount/README.rst +++ b/account_global_discount/README.rst @@ -14,13 +14,13 @@ Account 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%2Faccount--invoicing-lightgray.png?logo=github - :target: https://github.com/OCA/account-invoicing/tree/12.0/account_global_discount + :target: https://github.com/OCA/account-invoicing/tree/13.0/account_global_discount :alt: OCA/account-invoicing .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-invoicing-12-0/account-invoicing-12-0-account_global_discount + :target: https://translation.odoo-community.org/projects/account-invoicing-13-0/account-invoicing-13-0-account_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/95/12.0 + :target: https://runbot.odoo-community.org/runbot/95/13.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -32,13 +32,13 @@ Apply global discounts to invoices .. contents:: :local: -Usage -===== +Configuration +============= -To use this module, you need to: +To configure this module, you need to: -#. Go to *Settings > Parameters > Global Discounts* -#. Add a new discount that can be either by percentage or fixed amount. +#. Go to *Settings > Parameters > Global Discounts*. +#. Add a new discount percentage. #. Choose the discount scope (sales or purchases). #. You can also restrict it to a certain company if needed. @@ -46,9 +46,28 @@ 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. +#. In section sale, you can set sale discounts. +#. In section purchase, you can set purchase discounts. + +Usage +===== + +To use this module, you need to: + +#. Go to *Invoicing > Customers > Invoices*. +#. Create a new sales invoice, choose a customer with a defined global + discount and you will see how the value of the 'Invoice Global Discounts' + field is auto-completed with the global discounts defined in the customer + (See configuration section in this readme), although you can choose + then other global discounts defined in configuration. +#. Add several invoice lines. +#. At the bottom of the form you will see how global discounts affect the + total values. +#. Go to the 'Journal Items' tab (if you have permissions for that). + There you will see how the tax lines have the discount percentage applied + and you will also see the lines that reflect the global discount applied. +#. In the 'Other info' tab, you can see in the 'Global Discounts' table, + the global discounts applied to each of the invoice lines. Known issues / Roadmap ====================== @@ -64,7 +83,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. @@ -85,6 +104,7 @@ Contributors * David Vidal * Carlos Dauden * Rafael Blasco + * Ernesto Tejeda Maintainers ~~~~~~~~~~~ @@ -99,6 +119,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/account-invoicing `_ project on GitHub. +This module is part of the `OCA/account-invoicing `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index cdc14a865f3..3b3a035ee28 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,12 +3,12 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "12.0.1.0.3", + "version": "13.0.1.0.0", "category": "Accounting", - "author": "Tecnativa," "Odoo Community Association (OCA)", + "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-backend", "license": "AGPL-3", - "depends": ["account", "base_global_discount",], + "depends": ["account", "base_global_discount"], "data": [ "security/ir.model.access.csv", "security/security.xml", diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot index 025db9ff780..b7aba67fd78 100644 --- a/account_global_discount/i18n/account_global_discount.pot +++ b/account_global_discount/i18n/account_global_discount.pot @@ -1,12 +1,12 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * account_global_discount +# * account_global_discount # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: <>\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -15,7 +15,9 @@ msgstr "" #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document -msgid "Global Discounts
" +msgid "" +"Global Discounts\n" +"
" msgstr "" #. module: account_global_discount @@ -30,8 +32,8 @@ msgid "Account" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_untaxed_before_global_discounts -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_tax__base_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__base_before_global_discounts msgid "Amount Untaxed Before Discounts" msgstr "" @@ -51,6 +53,11 @@ msgstr "" msgid "Company" msgstr "" +#. module: account_global_discount +#: model:ir.model.fields,help:account_global_discount.field_account_invoice_global_discount__company_id +msgid "Company related to this journal" +msgstr "" + #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" @@ -92,7 +99,7 @@ msgid "Discounted amount" msgstr "" #. module: account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Discounts..." msgstr "" @@ -107,9 +114,14 @@ msgstr "" msgid "Global Discount" msgstr "" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__global_discount_item +msgid "Global Discount Item" +msgstr "" + #. module: account_global_discount #: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Global Discounts" msgstr "" @@ -119,32 +131,31 @@ msgid "ID" msgstr "" #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:63 +#: code:addons/account_global_discount/models/account_move.py:0 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" #. module: account_global_discount -#: model:ir.model,name:account_global_discount.model_account_invoice #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id msgid "Invoice" msgstr "" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__invoice_global_discount_ids #: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id msgid "Invoice Global Discount" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids msgid "Invoice Global Discounts" msgstr "" #. module: account_global_discount -#: model:ir.model,name:account_global_discount.model_account_invoice_tax -msgid "Invoice Tax" +#: model:ir.model,name:account_global_discount.model_account_move +msgid "Journal Entries" msgstr "" #. module: account_global_discount @@ -173,18 +184,17 @@ msgid "Tax" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount msgid "Total Global Discounts" msgstr "" #. module: account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Untaxed Amount Before Disc." msgstr "" #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:56 +#: code:addons/account_global_discount/models/account_move.py:0 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" - diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po index e90b2312e4d..9731f101e55 100644 --- a/account_global_discount/i18n/es.po +++ b/account_global_discount/i18n/es.po @@ -19,8 +19,10 @@ msgstr "" #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document -msgid "Global Discounts
" -msgstr "Descuento Global" +msgid "" +"Global Discounts\n" +"
" +msgstr "" #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document @@ -34,8 +36,8 @@ msgid "Account" msgstr "Cuenta" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_untaxed_before_global_discounts -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_tax__base_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__base_before_global_discounts msgid "Amount Untaxed Before Discounts" msgstr "Base Imponible sin Descuentos" @@ -55,6 +57,11 @@ msgstr "Base descontada" msgid "Company" msgstr "Compañía" +#. module: account_global_discount +#: model:ir.model.fields,help:account_global_discount.field_account_invoice_global_discount__company_id +msgid "Company related to this journal" +msgstr "" + #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" @@ -96,7 +103,7 @@ msgid "Discounted amount" msgstr "Importe Descontado" #. module: account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Discounts..." msgstr "Descuentos..." @@ -111,9 +118,14 @@ msgstr "Nombre mostrado" msgid "Global Discount" msgstr "Descuento Global" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__global_discount_item +msgid "Global Discount Item" +msgstr "" + #. module: account_global_discount #: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Global Discounts" msgstr "Descuentos Globales" @@ -123,34 +135,32 @@ msgid "ID" msgstr "ID (identificación)" #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:63 +#: code:addons/account_global_discount/models/account_move.py:0 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "" #. module: account_global_discount -#: model:ir.model,name:account_global_discount.model_account_invoice #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id msgid "Invoice" msgstr "Factura" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__invoice_global_discount_ids #: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id msgid "Invoice Global Discount" msgstr "Descuento Global en Factura" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids msgid "Invoice Global Discounts" msgstr "Descuentos Globales en Factura" #. module: account_global_discount -#: model:ir.model,name:account_global_discount.model_account_invoice_tax -#, fuzzy -msgid "Invoice Tax" -msgstr "Factura" +#: model:ir.model,name:account_global_discount.model_account_move +msgid "Journal Entries" +msgstr "" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move_line @@ -178,17 +188,24 @@ msgid "Tax" msgstr "" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount msgid "Total Global Discounts" msgstr "Total Descuentos Globales" #. module: account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Untaxed Amount Before Disc." msgstr "Base Imponible Antes Desc." #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:56 +#: code:addons/account_global_discount/models/account_move.py:0 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "" + +#~ msgid "Global Discounts
" +#~ msgstr "Descuento Global" + +#, fuzzy +#~ msgid "Invoice Tax" +#~ msgstr "Factura" diff --git a/account_global_discount/i18n/pt.po b/account_global_discount/i18n/pt.po index 7fbf6ea99df..bf2a7784e8e 100644 --- a/account_global_discount/i18n/pt.po +++ b/account_global_discount/i18n/pt.po @@ -18,8 +18,10 @@ msgstr "" #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document -msgid "Global Discounts
" -msgstr "Descontos Globais
" +msgid "" +"Global Discounts\n" +"
" +msgstr "" #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document @@ -33,8 +35,8 @@ msgid "Account" msgstr "Conta" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_untaxed_before_global_discounts -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_tax__base_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__base_before_global_discounts msgid "Amount Untaxed Before Discounts" msgstr "Montante sem Impostos antes de Descontos" @@ -54,6 +56,11 @@ msgstr "Base com desconto" msgid "Company" msgstr "Empresa" +#. module: account_global_discount +#: model:ir.model.fields,help:account_global_discount.field_account_invoice_global_discount__company_id +msgid "Company related to this journal" +msgstr "" + #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" @@ -95,7 +102,7 @@ msgid "Discounted amount" msgstr "Montante descontado" #. module: account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Discounts..." msgstr "Descontos..." @@ -110,9 +117,14 @@ msgstr "Nome a Apresentar" msgid "Global Discount" msgstr "Desconto Global" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__global_discount_item +msgid "Global Discount Item" +msgstr "" + #. module: account_global_discount #: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Global Discounts" msgstr "Descontos Globais" @@ -122,33 +134,32 @@ msgid "ID" msgstr "ID" #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:63 +#: code:addons/account_global_discount/models/account_move.py:0 #, python-format msgid "Incompatible taxes found for global discounts." msgstr "Foram encontrados impostos incompatíveis nos descontos globais." #. module: account_global_discount -#: model:ir.model,name:account_global_discount.model_account_invoice #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id msgid "Invoice" msgstr "Fatura" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__invoice_global_discount_ids #: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id msgid "Invoice Global Discount" msgstr "Desconto Global de Fatura" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids msgid "Invoice Global Discounts" msgstr "Descontos Globais de Fatura" #. module: account_global_discount -#: model:ir.model,name:account_global_discount.model_account_invoice_tax -msgid "Invoice Tax" -msgstr "Imposto de Fatura" +#: model:ir.model,name:account_global_discount.model_account_move +msgid "Journal Entries" +msgstr "" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move_line @@ -176,17 +187,23 @@ msgid "Tax" msgstr "Imposto" #. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice__amount_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount msgid "Total Global Discounts" msgstr "Descontos Globais Totais" #. module: account_global_discount -#: model_terms:ir.ui.view,arch_db:account_global_discount.account_invoice_form_view +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Untaxed Amount Before Disc." msgstr "Montante sem Impostos antes de Desc." #. module: account_global_discount -#: code:addons/account_global_discount/models/account_invoice.py:56 +#: code:addons/account_global_discount/models/account_move.py:0 #, python-format msgid "With global discounts, taxes in lines are required." msgstr "Com descontos globais, são requeridos impostos nas linhas." + +#~ msgid "Global Discounts
" +#~ msgstr "Descontos Globais
" + +#~ msgid "Invoice Tax" +#~ msgstr "Imposto de Fatura" diff --git a/account_global_discount/migrations/11.0.2.0.0/post-migration.py b/account_global_discount/migrations/11.0.2.0.0/post-migration.py deleted file mode 100644 index 60fe1c343c6..00000000000 --- a/account_global_discount/migrations/11.0.2.0.0/post-migration.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2020 Tecnativa - Pedro M. Baeza -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openupgradelib import openupgrade # pylint: disable=W7936 -from psycopg2 import sql - - -@openupgrade.migrate() -def migrate(env, version): - # Link the new field that points to the invoice global discount instead - # of the global discount definition - openupgrade.logged_query( - env.cr, - sql.SQL( - """ - UPDATE account_move_line aml - SET invoice_global_discount_id = aigd.id - FROM account_invoice_global_discount aigd - WHERE aigd.invoice_id = aml.invoice_id - AND aigd.global_discount_id = aml.{} - """ - ).format(sql.Identifier(openupgrade.get_legacy_name("global_discount_id"))), - ) - # Link to existing global discount records, all the invoice taxes as best - # effort - openupgrade.logged_query( - env.cr, - """ - INSERT INTO account_invoice_global_discount_account_tax_rel - (account_invoice_global_discount_id, account_tax_id) - SELECT aigd.id, ailt.tax_id - FROM account_invoice_global_discount aigd - JOIN account_invoice_line ail ON aigd.invoice_id = ail.invoice_id - JOIN account_invoice_line_tax ailt ON ailt.invoice_line_id = ail.id - GROUP BY aigd.id, ailt.tax_id""", - ) - # Delete in prevention of manual manipulations existing tax lines linked - # to global discount journal items - openupgrade.logged_query( - env.cr, - """ - DELETE FROM account_move_line_account_tax_rel rel - USING account_move_line aml - WHERE rel.account_move_line_id = aml.id - AND aml.invoice_global_discount_id IS NOT NULL""", - ) - # Link all invoice taxes in global discount existing journal items as best - # effort - openupgrade.logged_query( - env.cr, - """ - INSERT INTO account_move_line_account_tax_rel - (account_move_line_id, account_tax_id) - SELECT aml.id, rel.account_tax_id - FROM account_move_line aml - JOIN account_invoice_global_discount_account_tax_rel rel - ON rel.account_invoice_global_discount_id = - aml.invoice_global_discount_id""", - ) diff --git a/account_global_discount/migrations/11.0.2.0.0/pre-migration.py b/account_global_discount/migrations/11.0.2.0.0/pre-migration.py deleted file mode 100644 index d3b95329df0..00000000000 --- a/account_global_discount/migrations/11.0.2.0.0/pre-migration.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2020 Tecnativa - Pedro M. Baeza -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openupgradelib import openupgrade # pylint: disable=W7936 - - -@openupgrade.migrate() -def migrate(env, version): - openupgrade.rename_columns( - env.cr, {"account_move_line": [("global_discount_id", None)]} - ) diff --git a/account_global_discount/migrations/13.0.1.0.0/post-migration.py b/account_global_discount/migrations/13.0.1.0.0/post-migration.py new file mode 100644 index 00000000000..a7a3d4050c9 --- /dev/null +++ b/account_global_discount/migrations/13.0.1.0.0/post-migration.py @@ -0,0 +1,18 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + # _onchange_invoice_line_ids method is called to recalculate the + # value of the base_before_global_discounts field in + # account.move.line, to update the invoice_global_discount_ids + # table table in account.move and to create the necessary discount + # journal entries. + domain = [ + ("type", "in", env["account.move"].get_invoice_types()), + ("state", "=", "draft"), + ("global_discount_ids", "!=", False), + ] + env["account.move"].search(domain)._onchange_invoice_line_ids() diff --git a/account_global_discount/migrations/13.0.1.0.0/pre-migration.py b/account_global_discount/migrations/13.0.1.0.0/pre-migration.py new file mode 100644 index 00000000000..c712376ee6c --- /dev/null +++ b/account_global_discount/migrations/13.0.1.0.0/pre-migration.py @@ -0,0 +1,40 @@ +# Copyright 2020 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade # pylint: disable=W7936 +from psycopg2 import sql + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.rename_columns( + env.cr, + { + "account_invoice_global_discount": [("invoice_id", None)], + "account_invoice_global_discount_rel": [("invoice_id", None)], + }, + ) + openupgrade.logged_query( + env.cr, "ALTER TABLE account_invoice_global_discount ADD invoice_id INT4" + ) + openupgrade.logged_query( + env.cr, "ALTER TABLE account_invoice_global_discount_rel ADD invoice_id INT4" + ) + openupgrade.logged_query( + env.cr, + sql.SQL( + """UPDATE account_invoice_global_discount aigd + SET invoice_id = ai.move_id + FROM account_invoice ai + WHERE ai.id = aigd.{}""" + ).format(sql.Identifier(openupgrade.get_legacy_name("invoice_id"))), + ) + openupgrade.logged_query( + env.cr, + sql.SQL( + """UPDATE account_invoice_global_discount_rel aigdr + SET invoice_id = ai.move_id + FROM account_invoice ai + WHERE ai.id = aigdr.{}""" + ).format(sql.Identifier(openupgrade.get_legacy_name("invoice_id"))), + ) diff --git a/account_global_discount/models/__init__.py b/account_global_discount/models/__init__.py index 3c75f36efd7..4a3305a1e70 100644 --- a/account_global_discount/models/__init__.py +++ b/account_global_discount/models/__init__.py @@ -1,3 +1,2 @@ -from . import account_invoice -from . import account_move_line +from . import account_move from . import global_discount diff --git a/account_global_discount/models/account_invoice.py b/account_global_discount/models/account_move.py similarity index 60% rename from account_global_discount/models/account_invoice.py rename to account_global_discount/models/account_move.py index 6c4a88a1b94..5d1ec0ced0b 100644 --- a/account_global_discount/models/account_invoice.py +++ b/account_global_discount/models/account_move.py @@ -2,12 +2,11 @@ # 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 +from odoo.tools import config -from odoo.addons import decimal_precision as dp - -class AccountInvoice(models.Model): - _inherit = "account.invoice" +class AccountMove(models.Model): + _inherit = "account.move" global_discount_ids = fields.Many2many( comodel_name="global.discount", @@ -27,12 +26,14 @@ class AccountInvoice(models.Model): compute="_compute_amount", currency_field="currency_id", readonly=True, + compute_sudo=True, ) amount_untaxed_before_global_discounts = fields.Monetary( string="Amount Untaxed Before Discounts", compute="_compute_amount", currency_field="currency_id", readonly=True, + compute_sudo=True, ) invoice_global_discount_ids = fields.One2many( comodel_name="account.invoice.global.discount", @@ -40,67 +41,136 @@ class AccountInvoice(models.Model): readonly=True, ) + def _recompute_tax_lines(self, recompute_tax_base_amount=False): + super()._recompute_tax_lines(recompute_tax_base_amount) + # if recompute_tax_base_amount: + self._update_tax_lines_for_global_discount() + + def _update_tax_lines_for_global_discount(self): + """ Update tax_base_amount and taxes debits.""" + round_curr = self.currency_id.round + tax_lines = self.line_ids.filtered( + lambda r: r.tax_line_id.amount_type in ("percent", "division") + ) + for tax_line in tax_lines: + base = tax_line.tax_base_amount + tax_line.base_before_global_discounts = base + amount = tax_line.balance + for discount in self.global_discount_ids: + base = discount._get_global_discount_vals(base)["base_discounted"] + amount = discount._get_global_discount_vals(amount)["base_discounted"] + tax_line.tax_base_amount = round_curr(base) + tax_line.debit = amount > 0.0 and amount or 0.0 + tax_line.credit = amount < 0.0 and -amount or 0.0 + # Apply onchanges + tax_line._onchange_amount_currency() + tax_line._onchange_balance() + def _set_global_discounts_by_tax(self): """Create invoice global discount lines by taxes combinations and discounts. """ self.ensure_one() - if not self.global_discount_ids: - return - invoice_global_discounts = self.env["account.invoice.global.discount"] taxes_keys = {} # Perform a sanity check for discarding cases that will lead to # incorrect data in discounts for inv_line in self.invoice_line_ids.filtered(lambda l: not l.display_type): - if not inv_line.invoice_line_tax_ids: + if not inv_line.tax_ids and ( + not config["test_enable"] + or self.env.context.get("test_account_global_discount") + ): raise exceptions.UserError( _("With global discounts, taxes in lines are required.") ) for key in taxes_keys: - if key == inv_line.invoice_line_tax_ids: + if key == tuple(inv_line.tax_ids.ids): break - elif key & inv_line.invoice_line_tax_ids: + elif set(key) & set(inv_line.tax_ids.ids) and ( + not config["test_enable"] + or self.env.context.get("test_account_global_discount") + ): raise exceptions.UserError( _("Incompatible taxes found for global discounts.") ) else: - taxes_keys[inv_line.invoice_line_tax_ids] = True - for tax_line in self.tax_line_ids: + taxes_keys[tuple(inv_line.tax_ids.ids)] = True + self.invoice_global_discount_ids = False + invoice_global_discounts = [] + for tax_line in self.line_ids.filtered("tax_line_id"): key = [] to_create = True for key in taxes_keys: - if tax_line.tax_id in key: + if tax_line.tax_line_id.id in key: to_create = taxes_keys[key] taxes_keys[key] = False # mark for not duplicating break # we leave in key variable the proper taxes value if not to_create: continue - base = tax_line.base_before_global_discounts or tax_line.base + base = tax_line.base_before_global_discounts or tax_line.tax_base_amount for global_discount in self.global_discount_ids: discount = global_discount._get_global_discount_vals(base) - invoice_global_discounts += invoice_global_discounts.new( - { - "name": global_discount.display_name, - "invoice_id": self.id, - "global_discount_id": global_discount.id, - "discount": global_discount.discount, - "base": base, - "base_discounted": discount["base_discounted"], - "account_id": global_discount.account_id.id, - "tax_ids": [(4, x.id) for x in key], - } + invoice_global_discounts.append( + ( + 0, + 0, + { + "name": global_discount.display_name, + "invoice_id": self.id, + "global_discount_id": global_discount.id, + "discount": global_discount.discount, + "base": base, + "base_discounted": discount["base_discounted"], + "account_id": global_discount.account_id.id, + "tax_ids": [(4, taxid) for taxid in key], + }, + ) ) base = discount["base_discounted"] self.invoice_global_discount_ids = invoice_global_discounts + def _create_global_discount_journal_items(self): + """Append global discounts move lines""" + lines_to_delete = self.line_ids.filtered("global_discount_item") + if self != self._origin: + self.line_ids -= lines_to_delete + else: + lines_to_delete.with_context(check_move_validity=False).unlink() + vals_list = [] + for discount in self.invoice_global_discount_ids.filtered("discount"): + disc_amount = discount.discount_amount + vals_list.append( + ( + 0, + 0, + { + "global_discount_item": True, + "name": "%s - %s" + % (discount.name, ", ".join(discount.tax_ids.mapped("name"))), + "debit": disc_amount > 0.0 and disc_amount or 0.0, + "credit": disc_amount < 0.0 and -disc_amount or 0.0, + "account_id": discount.account_id.id, + "analytic_account_id": discount.account_analytic_id.id, + "exclude_from_invoice_tab": True, + }, + ) + ) + self.line_ids = vals_list + self._onchange_recompute_dynamic_lines() + def _set_global_discounts(self): """Get global discounts in order and apply them in chain. They will be - fetched in their sequence order """ + fetched in their sequence order""" for inv in self: inv._set_global_discounts_by_tax() + inv._create_global_discount_journal_items() @api.onchange("invoice_line_ids") def _onchange_invoice_line_ids(self): + others_lines = self.line_ids.filtered( + lambda line: line.exclude_from_invoice_tab + ) + if others_lines: + others_lines[0].recompute_tax_line = True res = super()._onchange_invoice_line_ids() self._set_global_discounts() return res @@ -127,6 +197,8 @@ def _onchange_global_discount_ids(self): def _compute_amount_one(self): if not self.invoice_global_discount_ids: + self.amount_global_discount = 0.0 + self.amount_untaxed_before_global_discounts = 0.0 return round_curr = self.currency_id.round self.amount_global_discount = sum( @@ -143,7 +215,7 @@ def _compute_amount_one(self): and self.company_id and self.currency_id != self.company_id.currency_id ): - date = self.date_invoice or fields.Date.today() + date = self.invoice_date or fields.Date.today() amount_total_company_signed = self.currency_id._convert( self.amount_total, self.company_id.currency_id, self.company_id, date ) @@ -156,13 +228,13 @@ def _compute_amount_one(self): self.amount_untaxed_signed = amount_untaxed_signed * sign @api.depends( - "invoice_line_ids.price_subtotal", - "tax_line_ids.amount", - "tax_line_ids.amount_rounding", - "currency_id", - "company_id", - "date_invoice", - "type", + "line_ids.debit", + "line_ids.credit", + "line_ids.currency_id", + "line_ids.amount_currency", + "line_ids.amount_residual", + "line_ids.amount_residual_currency", + "line_ids.payment_id.state", "invoice_global_discount_ids", "global_discount_ids", ) @@ -171,99 +243,57 @@ def _compute_amount(self): for record in self: record._compute_amount_one() - def get_taxes_values(self): - """Override this computation for adding global discount to taxes.""" - round_curr = self.currency_id.round - tax_grouped = super().get_taxes_values() - for key in tax_grouped.keys(): - base = tax_grouped[key]["base"] - tax_grouped[key]["base_before_global_discounts"] = base - amount = tax_grouped[key]["amount"] - for discount in self.global_discount_ids: - base = discount._get_global_discount_vals(base)["base_discounted"] - amount = discount._get_global_discount_vals(amount)["base_discounted"] - tax_grouped[key]["base"] = round_curr(base) - tax_grouped[key]["amount"] = round_curr(amount) - return tax_grouped - - @api.model - def invoice_line_move_line_get(self): - """Append global discounts move lines""" - res = super().invoice_line_move_line_get() - for discount in self.invoice_global_discount_ids: - if not discount.discount: - continue - # Traverse upstream result for taking existing dictionary vals - inv_lines = self.invoice_line_ids.filtered( - lambda x: x.invoice_line_tax_ids == discount.tax_ids - ) - discount_dict = {} - for move_line_dict in res: - if move_line_dict.get("invl_id", 0) in inv_lines.ids: - discount_dict.update(move_line_dict) - # Change needed values for the global discount - discount_dict.update( - { - "invoice_global_discount_id": discount.id, - "type": "global_discount", - "name": "%s - %s" - % (discount.name, ", ".join(discount.tax_ids.mapped("name"))), - "price_unit": discount.discount_amount * -1, - "quantity": 1, - "price": discount.discount_amount * -1, - "account_id": discount.account_id.id, - "account_analytic_id": discount.account_analytic_id.id, - } - ) - res.append(discount_dict) - return res - -class AccountInvoiceTax(models.Model): - _inherit = "account.invoice.tax" +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + invoice_global_discount_id = fields.Many2one( + comodel_name="account.invoice.global.discount", + string="Invoice Global Discount", + ) base_before_global_discounts = fields.Monetary( string="Amount Untaxed Before Discounts", readonly=True, ) + global_discount_item = fields.Boolean() class AccountInvoiceGlobalDiscount(models.Model): _name = "account.invoice.global.discount" _description = "Invoice Global Discount" - name = fields.Char(string="Discount Name", required=True,) + name = fields.Char(string="Discount Name", required=True) invoice_id = fields.Many2one( - "account.invoice", + "account.move", string="Invoice", ondelete="cascade", index=True, readonly=True, + domain=[ + ("type", "in", ["out_invoice", "out_refund", "in_invoice", "in_refund"]) + ], ) global_discount_id = fields.Many2one( comodel_name="global.discount", string="Global Discount", readonly=True, ) - discount = fields.Float(string="Discount (number)", readonly=True,) + discount = fields.Float(string="Discount (number)", readonly=True) discount_display = fields.Char( compute="_compute_discount_display", readonly=True, string="Discount", ) base = fields.Float( - string="Base discounted", - digits=dp.get_precision("Product Price"), - readonly=True, + string="Base discounted", digits="Product Price", readonly=True, ) base_discounted = fields.Float( - string="Discounted amount", - digits=dp.get_precision("Product Price"), - readonly=True, + string="Discounted amount", digits="Product Price", readonly=True, ) - currency_id = fields.Many2one(related="invoice_id.currency_id", readonly=True,) + currency_id = fields.Many2one(related="invoice_id.currency_id", readonly=True) discount_amount = fields.Monetary( string="Discounted Amount", compute="_compute_discount_amount", currency_field="currency_id", readonly=True, + compute_sudo=True, ) - tax_ids = fields.Many2many(comodel_name="account.tax",) + tax_ids = fields.Many2many(comodel_name="account.tax") account_id = fields.Many2one( comodel_name="account.account", required=True, @@ -273,7 +303,7 @@ class AccountInvoiceGlobalDiscount(models.Model): account_analytic_id = fields.Many2one( comodel_name="account.analytic.account", string="Analytic account", ) - company_id = fields.Many2one(related="invoice_id.company_id", readonly=True,) + company_id = fields.Many2one(related="invoice_id.company_id", readonly=True) def _compute_discount_display(self): """Given a discount type, we need to render a different symbol""" diff --git a/account_global_discount/models/account_move_line.py b/account_global_discount/models/account_move_line.py deleted file mode 100644 index 90f0f72fb7a..00000000000 --- a/account_global_discount/models/account_move_line.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2019 Tecnativa - David Vidal -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class AccountMoveLine(models.Model): - _inherit = "account.move.line" - - invoice_global_discount_id = fields.Many2one( - comodel_name="account.invoice.global.discount", - string="Invoice Global Discount", - ) diff --git a/account_global_discount/readme/CONFIGURE.rst b/account_global_discount/readme/CONFIGURE.rst new file mode 100644 index 00000000000..a6d4b5c93e4 --- /dev/null +++ b/account_global_discount/readme/CONFIGURE.rst @@ -0,0 +1,13 @@ +To configure this module, you need to: + +#. Go to *Settings > Parameters > Global Discounts*. +#. Add a new discount percentage. +#. 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, you can set sale discounts. +#. In section purchase, you can set purchase discounts. diff --git a/account_global_discount/readme/CONTRIBUTORS.rst b/account_global_discount/readme/CONTRIBUTORS.rst index 7b1dda35578..872df73d393 100644 --- a/account_global_discount/readme/CONTRIBUTORS.rst +++ b/account_global_discount/readme/CONTRIBUTORS.rst @@ -4,3 +4,4 @@ * David Vidal * Carlos Dauden * Rafael Blasco + * Ernesto Tejeda diff --git a/account_global_discount/readme/USAGE.rst b/account_global_discount/readme/USAGE.rst index 7ef8742387e..07854344107 100644 --- a/account_global_discount/readme/USAGE.rst +++ b/account_global_discount/readme/USAGE.rst @@ -1,14 +1,16 @@ To use this module, you need to: -#. Go to *Settings > Parameters > Global Discounts* -#. Add a new discount that can be either by percentage or fixed amount. -#. 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. +#. Go to *Invoicing > Customers > Invoices*. +#. Create a new sales invoice, choose a customer with a defined global + discount and you will see how the value of the 'Invoice Global Discounts' + field is auto-completed with the global discounts defined in the customer + (See configuration section in this readme), although you can choose + then other global discounts defined in configuration. +#. Add several invoice lines. +#. At the bottom of the form you will see how global discounts affect the + total values. +#. Go to the 'Journal Items' tab (if you have permissions for that). + There you will see how the tax lines have the discount percentage applied + and you will also see the lines that reflect the global discount applied. +#. In the 'Other info' tab, you can see in the 'Global Discounts' table, + the global discounts applied to each of the invoice lines. diff --git a/account_global_discount/security/security.xml b/account_global_discount/security/security.xml index c7ee7509861..11d680f82f1 100644 --- a/account_global_discount/security/security.xml +++ b/account_global_discount/security/security.xml @@ -10,7 +10,7 @@ /> ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + >['|',('company_id','=',False),('company_id', 'in', company_ids)]
diff --git a/account_global_discount/static/description/index.html b/account_global_discount/static/description/index.html index e7b6d9d7b93..750cb6a6622 100644 --- a/account_global_discount/static/description/index.html +++ b/account_global_discount/static/description/index.html @@ -367,28 +367,29 @@

Account Global Discount

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

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runbot

Apply global discounts to invoices

Table of contents

-
-

Usage

-

To use this module, you need to:

+
+

Configuration

+

To configure this module, you need to:

    -
  1. Go to Settings > Parameters > Global Discounts
  2. -
  3. Add a new discount that can be either by percentage or fixed amount.
  4. +
  5. Go to Settings > Parameters > Global Discounts.
  6. +
  7. Add a new discount percentage.
  8. Choose the discount scope (sales or purchases).
  9. You can also restrict it to a certain company if needed.
@@ -396,13 +397,32 @@

Usage

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

Usage

+

To use this module, you need to:

+
    +
  1. Go to Invoicing > Customers > Invoices.
  2. +
  3. Create a new sales invoice, choose a customer with a defined global +discount and you will see how the value of the ‘Invoice Global Discounts’ +field is auto-completed with the global discounts defined in the customer +(See configuration section in this readme), although you can choose +then other global discounts defined in configuration.
  4. +
  5. Add several invoice lines.
  6. +
  7. At the bottom of the form you will see how global discounts affect the +total values.
  8. +
  9. Go to the ‘Journal Items’ tab (if you have permissions for that). +There you will see how the tax lines have the discount percentage applied +and you will also see the lines that reflect the global discount applied.
  10. +
  11. In the ‘Other info’ tab, you can see in the ‘Global Discounts’ table, +the global discounts applied to each of the invoice lines.
-

Known issues / Roadmap

+

Known issues / Roadmap

  • Not all the taxes combination can be compatible with global discounts, as the generated journal items won’t be correct for taxes declarations. An error @@ -411,41 +431,42 @@

    Known issues / Roadmap

-

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 -feedback.

+feedback.

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Tecnativa
-

Contributors

+

Contributors

  • Tecnativa
    • Pedro M. Baeza
    • David Vidal
    • Carlos Dauden
    • Rafael Blasco
    • +
    • Ernesto Tejeda
-

Maintainers

+

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/account-invoicing project on GitHub.

+

This module is part of the OCA/account-invoicing project on GitHub.

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

diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index 3912ea1c2d1..7b122c79b74 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -2,7 +2,7 @@ # Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import exceptions -from odoo.tests import common +from odoo.tests import Form, common class TestGlobalDiscount(common.SavepointCase): @@ -10,7 +10,7 @@ class TestGlobalDiscount(common.SavepointCase): def setUpClass(cls): super().setUpClass() cls.account_type = cls.env["account.account.type"].create( - {"name": "Test", "type": "receivable",} + {"name": "Test", "type": "other", "internal_group": "income"} ) cls.account = cls.env["account.account"].create( { @@ -20,6 +20,28 @@ def setUpClass(cls): "reconcile": True, } ) + cls.account_type_receivable = cls.env["account.account.type"].create( + {"name": "Test", "type": "receivable", "internal_group": "income"} + ) + cls.account_receivable = cls.env["account.account"].create( + { + "name": "Test receivable account", + "code": "ACCRV", + "user_type_id": cls.account_type_receivable.id, + "reconcile": True, + } + ) + cls.account_type_payable = cls.env["account.account.type"].create( + {"name": "Test", "type": "payable", "internal_group": "income"} + ) + cls.account_payable = cls.env["account.account"].create( + { + "name": "Test receivable account", + "code": "ACCPAY", + "user_type_id": cls.account_type_payable.id, + "reconcile": True, + } + ) cls.global_discount_obj = cls.env["global.discount"] cls.global_discount_1 = cls.global_discount_obj.create( { @@ -45,8 +67,20 @@ def setUpClass(cls): "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_1 = cls.env["res.partner"].create( + { + "name": "Mr. Odoo", + "property_account_receivable_id": cls.account_receivable.id, + "property_account_payable_id": cls.account_payable.id, + } + ) + cls.partner_2 = cls.env["res.partner"].create( + { + "name": "Mrs. Odoo", + "property_account_receivable_id": cls.account_receivable.id, + "property_account_payable_id": cls.account_payable.id, + } + ) cls.partner_2.supplier_global_discount_ids = cls.global_discount_2 cls.tax = cls.env["account.tax"].create( { @@ -57,47 +91,42 @@ def setUpClass(cls): } ) cls.journal = cls.env["account.journal"].create( - {"name": "Test purchase journal", "code": "TPUR", "type": "purchase",} + {"name": "Test purchase journal", "code": "TPUR", "type": "purchase"} ) - cls.invoice = cls.env["account.invoice"].create( - { - "name": "Test Customer Invoice", - "journal_id": cls.journal.id, - "partner_id": cls.partner_1.id, - "account_id": cls.account.id, - "type": "in_invoice", - } - ) - cls.invoice_line = cls.env["account.invoice.line"] - cls.invoice_line1 = cls.invoice_line.create( - { - "invoice_id": cls.invoice.id, - "name": "Line 1", - "price_unit": 200.0, - "account_id": cls.account.id, - "invoice_line_tax_ids": [(6, 0, [cls.tax.id])], - "quantity": 1, - } + cls.invoice_line = cls.env["account.move.line"] + invoice_form = Form( + cls.env["account.move"].with_context( + default_type="in_invoice", test_account_global_discount=True, + ) ) - cls.invoice._onchange_invoice_line_ids() + invoice_form.partner_id = cls.partner_1 + with invoice_form.invoice_line_ids.new() as line_form: + line_form.name = "Line 1" + line_form.price_unit = 200.0 + line_form.quantity = 1 + line_form.tax_ids.clear() + line_form.tax_ids.add(cls.tax) + cls.invoice = invoice_form.save() def test_01_global_invoice_succesive_discounts(self): """Add global discounts to the invoice""" + invoice_no_check = self.invoice.with_context(check_move_validity=False) + invoice_tax_line = self.invoice.line_ids.filtered("tax_line_id") self.assertAlmostEqual(self.invoice.amount_total, 230) - self.assertAlmostEqual(self.invoice.tax_line_ids.base, 200.0) - self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 30.0) + self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 200.0) + self.assertAlmostEqual(invoice_tax_line.balance, 30.0) # Global discounts are applied to the base and taxes are recomputed: # 200 - 50% (global disc. 1) = 100 - self.invoice.global_discount_ids = self.global_discount_3 - self.invoice._onchange_global_discount_ids() + invoice_no_check.global_discount_ids = [(6, 0, self.global_discount_3.ids)] + invoice_no_check._onchange_global_discount_ids() self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) precision = self.env["decimal.precision"].precision_get("Discount") self.assertEqual( self.invoice.invoice_global_discount_ids.discount_display, "-50.{}%".format("0" * precision), ) - self.assertAlmostEqual(self.invoice.tax_line_ids.base, 100.0) - self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 15.0) + self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 100.0) + self.assertAlmostEqual(invoice_tax_line.balance, 15.0) self.assertAlmostEqual(self.invoice.amount_untaxed, 100.0) self.assertAlmostEqual(self.invoice.amount_total, 115.0) self.assertAlmostEqual(self.invoice.amount_global_discount, -100.0) @@ -105,11 +134,11 @@ def test_01_global_invoice_succesive_discounts(self): # 200 - 50% (global disc. 1) = 100 # 100 - 30% (global disc. 2) = 70 # The global discounts amount is then 200 - 70 = 130 - self.invoice.global_discount_ids += self.global_discount_2 - self.invoice._onchange_global_discount_ids() + self.invoice.global_discount_ids = [(4, self.global_discount_2.id)] + invoice_no_check._onchange_global_discount_ids() self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) - self.assertAlmostEqual(self.invoice.tax_line_ids.base, 70.0) - self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 10.5) + self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 70.0) + self.assertAlmostEqual(invoice_tax_line.balance, 10.5) self.assertAlmostEqual(self.invoice.amount_untaxed, 70.0) self.assertAlmostEqual(self.invoice.amount_total, 80.5) self.assertAlmostEqual(self.invoice.amount_global_discount, -130.0) @@ -118,56 +147,54 @@ def test_01_global_invoice_succesive_discounts(self): # 160 - 50% (global disc. 1) = 80 # 80 - 30% (global disc. 2) = 56 # The global discounts amount is then 160 - 56 = 104 - self.invoice_line1.discount = 20 - self.invoice._onchange_invoice_line_ids() + invoice_no_check.invoice_line_ids.discount = 20 + invoice_no_check._onchange_invoice_line_ids() self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) - self.assertAlmostEqual(self.invoice.tax_line_ids.base, 56.0) - self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 8.4) + self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 56.0) + self.assertAlmostEqual(invoice_tax_line.balance, 8.4) self.assertAlmostEqual(self.invoice.amount_untaxed, 56.0) self.assertAlmostEqual(self.invoice.amount_total, 64.4) self.assertAlmostEqual(self.invoice.amount_global_discount, -104.0) def test_02_global_invoice_discounts_from_partner(self): """Change the partner and his global discounts go to the invoice""" + invoice_no_check = self.invoice.with_context(check_move_validity=False) + invoice_tax_line = self.invoice.line_ids.filtered("tax_line_id") self.assertAlmostEqual(self.invoice.amount_total, 230) - self.assertAlmostEqual(self.invoice.tax_line_ids.base, 200.0) - self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 30.0) + self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 200.0) + self.assertAlmostEqual(invoice_tax_line.balance, 30.0) # When we change the parter, his global discounts are fetched depending # on the type of the invoice. In this case, we fetch the supplier # global discounts self.invoice.partner_id = self.partner_2 # trigger onchanges mimicking UI - self.invoice._onchange_partner_id() - self.invoice._onchange_global_discount_ids() - self.assertAlmostEqual(self.invoice.tax_line_ids.base, 140.0) - self.assertAlmostEqual(self.invoice.tax_line_ids.amount, 21.0) + invoice_no_check._onchange_partner_id() + invoice_no_check._onchange_global_discount_ids() + self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 140.0) + self.assertAlmostEqual(invoice_tax_line.balance, 21.0) self.assertAlmostEqual(self.invoice.amount_untaxed, 140.0) self.assertAlmostEqual(self.invoice.amount_total, 161.0) self.assertAlmostEqual(self.invoice.amount_global_discount, -60.0) def test_03_multiple_taxes_multi_line(self): + invoice_no_check = self.invoice.with_context(check_move_validity=False) tax2 = self.env["account.tax"].create( { - "name": "TAX 20%", + "name": "TAX 20% 2", "amount_type": "percent", "type_tax_use": "purchase", "amount": 20.0, } ) - self.invoice_line.create( - { - "invoice_id": self.invoice.id, - "name": "Line 2", - "price_unit": 100.0, - "account_id": self.account.id, - "invoice_line_tax_ids": [(6, 0, [tax2.id])], - "quantity": 1, - } - ) - self.invoice.global_discount_ids = self.global_discount_1 - self.invoice._onchange_global_discount_ids() - # Global discounts are applied to the base and taxes are recomputed: - # 300 - 20% (global disc. 1) = 240 + with Form(self.invoice) as invoice_form: + invoice_form.global_discount_ids.add(self.global_discount_1) + with invoice_form.invoice_line_ids.new() as line_form: + line_form.name = "Line 2" + line_form.price_unit = 100.0 + line_form.quantity = 1 + line_form.tax_ids.clear() + line_form.tax_ids.add(tax2) + invoice_no_check._onchange_global_discount_ids() self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) discount_tax_15 = self.invoice.invoice_global_discount_ids.filtered( lambda x: x.tax_ids == self.tax @@ -177,20 +204,24 @@ def test_03_multiple_taxes_multi_line(self): ) self.assertAlmostEqual(discount_tax_15.discount_amount, 40) self.assertAlmostEqual(discount_tax_20.discount_amount, 20) - tax_line_15 = self.invoice.tax_line_ids.filtered(lambda x: x.tax_id == self.tax) - tax_line_20 = self.invoice.tax_line_ids.filtered(lambda x: x.tax_id == tax2) - self.assertAlmostEqual(tax_line_15.base, 160) - self.assertAlmostEqual(tax_line_15.amount, 24) - self.assertAlmostEqual(tax_line_20.base, 80.0) - self.assertAlmostEqual(tax_line_20.amount, 16) + tax_line_15 = self.invoice.line_ids.filtered( + lambda x: x.tax_line_id == self.tax + ) + tax_line_20 = self.invoice.line_ids.filtered(lambda x: x.tax_line_id == tax2) + self.assertAlmostEqual(tax_line_15.tax_base_amount, 160) + self.assertAlmostEqual(tax_line_15.balance, 24) + self.assertAlmostEqual(tax_line_20.tax_base_amount, 80.0) + self.assertAlmostEqual(tax_line_20.balance, 16) self.assertAlmostEqual(self.invoice.amount_untaxed, 240.0) self.assertAlmostEqual(self.invoice.amount_total, 280) self.assertAlmostEqual(self.invoice.amount_global_discount, -60.0) # Validate invoice for seeing result - self.invoice.action_invoice_open() - self.assertEqual(len(self.invoice.move_id.line_ids), 7) + self.assertEqual(len(self.invoice.line_ids), 7) + self.invoice.post() + self.assertEqual(len(self.invoice.line_ids), 7) def test_04_multiple_taxes_same_line(self): + invoice_no_check = self.invoice.with_context(check_move_validity=False) tax2 = self.env["account.tax"].create( { "name": "Retention 20%", @@ -199,9 +230,11 @@ def test_04_multiple_taxes_same_line(self): "amount": -20.0, # negative for testing more use cases } ) - self.invoice_line1.invoice_line_tax_ids = [(4, tax2.id)] - self.invoice.global_discount_ids = self.global_discount_1 - self.invoice._onchange_global_discount_ids() + with Form(self.invoice.with_context(check_move_validity=False)) as invoice_form: + invoice_form.global_discount_ids.add(self.global_discount_1) + with invoice_form.invoice_line_ids.edit(0) as line_form: + line_form.tax_ids.add(tax2) + invoice_no_check._onchange_global_discount_ids() # Global discounts are applied to the base and taxes are recomputed: # 300 - 20% (global disc. 1) = 240 self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) @@ -211,21 +244,21 @@ def test_04_multiple_taxes_same_line(self): self.assertEqual( self.invoice.invoice_global_discount_ids.tax_ids, self.tax + tax2 ) - tax_line_15 = self.invoice.tax_line_ids.filtered(lambda x: x.tax_id == self.tax) - tax_line_20 = self.invoice.tax_line_ids.filtered(lambda x: x.tax_id == tax2) - self.assertAlmostEqual(tax_line_15.base, 160) - self.assertAlmostEqual(tax_line_15.amount, 24) - self.assertAlmostEqual(tax_line_20.base, 160.0) - self.assertAlmostEqual(tax_line_20.amount, -32) + tax_line_15 = self.invoice.line_ids.filtered( + lambda x: x.tax_line_id == self.tax + ) + tax_line_20 = self.invoice.line_ids.filtered(lambda x: x.tax_line_id == tax2) + self.assertAlmostEqual(tax_line_15.tax_base_amount, 160) + self.assertAlmostEqual(tax_line_15.balance, 24) + self.assertAlmostEqual(tax_line_20.tax_base_amount, 160.0) + self.assertAlmostEqual(tax_line_20.balance, -32) self.assertAlmostEqual(self.invoice.amount_untaxed, 160.0) self.assertAlmostEqual(self.invoice.amount_total, 152) self.assertAlmostEqual(self.invoice.amount_global_discount, -40.0) # Validate invoice for seeing result - self.invoice.action_invoice_open() - move = self.invoice.move_id - self.assertEqual(len(move.line_ids), 5) - line = move.line_ids.filtered(lambda x: "Test Discount 1" in x.name) - self.assertEqual(line.tax_ids, self.tax + tax2) + self.assertEqual(len(self.invoice.line_ids), 5) + self.invoice.post() + self.assertEqual(len(self.invoice.line_ids), 5) def test_05_incompatible_taxes(self): # Line 1 with tax and tax2 @@ -238,23 +271,20 @@ def test_05_incompatible_taxes(self): "amount": -20.0, # negative for testing more use cases } ) - self.invoice_line1.invoice_line_tax_ids = [(4, tax2.id), (4, self.tax.id)] - self.invoice_line.create( - { - "invoice_id": self.invoice.id, - "name": "Line 2", - "price_unit": 100.0, - "account_id": self.account.id, - "invoice_line_tax_ids": [(6, 0, [tax2.id])], - "quantity": 1, - } - ) - self.invoice.global_discount_ids = self.global_discount_1 with self.assertRaises(exceptions.UserError): - self.invoice._onchange_global_discount_ids() + with Form(self.invoice) as invoice_form: + invoice_form.global_discount_ids.add(self.global_discount_1) + with invoice_form.invoice_line_ids.new() as line_form: + line_form.name = "Line 2" + line_form.price_unit = 100.0 + line_form.quantity = 1 + line_form.tax_ids.clear() + line_form.tax_ids.add(self.tax) + line_form.tax_ids.add(tax2) def test_06_no_taxes(self): - self.invoice_line1.invoice_line_tax_ids = False - self.invoice.global_discount_ids = self.global_discount_1 with self.assertRaises(exceptions.UserError): - self.invoice._onchange_global_discount_ids() + with Form(self.invoice) as invoice_form: + invoice_form.global_discount_ids.add(self.global_discount_1) + with invoice_form.invoice_line_ids.edit(0) as line_form: + line_form.tax_ids.clear() diff --git a/account_global_discount/views/account_invoice_views.xml b/account_global_discount/views/account_invoice_views.xml index deaa17a6e3a..16855d89eb5 100644 --- a/account_global_discount/views/account_invoice_views.xml +++ b/account_global_discount/views/account_invoice_views.xml @@ -2,17 +2,20 @@ - - account.invoice - + + account.move + - + + + + - + - + + + + - + From a04e7bc17e989604878a2a60c6e9f5fb3fe634ba Mon Sep 17 00:00:00 2001 From: Ernesto Tejeda Date: Wed, 23 Dec 2020 13:24:48 -0500 Subject: [PATCH 11/34] [FIX] account_global_discount: wrong tax move line discounts on invoice post --- account_global_discount/__manifest__.py | 2 +- account_global_discount/models/account_move.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 3b3a035ee28..be4e3ff075e 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "13.0.1.0.0", + "version": "13.0.1.0.1", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-backend", diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index 5d1ec0ced0b..5a753c0c957 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -43,8 +43,13 @@ class AccountMove(models.Model): def _recompute_tax_lines(self, recompute_tax_base_amount=False): super()._recompute_tax_lines(recompute_tax_base_amount) - # if recompute_tax_base_amount: - self._update_tax_lines_for_global_discount() + # If recompute_tax_base_amount is True, only the tax_base_amount + # field is recalculated, therefore the debit and debit fields + # will not be recalculated and it doesn't make sense to apply + # the global discount to the taxes move lines by calling the + # _update_tax_lines_for_global_discount method. + if not recompute_tax_base_amount: + self._update_tax_lines_for_global_discount() def _update_tax_lines_for_global_discount(self): """ Update tax_base_amount and taxes debits.""" From 607d31234977449b9008673af66d019ccb7ac1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Tue, 26 Jan 2021 11:02:08 +0100 Subject: [PATCH 12/34] [FIX] account_global_discount: Compute global discount with 0% taxes Co-Authored-By: Pedro M. Baeza [UPD] README.rst --- account_global_discount/README.rst | 1 + .../models/account_move.py | 48 +++++++++++------ .../readme/CONTRIBUTORS.rst | 1 + .../static/description/index.html | 1 + .../tests/test_global_discount.py | 52 +++++++++++++++++++ 5 files changed, 86 insertions(+), 17 deletions(-) diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst index ac9091ca121..b7b1ac9a5fd 100644 --- a/account_global_discount/README.rst +++ b/account_global_discount/README.rst @@ -105,6 +105,7 @@ Contributors * Carlos Dauden * Rafael Blasco * Ernesto Tejeda + * Víctor Martínez Maintainers ~~~~~~~~~~~ diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index 5a753c0c957..82e9f5654cb 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -1,5 +1,6 @@ # Copyright 2019 Tecnativa - David Vidal # Copyright 2020 Tecnativa - Pedro M. Baeza +# Copyright 2021 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import _, api, exceptions, fields, models from odoo.tools import config @@ -71,6 +72,22 @@ def _update_tax_lines_for_global_discount(self): tax_line._onchange_amount_currency() tax_line._onchange_balance() + def _prepare_global_discount_vals(self, global_discount, base, tax_ids): + """Prepare the dictionary values for an invoice global discount + line. + """ + discount = global_discount._get_global_discount_vals(base) + return { + "name": global_discount.display_name, + "invoice_id": self.id, + "global_discount_id": global_discount.id, + "discount": global_discount.discount, + "base": base, + "base_discounted": discount["base_discounted"], + "account_id": global_discount.account_id.id, + "tax_ids": [(4, tax_id) for tax_id in tax_ids], + } + def _set_global_discounts_by_tax(self): """Create invoice global discount lines by taxes combinations and discounts. @@ -113,24 +130,21 @@ def _set_global_discounts_by_tax(self): continue base = tax_line.base_before_global_discounts or tax_line.tax_base_amount for global_discount in self.global_discount_ids: - discount = global_discount._get_global_discount_vals(base) - invoice_global_discounts.append( - ( - 0, - 0, - { - "name": global_discount.display_name, - "invoice_id": self.id, - "global_discount_id": global_discount.id, - "discount": global_discount.discount, - "base": base, - "base_discounted": discount["base_discounted"], - "account_id": global_discount.account_id.id, - "tax_ids": [(4, taxid) for taxid in key], - }, + vals = self._prepare_global_discount_vals(global_discount, base, key) + invoice_global_discounts.append((0, 0, vals)) + base = vals["base_discounted"] + # Check all moves with defined taxes to check if there's any discount not + # created (tax amount is zero and only one tax is applied) + for line in self.line_ids.filtered("tax_ids"): + key = tuple(line.tax_ids.ids) + if taxes_keys.get(key): + base = line.price_subtotal + for global_discount in self.global_discount_ids: + vals = self._prepare_global_discount_vals( + global_discount, base, key ) - ) - base = discount["base_discounted"] + invoice_global_discounts.append((0, 0, vals)) + base = vals["base_discounted"] self.invoice_global_discount_ids = invoice_global_discounts def _create_global_discount_journal_items(self): diff --git a/account_global_discount/readme/CONTRIBUTORS.rst b/account_global_discount/readme/CONTRIBUTORS.rst index 872df73d393..8a3c8075c37 100644 --- a/account_global_discount/readme/CONTRIBUTORS.rst +++ b/account_global_discount/readme/CONTRIBUTORS.rst @@ -5,3 +5,4 @@ * Carlos Dauden * Rafael Blasco * Ernesto Tejeda + * Víctor Martínez diff --git a/account_global_discount/static/description/index.html b/account_global_discount/static/description/index.html index 750cb6a6622..a91a4013898 100644 --- a/account_global_discount/static/description/index.html +++ b/account_global_discount/static/description/index.html @@ -455,6 +455,7 @@

Contributors

  • Carlos Dauden
  • Rafael Blasco
  • Ernesto Tejeda
  • +
  • Víctor Martínez
  • diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index 7b122c79b74..5764d3578d3 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -1,5 +1,6 @@ # Copyright 2019 Tecnativa - David Vidal # Copyright 2020 Tecnativa - Pedro M. Baeza +# Copyright 2021 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import exceptions from odoo.tests import Form, common @@ -90,6 +91,14 @@ def setUpClass(cls): "amount": 15.0, } ) + cls.tax_0 = cls.env["account.tax"].create( + { + "name": "TAX 0%", + "amount_type": "percent", + "type_tax_use": "purchase", + "amount": 0.0, + } + ) cls.journal = cls.env["account.journal"].create( {"name": "Test purchase journal", "code": "TPUR", "type": "purchase"} ) @@ -288,3 +297,46 @@ def test_06_no_taxes(self): invoice_form.global_discount_ids.add(self.global_discount_1) with invoice_form.invoice_line_ids.edit(0) as line_form: line_form.tax_ids.clear() + + def test_07_line_with_tax_0(self): + invoice_form = Form( + self.env["account.move"].with_context( + default_type="in_invoice", test_account_global_discount=True, + ) + ) + invoice_form.partner_id = self.partner_1 + invoice_form.global_discount_ids.add(self.global_discount_1) + with invoice_form.invoice_line_ids.new() as line_form: + line_form.name = "Line 1" + line_form.price_unit = 200.0 + line_form.quantity = 1 + line_form.tax_ids.clear() + line_form.tax_ids.add(self.tax_0) + invoice = invoice_form.save() + invoice._onchange_global_discount_ids() + self.assertEqual(len(invoice.invoice_global_discount_ids), 1) + discount_tax_0 = invoice.invoice_global_discount_ids.filtered( + lambda x: x.tax_ids == self.tax_0 + ) + self.assertAlmostEqual(discount_tax_0.discount_amount, 40) + + def test_07_line2_with_tax_0(self): + invoice_no_check = self.invoice.with_context(check_move_validity=False) + with Form(self.invoice) as invoice_form: + invoice_form.global_discount_ids.add(self.global_discount_1) + with invoice_form.invoice_line_ids.new() as line_form: + line_form.name = "Line 2" + line_form.price_unit = 100.0 + line_form.quantity = 1 + line_form.tax_ids.clear() + line_form.tax_ids.add(self.tax_0) + invoice_no_check._onchange_global_discount_ids() + self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) + discount_tax_15 = self.invoice.invoice_global_discount_ids.filtered( + lambda x: x.tax_ids == self.tax + ) + self.assertAlmostEqual(discount_tax_15.discount_amount, 40) + discount_tax_0 = self.invoice.invoice_global_discount_ids.filtered( + lambda x: x.tax_ids == self.tax_0 + ) + self.assertAlmostEqual(discount_tax_0.discount_amount, 20) From 15a21b3d9a8fb770568e51feae2c759b248ca318 Mon Sep 17 00:00:00 2001 From: Cas Vissers Date: Fri, 29 Jan 2021 08:59:38 +0000 Subject: [PATCH 13/34] Added translation using Weblate (Dutch) Currently translated at 100.0% (35 of 35 strings) Translation: account-invoicing-13.0/account-invoicing-13.0-account_global_discount Translate-URL: https://translation.odoo-community.org/projects/account-invoicing-13-0/account-invoicing-13-0-account_global_discount/nl_NL/ --- .../i18n/account_global_discount.pot | 5 - account_global_discount/i18n/nl_NL.po | 205 ++++++++++++++++++ 2 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 account_global_discount/i18n/nl_NL.po diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot index b7aba67fd78..318886fc021 100644 --- a/account_global_discount/i18n/account_global_discount.pot +++ b/account_global_discount/i18n/account_global_discount.pot @@ -53,11 +53,6 @@ msgstr "" msgid "Company" msgstr "" -#. module: account_global_discount -#: model:ir.model.fields,help:account_global_discount.field_account_invoice_global_discount__company_id -msgid "Company related to this journal" -msgstr "" - #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" diff --git a/account_global_discount/i18n/nl_NL.po b/account_global_discount/i18n/nl_NL.po new file mode 100644 index 00000000000..b4082e5826f --- /dev/null +++ b/account_global_discount/i18n/nl_NL.po @@ -0,0 +1,205 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_global_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-01-29 11:44+0000\n" +"Last-Translator: Cas Vissers \n" +"Language-Team: none\n" +"Language: nl_NL\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 4.3.2\n" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "" +"Global Discounts\n" +"
    " +msgstr "" +"Algemene kortingen\n" +"
    " + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "Subtotal w/o disc." +msgstr "Subtotaal zonder korting" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_id +msgid "Account" +msgstr "Bedrag" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__base_before_global_discounts +msgid "Amount Untaxed Before Discounts" +msgstr "Onbelast bedrag voor kortingen" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_analytic_id +msgid "Analytic account" +msgstr "Kostenplaats" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base +msgid "Base discounted" +msgstr "Basis korting" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id +msgid "Company" +msgstr "Bedrijf" + +#. module: account_global_discount +#: model:ir.model.fields,help:account_global_discount.field_account_invoice_global_discount__company_id +msgid "Company related to this journal" +msgstr "Bedrijf gerelateerd aan journaal" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid +msgid "Created by" +msgstr "Aangemaakt door" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_date +msgid "Created on" +msgstr "Aangemaakt op" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__currency_id +msgid "Currency" +msgstr "Valuta" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_display +msgid "Discount" +msgstr "Korting" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount +msgid "Discount (number)" +msgstr "Korting (nummer)" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__name +msgid "Discount Name" +msgstr "Kortingsnaam" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_amount +msgid "Discounted Amount" +msgstr "Kortingsbedrag" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted +msgid "Discounted amount" +msgstr "Kortingsbedrag" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +msgid "Discounts..." +msgstr "Korting…" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__display_name +msgid "Display Name" +msgstr "Schermnaam" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__global_discount_id +msgid "Global Discount" +msgstr "Algemene korting" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__global_discount_item +msgid "Global Discount Item" +msgstr "Algemeen korting item" + +#. module: account_global_discount +#: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +msgid "Global Discounts" +msgstr "Algemene kortingen" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__id +msgid "ID" +msgstr "ID" + +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_move.py:0 +#, python-format +msgid "Incompatible taxes found for global discounts." +msgstr "Incompatibele belastingen gevonden voor algemene kortingen." + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id +msgid "Invoice" +msgstr "Factuur" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id +msgid "Invoice Global Discount" +msgstr "Factuur algemene korting" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids +msgid "Invoice Global Discounts" +msgstr "Factuur algemene kortingen" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_move +msgid "Journal Entries" +msgstr "Boekingen" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_move_line +msgid "Journal Item" +msgstr "Boeking" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount____last_update +msgid "Last Modified on" +msgstr "Laatst gewijzigd op" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_uid +msgid "Last Updated by" +msgstr "Laatst bijgewerkt door" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_date +msgid "Last Updated on" +msgstr "Laatst bijgewerkt op" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids +msgid "Tax" +msgstr "Belasting" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount +msgid "Total Global Discounts" +msgstr "Totaal algemene kortingen" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +msgid "Untaxed Amount Before Disc." +msgstr "Onbelast bedrag voor kort." + +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_move.py:0 +#, python-format +msgid "With global discounts, taxes in lines are required." +msgstr "Met globale kortingen zijn belastingen in regels vereist." From de2d0595c7686433e33fb7d2881feced67923911 Mon Sep 17 00:00:00 2001 From: OCA Transbot Date: Mon, 8 Mar 2021 08:05:26 +0000 Subject: [PATCH 14/34] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: account-invoicing-13.0/account-invoicing-13.0-account_global_discount Translate-URL: https://translation.odoo-community.org/projects/account-invoicing-13-0/account-invoicing-13-0-account_global_discount/ --- account_global_discount/i18n/es.po | 5 ----- account_global_discount/i18n/nl_NL.po | 8 +++----- account_global_discount/i18n/pt.po | 5 ----- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po index 9731f101e55..f06f8c6be56 100644 --- a/account_global_discount/i18n/es.po +++ b/account_global_discount/i18n/es.po @@ -57,11 +57,6 @@ msgstr "Base descontada" msgid "Company" msgstr "Compañía" -#. module: account_global_discount -#: model:ir.model.fields,help:account_global_discount.field_account_invoice_global_discount__company_id -msgid "Company related to this journal" -msgstr "" - #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" diff --git a/account_global_discount/i18n/nl_NL.po b/account_global_discount/i18n/nl_NL.po index b4082e5826f..558b41df04f 100644 --- a/account_global_discount/i18n/nl_NL.po +++ b/account_global_discount/i18n/nl_NL.po @@ -58,11 +58,6 @@ msgstr "Basis korting" msgid "Company" msgstr "Bedrijf" -#. module: account_global_discount -#: model:ir.model.fields,help:account_global_discount.field_account_invoice_global_discount__company_id -msgid "Company related to this journal" -msgstr "Bedrijf gerelateerd aan journaal" - #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" @@ -203,3 +198,6 @@ msgstr "Onbelast bedrag voor kort." #, python-format msgid "With global discounts, taxes in lines are required." msgstr "Met globale kortingen zijn belastingen in regels vereist." + +#~ msgid "Company related to this journal" +#~ msgstr "Bedrijf gerelateerd aan journaal" diff --git a/account_global_discount/i18n/pt.po b/account_global_discount/i18n/pt.po index bf2a7784e8e..2c96d260977 100644 --- a/account_global_discount/i18n/pt.po +++ b/account_global_discount/i18n/pt.po @@ -56,11 +56,6 @@ msgstr "Base com desconto" msgid "Company" msgstr "Empresa" -#. module: account_global_discount -#: model:ir.model.fields,help:account_global_discount.field_account_invoice_global_discount__company_id -msgid "Company related to this journal" -msgstr "" - #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid msgid "Created by" From 39d4f8e8bda8afd4936cd77bc2483d871cdb5bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=20Su=C3=A1rez?= Date: Wed, 10 Mar 2021 08:26:59 +0000 Subject: [PATCH 15/34] Translated using Weblate (Spanish) Currently translated at 100.0% (34 of 34 strings) Translation: account-invoicing-13.0/account-invoicing-13.0-account_global_discount Translate-URL: https://translation.odoo-community.org/projects/account-invoicing-13-0/account-invoicing-13-0-account_global_discount/es/ --- account_global_discount/i18n/es.po | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po index f06f8c6be56..9f975e49463 100644 --- a/account_global_discount/i18n/es.po +++ b/account_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: \n" -"PO-Revision-Date: 2020-07-08 11:07+0200\n" -"Last-Translator: <>\n" +"PO-Revision-Date: 2021-03-10 10:46+0000\n" +"Last-Translator: Ana Suárez \n" "Language-Team: \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: \n" -"X-Generator: Poedit 2.3\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.3.2\n" #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document @@ -23,6 +23,8 @@ msgid "" "Global Discounts\n" "
    " msgstr "" +"Descuentos Globales\n" +"
    " #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document @@ -116,7 +118,7 @@ msgstr "Descuento Global" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__global_discount_item msgid "Global Discount Item" -msgstr "" +msgstr "Artículo de Descuento Global" #. module: account_global_discount #: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount @@ -133,7 +135,7 @@ msgstr "ID (identificación)" #: code:addons/account_global_discount/models/account_move.py:0 #, python-format msgid "Incompatible taxes found for global discounts." -msgstr "" +msgstr "Impuestos incompatibles encontrados para descuentos globales." #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id @@ -155,7 +157,7 @@ msgstr "Descuentos Globales en Factura" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move msgid "Journal Entries" -msgstr "" +msgstr "Asientos contables" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move_line @@ -180,7 +182,7 @@ msgstr "Última actualización en" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids msgid "Tax" -msgstr "" +msgstr "Impuesto" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount @@ -196,7 +198,7 @@ msgstr "Base Imponible Antes Desc." #: code:addons/account_global_discount/models/account_move.py:0 #, python-format msgid "With global discounts, taxes in lines are required." -msgstr "" +msgstr "Con descuentos globales, se requieren impuestos en las líneas." #~ msgid "Global Discounts
    " #~ msgstr "Descuento Global" From 1e4ae23b6cf839609f798c8075894c1bc4238378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 15 Mar 2021 10:21:20 +0100 Subject: [PATCH 16/34] [FIX] account_global_discount: Fix tests --- account_global_discount/__manifest__.py | 2 +- .../tests/test_global_discount.py | 25 ++++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index be4e3ff075e..f492e74fe38 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "13.0.1.0.1", + "version": "13.0.1.0.2", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-backend", diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index 5764d3578d3..b3818073bb4 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -299,23 +299,14 @@ def test_06_no_taxes(self): line_form.tax_ids.clear() def test_07_line_with_tax_0(self): - invoice_form = Form( - self.env["account.move"].with_context( - default_type="in_invoice", test_account_global_discount=True, - ) - ) - invoice_form.partner_id = self.partner_1 - invoice_form.global_discount_ids.add(self.global_discount_1) - with invoice_form.invoice_line_ids.new() as line_form: - line_form.name = "Line 1" - line_form.price_unit = 200.0 - line_form.quantity = 1 - line_form.tax_ids.clear() - line_form.tax_ids.add(self.tax_0) - invoice = invoice_form.save() - invoice._onchange_global_discount_ids() - self.assertEqual(len(invoice.invoice_global_discount_ids), 1) - discount_tax_0 = invoice.invoice_global_discount_ids.filtered( + with Form(self.invoice) as invoice_form: + invoice_form.global_discount_ids.add(self.global_discount_1) + with invoice_form.invoice_line_ids.edit(0) as line_form: + line_form.tax_ids.clear() + line_form.tax_ids.add(self.tax_0) + self.invoice._onchange_global_discount_ids() + self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) + discount_tax_0 = self.invoice.invoice_global_discount_ids.filtered( lambda x: x.tax_ids == self.tax_0 ) self.assertAlmostEqual(discount_tax_0.discount_amount, 40) From 9b719e08bb321c7c4843dd11347063225074c8b8 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 20 Apr 2021 11:58:11 +0200 Subject: [PATCH 17/34] [FIX] account_global_discount: discounts on create When we set the global discount on the create method we must ensure that the move lines are properly recomputed --- account_global_discount/models/account_move.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index 82e9f5654cb..2dcd58396cf 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -262,6 +262,16 @@ def _compute_amount(self): for record in self: record._compute_amount_one() + @api.model_create_multi + def create(self, vals_list): + """If we create the invoice with the discounts already set like from + a sales order, we must compute the global discounts as well""" + moves = super().create(vals_list) + move_with_global_discounts = moves.filtered("global_discount_ids") + for move in move_with_global_discounts: + move.with_context(check_move_validity=False)._onchange_invoice_line_ids() + return moves + class AccountMoveLine(models.Model): _inherit = "account.move.line" From 8b693f4f1d0d5a645e8e623188a6271f220b1a60 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 11 Dec 2020 16:53:22 +0100 Subject: [PATCH 18/34] [IMP] account_global_discount: security group Users will need a specific group to set global discounts so it's easier to decide who's allowed to apply them. [UPD] Update account_global_discount.pot --- account_global_discount/__manifest__.py | 2 +- account_global_discount/i18n/account_global_discount.pot | 6 ++++++ account_global_discount/i18n/es.po | 6 ++++++ account_global_discount/i18n/nl_NL.po | 6 ++++++ account_global_discount/i18n/pt.po | 6 ++++++ account_global_discount/models/account_move.py | 9 +++++++++ account_global_discount/views/account_invoice_views.xml | 8 ++++++++ account_global_discount/views/global_discount_views.xml | 1 + 8 files changed, 43 insertions(+), 1 deletion(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index f492e74fe38..bf8c50da913 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "13.0.1.0.2", + "version": "13.0.1.1.0", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-backend", diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot index 318886fc021..4bfc309c15f 100644 --- a/account_global_discount/i18n/account_global_discount.pot +++ b/account_global_discount/i18n/account_global_discount.pot @@ -145,9 +145,15 @@ msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Invoice Global Discounts" msgstr "" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids_readonly +msgid "Invoice Global Discounts (readonly)" +msgstr "" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move msgid "Journal Entries" diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po index 9f975e49463..9fd062a5c3d 100644 --- a/account_global_discount/i18n/es.po +++ b/account_global_discount/i18n/es.po @@ -151,9 +151,15 @@ msgstr "Descuento Global en Factura" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Invoice Global Discounts" msgstr "Descuentos Globales en Factura" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids_readonly +msgid "Invoice Global Discounts (readonly)" +msgstr "" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move msgid "Journal Entries" diff --git a/account_global_discount/i18n/nl_NL.po b/account_global_discount/i18n/nl_NL.po index 558b41df04f..5f955fc9431 100644 --- a/account_global_discount/i18n/nl_NL.po +++ b/account_global_discount/i18n/nl_NL.po @@ -150,9 +150,15 @@ msgstr "Factuur algemene korting" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Invoice Global Discounts" msgstr "Factuur algemene kortingen" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids_readonly +msgid "Invoice Global Discounts (readonly)" +msgstr "" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move msgid "Journal Entries" diff --git a/account_global_discount/i18n/pt.po b/account_global_discount/i18n/pt.po index 2c96d260977..44e1664109b 100644 --- a/account_global_discount/i18n/pt.po +++ b/account_global_discount/i18n/pt.po @@ -148,9 +148,15 @@ msgstr "Desconto Global de Fatura" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Invoice Global Discounts" msgstr "Descontos Globais de Fatura" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids_readonly +msgid "Invoice Global Discounts (readonly)" +msgstr "" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move msgid "Journal Entries" diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index 2dcd58396cf..c5fcd2d3104 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -9,6 +9,15 @@ class AccountMove(models.Model): _inherit = "account.move" + # 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( + string="Invoice Global Discounts (readonly)", + related="global_discount_ids", + readonly=True, + ) global_discount_ids = fields.Many2many( comodel_name="global.discount", column1="invoice_id", diff --git a/account_global_discount/views/account_invoice_views.xml b/account_global_discount/views/account_invoice_views.xml index 16855d89eb5..76773f56018 100644 --- a/account_global_discount/views/account_invoice_views.xml +++ b/account_global_discount/views/account_invoice_views.xml @@ -14,6 +14,14 @@ name="global_discount_ids" widget="many2many_tags" placeholder="Discounts..." + groups="base_global_discount.group_global_discount" + /> + diff --git a/account_global_discount/views/global_discount_views.xml b/account_global_discount/views/global_discount_views.xml index 60c9b59bc2c..12ba9c6f042 100644 --- a/account_global_discount/views/global_discount_views.xml +++ b/account_global_discount/views/global_discount_views.xml @@ -34,6 +34,7 @@ id="menu_account_global_discount" action="base_global_discount.action_global_discount_tree" name="Global Discounts" + groups="base_global_discount.group_global_discount" parent="account.account_management_menu" /> From 1e891b171d1646b6b7c10c4b9aaaa38ac3754a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Tue, 22 Jun 2021 17:35:24 +0200 Subject: [PATCH 19/34] [FIX] account_global_discount: Prevent error when add more taxes in multi-lines from invoices when global discount is empty --- account_global_discount/__manifest__.py | 2 +- account_global_discount/models/account_move.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index bf8c50da913..78632e8fe72 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "13.0.1.1.0", + "version": "13.0.1.1.1", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-backend", diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index c5fcd2d3104..ddb4dce8958 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -105,7 +105,8 @@ def _set_global_discounts_by_tax(self): taxes_keys = {} # Perform a sanity check for discarding cases that will lead to # incorrect data in discounts - for inv_line in self.invoice_line_ids.filtered(lambda l: not l.display_type): + _self = self.filtered("global_discount_ids") + for inv_line in _self.invoice_line_ids.filtered(lambda l: not l.display_type): if not inv_line.tax_ids and ( not config["test_enable"] or self.env.context.get("test_account_global_discount") @@ -127,7 +128,7 @@ def _set_global_discounts_by_tax(self): taxes_keys[tuple(inv_line.tax_ids.ids)] = True self.invoice_global_discount_ids = False invoice_global_discounts = [] - for tax_line in self.line_ids.filtered("tax_line_id"): + for tax_line in _self.line_ids.filtered("tax_line_id"): key = [] to_create = True for key in taxes_keys: @@ -144,7 +145,7 @@ def _set_global_discounts_by_tax(self): base = vals["base_discounted"] # Check all moves with defined taxes to check if there's any discount not # created (tax amount is zero and only one tax is applied) - for line in self.line_ids.filtered("tax_ids"): + for line in _self.line_ids.filtered("tax_ids"): key = tuple(line.tax_ids.ids) if taxes_keys.get(key): base = line.price_subtotal From 9c288a3674e0b9e8f12040bd7fc64994522e7f64 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 6 Jul 2021 17:23:04 +0200 Subject: [PATCH 20/34] [FIX+REF] account_global_discount: Include taxes in global discount move lines Previously, no taxes were populated as base taxes for global discount move lines, which means that the tax reports were incorrect. The global discount lines + move lines has been injected other way for avoiding inconsistencies, and the rest of the features have been adapted according this. A migration script is provided as well for filling taxes in existing global discount move lines. [UPD] Update account_global_discount.pot [UPD] README.rst Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: account-invoicing-13.0/account-invoicing-13.0-account_global_discount Translate-URL: https://translation.odoo-community.org/projects/account-invoicing-13-0/account-invoicing-13-0-account_global_discount/ --- account_global_discount/README.rst | 1 + account_global_discount/__manifest__.py | 4 +- .../i18n/account_global_discount.pot | 14 +- account_global_discount/i18n/es.po | 27 ++- account_global_discount/i18n/nl_NL.po | 27 ++- account_global_discount/i18n/pt.po | 27 ++- .../migrations/13.0.2.0.0/post-migration.py | 46 +++++ .../models/account_move.py | 193 ++++++++++-------- account_global_discount/readme/ROADMAP.rst | 1 + .../static/description/index.html | 1 + .../tests/test_global_discount.py | 106 +++++++--- .../views/account_invoice_views.xml | 16 +- 12 files changed, 298 insertions(+), 165 deletions(-) create mode 100644 account_global_discount/migrations/13.0.2.0.0/post-migration.py diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst index b7b1ac9a5fd..6748399f266 100644 --- a/account_global_discount/README.rst +++ b/account_global_discount/README.rst @@ -76,6 +76,7 @@ Known issues / Roadmap the generated journal items won't be correct for taxes declarations. An error is raised in that cases. * Currently, taxes in invoice lines are mandatory with global discounts. +* No tax tags are populated for the global discount move lines, only `tax_ids`. Bug Tracker =========== diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 78632e8fe72..fe1127baac8 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -1,9 +1,9 @@ # Copyright 2019 Tecnativa S.L. - David Vidal -# Copyright 2020 Tecnativa - Pedro M. Baeza +# Copyright 2020-2021 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "13.0.1.1.1", + "version": "13.0.2.0.0", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-backend", diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot index 4bfc309c15f..57b13837ac3 100644 --- a/account_global_discount/i18n/account_global_discount.pot +++ b/account_global_discount/i18n/account_global_discount.pot @@ -43,9 +43,14 @@ msgstr "" msgid "Analytic account" msgstr "" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted +msgid "Base after discount" +msgstr "" + #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base -msgid "Base discounted" +msgid "Base before discount" msgstr "" #. module: account_global_discount @@ -88,11 +93,6 @@ msgstr "" msgid "Discounted Amount" msgstr "" -#. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted -msgid "Discounted amount" -msgstr "" - #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Discounts..." @@ -181,7 +181,7 @@ msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids -msgid "Tax" +msgid "Taxes" msgstr "" #. module: account_global_discount diff --git a/account_global_discount/i18n/es.po b/account_global_discount/i18n/es.po index 9fd062a5c3d..5281ff89d13 100644 --- a/account_global_discount/i18n/es.po +++ b/account_global_discount/i18n/es.po @@ -49,10 +49,15 @@ msgstr "Base Imponible sin Descuentos" msgid "Analytic account" msgstr "Cuenta analítica" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted +msgid "Base after discount" +msgstr "" + #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base -msgid "Base discounted" -msgstr "Base descontada" +msgid "Base before discount" +msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id @@ -94,11 +99,6 @@ msgstr "Nombre del descuento" msgid "Discounted Amount" msgstr "Importe Descontado" -#. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted -msgid "Discounted amount" -msgstr "Importe Descontado" - #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Discounts..." @@ -187,8 +187,8 @@ msgstr "Última actualización en" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids -msgid "Tax" -msgstr "Impuesto" +msgid "Taxes" +msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount @@ -206,6 +206,15 @@ msgstr "Base Imponible Antes Desc." msgid "With global discounts, taxes in lines are required." msgstr "Con descuentos globales, se requieren impuestos en las líneas." +#~ msgid "Base discounted" +#~ msgstr "Base descontada" + +#~ msgid "Discounted amount" +#~ msgstr "Importe Descontado" + +#~ msgid "Tax" +#~ msgstr "Impuesto" + #~ msgid "Global Discounts
    " #~ msgstr "Descuento Global" diff --git a/account_global_discount/i18n/nl_NL.po b/account_global_discount/i18n/nl_NL.po index 5f955fc9431..cff6bdcb664 100644 --- a/account_global_discount/i18n/nl_NL.po +++ b/account_global_discount/i18n/nl_NL.po @@ -48,10 +48,15 @@ msgstr "Onbelast bedrag voor kortingen" msgid "Analytic account" msgstr "Kostenplaats" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted +msgid "Base after discount" +msgstr "" + #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base -msgid "Base discounted" -msgstr "Basis korting" +msgid "Base before discount" +msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id @@ -93,11 +98,6 @@ msgstr "Kortingsnaam" msgid "Discounted Amount" msgstr "Kortingsbedrag" -#. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted -msgid "Discounted amount" -msgstr "Kortingsbedrag" - #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Discounts..." @@ -186,8 +186,8 @@ msgstr "Laatst bijgewerkt op" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids -msgid "Tax" -msgstr "Belasting" +msgid "Taxes" +msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount @@ -205,5 +205,14 @@ msgstr "Onbelast bedrag voor kort." msgid "With global discounts, taxes in lines are required." msgstr "Met globale kortingen zijn belastingen in regels vereist." +#~ msgid "Base discounted" +#~ msgstr "Basis korting" + +#~ msgid "Discounted amount" +#~ msgstr "Kortingsbedrag" + +#~ msgid "Tax" +#~ msgstr "Belasting" + #~ msgid "Company related to this journal" #~ msgstr "Bedrijf gerelateerd aan journaal" diff --git a/account_global_discount/i18n/pt.po b/account_global_discount/i18n/pt.po index 44e1664109b..57252865240 100644 --- a/account_global_discount/i18n/pt.po +++ b/account_global_discount/i18n/pt.po @@ -46,10 +46,15 @@ msgstr "Montante sem Impostos antes de Descontos" msgid "Analytic account" msgstr "Conta analítica" +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted +msgid "Base after discount" +msgstr "" + #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base -msgid "Base discounted" -msgstr "Base com desconto" +msgid "Base before discount" +msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id @@ -91,11 +96,6 @@ msgstr "Nome do Desconto" msgid "Discounted Amount" msgstr "Valor Descontado" -#. module: account_global_discount -#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted -msgid "Discounted amount" -msgstr "Montante descontado" - #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Discounts..." @@ -184,8 +184,8 @@ msgstr "Atualizado pela última vez em" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids -msgid "Tax" -msgstr "Imposto" +msgid "Taxes" +msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount @@ -203,6 +203,15 @@ msgstr "Montante sem Impostos antes de Desc." msgid "With global discounts, taxes in lines are required." msgstr "Com descontos globais, são requeridos impostos nas linhas." +#~ msgid "Base discounted" +#~ msgstr "Base com desconto" + +#~ msgid "Discounted amount" +#~ msgstr "Montante descontado" + +#~ msgid "Tax" +#~ msgstr "Imposto" + #~ msgid "Global Discounts
    " #~ msgstr "Descontos Globais
    " diff --git a/account_global_discount/migrations/13.0.2.0.0/post-migration.py b/account_global_discount/migrations/13.0.2.0.0/post-migration.py new file mode 100644 index 00000000000..5a6efea713a --- /dev/null +++ b/account_global_discount/migrations/13.0.2.0.0/post-migration.py @@ -0,0 +1,46 @@ +# Copyright 2021 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from openupgradelib import openupgrade + +from odoo.tools import float_compare + + +@openupgrade.migrate() +def migrate(env, version): + """Fill existing global discount entries with the source global discount + and missing tax links. + """ + lines = env["account.move.line"].search([("global_discount_item", "=", True)]) + for line in lines: + discount = line.move_id.invoice_global_discount_ids.filtered( + lambda x: x.account_id == line.account_id + and x.account_analytic_id == line.analytic_account_id + and ( + not float_compare( + x.discount_amount, + line.debit, + precision_rounding=line.move_id.currency_id.rounding, + ) + or not float_compare( + x.discount_amount, + -line.credit, + precision_rounding=line.move_id.currency_id.rounding, + ) + ) + and x not in line.move_id.line_ids.mapped("invoice_global_discount_id") + )[:1] + if discount: + for tax in line.tax_ids - discount.tax_ids: + env.cr.execute( + "DELETE FROM account_move_line_account_tax_rel " + "WHERE account_move_line_id = %s AND account_tax_id = %s ", + (line.id, tax.id), + ) + for tax in discount.tax_ids - line.tax_ids: + env.cr.execute( + "INSERT INTO account_move_line_account_tax_rel " + "(account_move_line_id, account_tax_id) VALUES (%s, %s)", + (line.id, tax.id), + ) + # Fill it for existing lines, although this is not working with new ones + line.invoice_global_discount_id = discount.id diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index ddb4dce8958..5a072687084 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -1,5 +1,5 @@ # Copyright 2019 Tecnativa - David Vidal -# Copyright 2020 Tecnativa - Pedro M. Baeza +# Copyright 2020-2021 Tecnativa - Pedro M. Baeza # Copyright 2021 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import _, api, exceptions, fields, models @@ -52,17 +52,29 @@ class AccountMove(models.Model): ) def _recompute_tax_lines(self, recompute_tax_base_amount=False): - super()._recompute_tax_lines(recompute_tax_base_amount) - # If recompute_tax_base_amount is True, only the tax_base_amount - # field is recalculated, therefore the debit and debit fields - # will not be recalculated and it doesn't make sense to apply - # the global discount to the taxes move lines by calling the - # _update_tax_lines_for_global_discount method. + """Inject the global discounts recomputation if recompute_tax_base_amount is + false, as on contrary, only the tax_base_amount field is recalculated, not + affecting global discount computation. + """ + # Remove first existing previous global discount move lines for not altering + # tax computation + if not recompute_tax_base_amount: + # TODO: To be changed to invoice_global_discount_id when properly filled + self.line_ids -= self.line_ids.filtered("global_discount_item") + res = super()._recompute_tax_lines(recompute_tax_base_amount) if not recompute_tax_base_amount: self._update_tax_lines_for_global_discount() + self._set_global_discounts_by_tax() + self._recompute_global_discount_lines() + return res def _update_tax_lines_for_global_discount(self): - """ Update tax_base_amount and taxes debits.""" + """Update tax_base_amount and taxes debits on tax move lines using global + discounts. + + We are altering the recently recreated tax move lines got calling super on + ``_recompute_tax_lines``. + """ round_curr = self.currency_id.round tax_lines = self.line_ids.filtered( lambda r: r.tax_line_id.amount_type in ("percent", "division") @@ -85,6 +97,7 @@ def _prepare_global_discount_vals(self, global_discount, base, tax_ids): """Prepare the dictionary values for an invoice global discount line. """ + self.ensure_one() discount = global_discount._get_global_discount_vals(base) return { "name": global_discount.display_name, @@ -100,34 +113,27 @@ def _prepare_global_discount_vals(self, global_discount, base, tax_ids): def _set_global_discounts_by_tax(self): """Create invoice global discount lines by taxes combinations and discounts. + + This also resets previous global discounts in case they existed. """ self.ensure_one() + if not self.is_invoice(): + return + in_draft_mode = self != self._origin taxes_keys = {} # Perform a sanity check for discarding cases that will lead to # incorrect data in discounts _self = self.filtered("global_discount_ids") for inv_line in _self.invoice_line_ids.filtered(lambda l: not l.display_type): - if not inv_line.tax_ids and ( - not config["test_enable"] - or self.env.context.get("test_account_global_discount") - ): - raise exceptions.UserError( - _("With global discounts, taxes in lines are required.") - ) for key in taxes_keys: if key == tuple(inv_line.tax_ids.ids): break - elif set(key) & set(inv_line.tax_ids.ids) and ( - not config["test_enable"] - or self.env.context.get("test_account_global_discount") - ): - raise exceptions.UserError( - _("Incompatible taxes found for global discounts.") - ) else: taxes_keys[tuple(inv_line.tax_ids.ids)] = True - self.invoice_global_discount_ids = False - invoice_global_discounts = [] + # Reset previous global discounts + self.invoice_global_discount_ids -= self.invoice_global_discount_ids + model = "account.invoice.global.discount" + create_method = in_draft_mode and self.env[model].new or self.env[model].create for tax_line in _self.line_ids.filtered("tax_line_id"): key = [] to_create = True @@ -141,7 +147,7 @@ def _set_global_discounts_by_tax(self): base = tax_line.base_before_global_discounts or tax_line.tax_base_amount for global_discount in self.global_discount_ids: vals = self._prepare_global_discount_vals(global_discount, base, key) - invoice_global_discounts.append((0, 0, vals)) + create_method(vals) base = vals["base_discounted"] # Check all moves with defined taxes to check if there's any discount not # created (tax amount is zero and only one tax is applied) @@ -153,78 +159,69 @@ def _set_global_discounts_by_tax(self): vals = self._prepare_global_discount_vals( global_discount, base, key ) - invoice_global_discounts.append((0, 0, vals)) + create_method(vals) base = vals["base_discounted"] - self.invoice_global_discount_ids = invoice_global_discounts - def _create_global_discount_journal_items(self): - """Append global discounts move lines""" - lines_to_delete = self.line_ids.filtered("global_discount_item") - if self != self._origin: - self.line_ids -= lines_to_delete - else: - lines_to_delete.with_context(check_move_validity=False).unlink() - vals_list = [] + def _recompute_global_discount_lines(self): + """Append global discounts move lines. + + This is called when recomputing dynamic lines before calling + `_recompute_payment_terms_lines`, but after calling `_recompute_tax_lines`. + """ + self.ensure_one() + in_draft_mode = self != self._origin + model = "account.move.line" + create_method = in_draft_mode and self.env[model].new or self.env[model].create for discount in self.invoice_global_discount_ids.filtered("discount"): - disc_amount = discount.discount_amount - vals_list.append( - ( - 0, - 0, - { - "global_discount_item": True, - "name": "%s - %s" - % (discount.name, ", ".join(discount.tax_ids.mapped("name"))), - "debit": disc_amount > 0.0 and disc_amount or 0.0, - "credit": disc_amount < 0.0 and -disc_amount or 0.0, - "account_id": discount.account_id.id, - "analytic_account_id": discount.account_analytic_id.id, - "exclude_from_invoice_tab": True, - }, - ) + sign = -1 if self.type in {"in_invoice", "out_refund"} else 1 + disc_amount = sign * discount.discount_amount + create_method( + { + "global_discount_item": True, + # TODO: This field is not properly saved, probably due to ORM glitch + "invoice_global_discount_id": discount.id, + "move_id": self.id, + "name": "%s - %s" + % (discount.name, ", ".join(discount.tax_ids.mapped("name"))), + "debit": disc_amount > 0.0 and disc_amount or 0.0, + "credit": disc_amount < 0.0 and -disc_amount or 0.0, + "account_id": discount.account_id.id, + "analytic_account_id": discount.account_analytic_id.id, + "exclude_from_invoice_tab": True, + "tax_ids": [(4, x.id) for x in discount.tax_ids], + } ) - self.line_ids = vals_list - self._onchange_recompute_dynamic_lines() - - def _set_global_discounts(self): - """Get global discounts in order and apply them in chain. They will be - fetched in their sequence order""" - for inv in self: - inv._set_global_discounts_by_tax() - inv._create_global_discount_journal_items() - - @api.onchange("invoice_line_ids") - def _onchange_invoice_line_ids(self): - others_lines = self.line_ids.filtered( - lambda line: line.exclude_from_invoice_tab - ) - if others_lines: - others_lines[0].recompute_tax_line = True - res = super()._onchange_invoice_line_ids() - self._set_global_discounts() - return res @api.onchange("partner_id", "company_id") def _onchange_partner_id(self): res = super()._onchange_partner_id() + discounts = False if ( self.type in ["out_invoice", "out_refund"] and self.partner_id.customer_global_discount_ids ): - self.global_discount_ids = self.partner_id.customer_global_discount_ids + discounts = self.partner_id.customer_global_discount_ids elif ( self.type in ["in_refund", "in_invoice"] and self.partner_id.supplier_global_discount_ids ): - self.global_discount_ids = self.partner_id.supplier_global_discount_ids + discounts = self.partner_id.supplier_global_discount_ids + if discounts: + self.global_discount_ids = discounts + # We need to manually launch the onchange, as the recursivity is explicitly + # disabled in this model: + # https://github.com/odoo/odoo/blob/a8d3f466dfffca08214acecf08ec298e3ace6272 + # /addons/account/models/account_move.py#L1021-L1025 + self._onchange_global_discount_ids() return res @api.onchange("global_discount_ids") def _onchange_global_discount_ids(self): - """Trigger global discount lines to recompute all""" - return self._onchange_invoice_line_ids() + """Trigger move lines recomputation.""" + return self._recompute_dynamic_lines(recompute_all_taxes=True) def _compute_amount_one(self): + """Perform totals computation of a move with global discounts.""" if not self.invoice_global_discount_ids: self.amount_global_discount = 0.0 self.amount_untaxed_before_global_discounts = 0.0 @@ -268,6 +265,7 @@ def _compute_amount_one(self): "global_discount_ids", ) def _compute_amount(self): + """Modify totals computation for including global discounts.""" super()._compute_amount() for record in self: record._compute_amount_one() @@ -276,12 +274,38 @@ def _compute_amount(self): def create(self, vals_list): """If we create the invoice with the discounts already set like from a sales order, we must compute the global discounts as well""" + # TODO: Still needed for sale_global_discount with latest refactoring? moves = super().create(vals_list) move_with_global_discounts = moves.filtered("global_discount_ids") for move in move_with_global_discounts: move.with_context(check_move_validity=False)._onchange_invoice_line_ids() return moves + def _check_balanced(self): + """Add the check of proper taxes for global discounts.""" + super()._check_balanced() + test_condition = not config["test_enable"] or self.env.context.get( + "test_account_global_discount" + ) + for move in self.filtered(lambda x: x.is_invoice() and x.global_discount_ids): + taxes_keys = {} + for inv_line in move.invoice_line_ids.filtered( + lambda l: not l.display_type + ): + if not inv_line.tax_ids and test_condition: + raise exceptions.UserError( + _("With global discounts, taxes in lines are required.") + ) + for key in taxes_keys: + if key == tuple(inv_line.tax_ids.ids): + break + elif set(key) & set(inv_line.tax_ids.ids) and test_condition: + raise exceptions.UserError( + _("Incompatible taxes found for global discounts.") + ) + else: + taxes_keys[tuple(inv_line.tax_ids.ids)] = True + class AccountMoveLine(models.Model): _inherit = "account.move.line" @@ -293,6 +317,8 @@ class AccountMoveLine(models.Model): base_before_global_discounts = fields.Monetary( string="Amount Untaxed Before Discounts", readonly=True, ) + # TODO: To be removed on v14 if invoice_global_discount_id is properly filled + # Provided for compatibility in stable branch global_discount_item = fields.Boolean() @@ -312,27 +338,22 @@ class AccountInvoiceGlobalDiscount(models.Model): ], ) global_discount_id = fields.Many2one( - comodel_name="global.discount", string="Global Discount", readonly=True, + comodel_name="global.discount", string="Global Discount", ) - discount = fields.Float(string="Discount (number)", readonly=True) + discount = fields.Float(string="Discount (number)") discount_display = fields.Char( - compute="_compute_discount_display", readonly=True, string="Discount", - ) - base = fields.Float( - string="Base discounted", digits="Product Price", readonly=True, - ) - base_discounted = fields.Float( - string="Discounted amount", digits="Product Price", readonly=True, + compute="_compute_discount_display", string="Discount", ) + base = fields.Float(string="Base before discount", digits="Product Price") + base_discounted = fields.Float(string="Base after discount", digits="Product Price") currency_id = fields.Many2one(related="invoice_id.currency_id", readonly=True) discount_amount = fields.Monetary( string="Discounted Amount", compute="_compute_discount_amount", currency_field="currency_id", - readonly=True, compute_sudo=True, ) - tax_ids = fields.Many2many(comodel_name="account.tax") + tax_ids = fields.Many2many(comodel_name="account.tax", string="Taxes") account_id = fields.Many2one( comodel_name="account.account", required=True, diff --git a/account_global_discount/readme/ROADMAP.rst b/account_global_discount/readme/ROADMAP.rst index 68997140579..cb97fb12adc 100644 --- a/account_global_discount/readme/ROADMAP.rst +++ b/account_global_discount/readme/ROADMAP.rst @@ -2,3 +2,4 @@ the generated journal items won't be correct for taxes declarations. An error is raised in that cases. * Currently, taxes in invoice lines are mandatory with global discounts. +* No tax tags are populated for the global discount move lines, only `tax_ids`. diff --git a/account_global_discount/static/description/index.html b/account_global_discount/static/description/index.html index a91a4013898..3643ec9f691 100644 --- a/account_global_discount/static/description/index.html +++ b/account_global_discount/static/description/index.html @@ -428,6 +428,7 @@

    Known issues / Roadmap

    the generated journal items won’t be correct for taxes declarations. An error is raised in that cases.
  • Currently, taxes in invoice lines are mandatory with global discounts.
  • +
  • No tax tags are populated for the global discount move lines, only tax_ids.
  • diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index b3818073bb4..9e6516ba72c 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -50,6 +50,7 @@ def setUpClass(cls): "discount_scope": "sale", "discount": 20, "account_id": cls.account.id, + "sequence": 3, } ) cls.global_discount_2 = cls.global_discount_obj.create( @@ -58,6 +59,7 @@ def setUpClass(cls): "discount_scope": "purchase", "discount": 30, "account_id": cls.account.id, + "sequence": 2, } ) cls.global_discount_3 = cls.global_discount_obj.create( @@ -66,6 +68,7 @@ def setUpClass(cls): "discount_scope": "purchase", "discount": 50, "account_id": cls.account.id, + "sequence": 1, } ) cls.partner_1 = cls.env["res.partner"].create( @@ -109,6 +112,7 @@ def setUpClass(cls): ) ) invoice_form.partner_id = cls.partner_1 + invoice_form.ref = "Test global discount" with invoice_form.invoice_line_ids.new() as line_form: line_form.name = "Line 1" line_form.price_unit = 200.0 @@ -119,21 +123,22 @@ def setUpClass(cls): def test_01_global_invoice_succesive_discounts(self): """Add global discounts to the invoice""" - invoice_no_check = self.invoice.with_context(check_move_validity=False) invoice_tax_line = self.invoice.line_ids.filtered("tax_line_id") self.assertAlmostEqual(self.invoice.amount_total, 230) self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 200.0) self.assertAlmostEqual(invoice_tax_line.balance, 30.0) # Global discounts are applied to the base and taxes are recomputed: # 200 - 50% (global disc. 1) = 100 - invoice_no_check.global_discount_ids = [(6, 0, self.global_discount_3.ids)] - invoice_no_check._onchange_global_discount_ids() + with Form(self.invoice) as invoice_form: + invoice_form.global_discount_ids.clear() + invoice_form.global_discount_ids.add(self.global_discount_3) self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) precision = self.env["decimal.precision"].precision_get("Discount") self.assertEqual( self.invoice.invoice_global_discount_ids.discount_display, "-50.{}%".format("0" * precision), ) + invoice_tax_line = self.invoice.line_ids.filtered("tax_line_id") self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 100.0) self.assertAlmostEqual(invoice_tax_line.balance, 15.0) self.assertAlmostEqual(self.invoice.amount_untaxed, 100.0) @@ -143,9 +148,10 @@ def test_01_global_invoice_succesive_discounts(self): # 200 - 50% (global disc. 1) = 100 # 100 - 30% (global disc. 2) = 70 # The global discounts amount is then 200 - 70 = 130 - self.invoice.global_discount_ids = [(4, self.global_discount_2.id)] - invoice_no_check._onchange_global_discount_ids() + with Form(self.invoice) as invoice_form: + invoice_form.global_discount_ids.add(self.global_discount_2) self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) + invoice_tax_line = self.invoice.line_ids.filtered("tax_line_id") self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 70.0) self.assertAlmostEqual(invoice_tax_line.balance, 10.5) self.assertAlmostEqual(self.invoice.amount_untaxed, 70.0) @@ -156,9 +162,11 @@ def test_01_global_invoice_succesive_discounts(self): # 160 - 50% (global disc. 1) = 80 # 80 - 30% (global disc. 2) = 56 # The global discounts amount is then 160 - 56 = 104 - invoice_no_check.invoice_line_ids.discount = 20 - invoice_no_check._onchange_invoice_line_ids() + with Form(self.invoice) as invoice_form: + with invoice_form.invoice_line_ids.edit(0) as line_form: + line_form.discount = 20 self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) + invoice_tax_line = self.invoice.line_ids.filtered("tax_line_id") self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 56.0) self.assertAlmostEqual(invoice_tax_line.balance, 8.4) self.assertAlmostEqual(self.invoice.amount_untaxed, 56.0) @@ -167,7 +175,6 @@ def test_01_global_invoice_succesive_discounts(self): def test_02_global_invoice_discounts_from_partner(self): """Change the partner and his global discounts go to the invoice""" - invoice_no_check = self.invoice.with_context(check_move_validity=False) invoice_tax_line = self.invoice.line_ids.filtered("tax_line_id") self.assertAlmostEqual(self.invoice.amount_total, 230) self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 200.0) @@ -175,10 +182,8 @@ def test_02_global_invoice_discounts_from_partner(self): # When we change the parter, his global discounts are fetched depending # on the type of the invoice. In this case, we fetch the supplier # global discounts - self.invoice.partner_id = self.partner_2 - # trigger onchanges mimicking UI - invoice_no_check._onchange_partner_id() - invoice_no_check._onchange_global_discount_ids() + with Form(self.invoice) as invoice_form: + invoice_form.partner_id = self.partner_2 self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 140.0) self.assertAlmostEqual(invoice_tax_line.balance, 21.0) self.assertAlmostEqual(self.invoice.amount_untaxed, 140.0) @@ -186,7 +191,6 @@ def test_02_global_invoice_discounts_from_partner(self): self.assertAlmostEqual(self.invoice.amount_global_discount, -60.0) def test_03_multiple_taxes_multi_line(self): - invoice_no_check = self.invoice.with_context(check_move_validity=False) tax2 = self.env["account.tax"].create( { "name": "TAX 20% 2", @@ -203,7 +207,6 @@ def test_03_multiple_taxes_multi_line(self): line_form.quantity = 1 line_form.tax_ids.clear() line_form.tax_ids.add(tax2) - invoice_no_check._onchange_global_discount_ids() self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) discount_tax_15 = self.invoice.invoice_global_discount_ids.filtered( lambda x: x.tax_ids == self.tax @@ -224,13 +227,16 @@ def test_03_multiple_taxes_multi_line(self): self.assertAlmostEqual(self.invoice.amount_untaxed, 240.0) self.assertAlmostEqual(self.invoice.amount_total, 280) self.assertAlmostEqual(self.invoice.amount_global_discount, -60.0) - # Validate invoice for seeing result - self.assertEqual(len(self.invoice.line_ids), 7) - self.invoice.post() - self.assertEqual(len(self.invoice.line_ids), 7) + # Check journal items validity + lines = self.invoice.line_ids + line_15 = lines.filtered( + lambda x: x.global_discount_item and x.tax_ids == self.tax + ) + self.assertAlmostEqual(line_15.credit, 40) + line_20 = lines.filtered(lambda x: x.global_discount_item and x.tax_ids == tax2) + self.assertAlmostEqual(line_20.credit, 20) def test_04_multiple_taxes_same_line(self): - invoice_no_check = self.invoice.with_context(check_move_validity=False) tax2 = self.env["account.tax"].create( { "name": "Retention 20%", @@ -243,7 +249,6 @@ def test_04_multiple_taxes_same_line(self): invoice_form.global_discount_ids.add(self.global_discount_1) with invoice_form.invoice_line_ids.edit(0) as line_form: line_form.tax_ids.add(tax2) - invoice_no_check._onchange_global_discount_ids() # Global discounts are applied to the base and taxes are recomputed: # 300 - 20% (global disc. 1) = 240 self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) @@ -264,10 +269,6 @@ def test_04_multiple_taxes_same_line(self): self.assertAlmostEqual(self.invoice.amount_untaxed, 160.0) self.assertAlmostEqual(self.invoice.amount_total, 152) self.assertAlmostEqual(self.invoice.amount_global_discount, -40.0) - # Validate invoice for seeing result - self.assertEqual(len(self.invoice.line_ids), 5) - self.invoice.post() - self.assertEqual(len(self.invoice.line_ids), 5) def test_05_incompatible_taxes(self): # Line 1 with tax and tax2 @@ -304,15 +305,11 @@ def test_07_line_with_tax_0(self): with invoice_form.invoice_line_ids.edit(0) as line_form: line_form.tax_ids.clear() line_form.tax_ids.add(self.tax_0) - self.invoice._onchange_global_discount_ids() - self.assertEqual(len(self.invoice.invoice_global_discount_ids), 1) - discount_tax_0 = self.invoice.invoice_global_discount_ids.filtered( - lambda x: x.tax_ids == self.tax_0 - ) - self.assertAlmostEqual(discount_tax_0.discount_amount, 40) + discounts = self.invoice.invoice_global_discount_ids + self.assertEqual(len(discounts), 1) + self.assertAlmostEqual(discounts.discount_amount, 40) - def test_07_line2_with_tax_0(self): - invoice_no_check = self.invoice.with_context(check_move_validity=False) + def test_08_line2_with_tax_0(self): with Form(self.invoice) as invoice_form: invoice_form.global_discount_ids.add(self.global_discount_1) with invoice_form.invoice_line_ids.new() as line_form: @@ -321,7 +318,6 @@ def test_07_line2_with_tax_0(self): line_form.quantity = 1 line_form.tax_ids.clear() line_form.tax_ids.add(self.tax_0) - invoice_no_check._onchange_global_discount_ids() self.assertEqual(len(self.invoice.invoice_global_discount_ids), 2) discount_tax_15 = self.invoice.invoice_global_discount_ids.filtered( lambda x: x.tax_ids == self.tax @@ -331,3 +327,47 @@ def test_07_line2_with_tax_0(self): lambda x: x.tax_ids == self.tax_0 ) self.assertAlmostEqual(discount_tax_0.discount_amount, 20) + + def test_09_customer_invoice(self): + global_discount = self.global_discount_obj.create( + { + "name": "Test Discount Sales", + "discount_scope": "sale", + "discount": 50, + "account_id": self.account.id, + "sequence": 1, + } + ) + tax = self.env["account.tax"].create( + { + "name": "TAX 15% Sales", + "amount_type": "percent", + "type_tax_use": "sale", + "amount": 15.0, + } + ) + invoice_form = Form( + self.env["account.move"].with_context( + default_type="out_invoice", test_account_global_discount=True, + ) + ) + invoice_form.partner_id = self.partner_1 + invoice_form.global_discount_ids.add(global_discount) + with invoice_form.invoice_line_ids.new() as line_form: + line_form.name = "Line 1" + line_form.price_unit = 200.0 + line_form.quantity = 1 + line_form.tax_ids.clear() + line_form.tax_ids.add(tax) + invoice = invoice_form.save() + self.assertEqual(len(invoice.invoice_global_discount_ids), 1) + invoice_tax_line = invoice.line_ids.filtered("tax_line_id") + self.assertAlmostEqual(invoice_tax_line.tax_base_amount, 100.0) + self.assertAlmostEqual(invoice_tax_line.balance, -15.0) + self.assertAlmostEqual(invoice.amount_untaxed, 100.0) + self.assertAlmostEqual(invoice.amount_total, 115.0) + self.assertAlmostEqual(invoice.amount_global_discount, -100.0) + # Check journal item validity + lines = invoice.line_ids + line_15 = lines.filtered(lambda x: x.global_discount_item and x.tax_ids == tax) + self.assertAlmostEqual(line_15.debit, 100) diff --git a/account_global_discount/views/account_invoice_views.xml b/account_global_discount/views/account_invoice_views.xml index 76773f56018..c3dc6c1afe9 100644 --- a/account_global_discount/views/account_invoice_views.xml +++ b/account_global_discount/views/account_invoice_views.xml @@ -6,7 +6,9 @@ account.move + + @@ -48,23 +50,19 @@ - - + + @@ -72,16 +70,14 @@ name="base_discounted" widget="monetary" options="{'currency_field': 'currency_id'}" - force_save="1" /> - + From b0280827a7c0f6d52d1f6c3c50d349ce9abab797 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Mon, 19 Jul 2021 11:15:39 +0200 Subject: [PATCH 21/34] [FIX] account_global_discount: Call the proper method on create For recomputing the global discounts, now we need to call another method. TT30077 --- account_global_discount/__manifest__.py | 2 +- account_global_discount/models/account_move.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index fe1127baac8..12adfa9c0db 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "13.0.2.0.0", + "version": "13.0.2.0.1", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-backend", diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index 5a072687084..a7332797b97 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -273,12 +273,13 @@ def _compute_amount(self): @api.model_create_multi def create(self, vals_list): """If we create the invoice with the discounts already set like from - a sales order, we must compute the global discounts as well""" - # TODO: Still needed for sale_global_discount with latest refactoring? + a sales order, we must compute the global discounts as well, as some data + like ``tax_ids`` is not set until the final step. + """ moves = super().create(vals_list) move_with_global_discounts = moves.filtered("global_discount_ids") for move in move_with_global_discounts: - move.with_context(check_move_validity=False)._onchange_invoice_line_ids() + move.with_context(check_move_validity=False)._onchange_global_discount_ids() return moves def _check_balanced(self): From ccfaeb70e55dd39235d65804ef2422d61f6f3ae6 Mon Sep 17 00:00:00 2001 From: "Omar (Comunitea)" Date: Mon, 23 Aug 2021 14:13:54 +0200 Subject: [PATCH 22/34] [MIG] account_global_discount: Migration to 14.0 - Signed amount error in supplier invoices - computed fields must be stored because the other standard computed fields in same method are - Filtering global discount in on_change by invoice's company - Partner in global discount move line - When writing Odoo doesn't autocomplete amount_currency and it could fail with constraint --- account_global_discount/README.rst | 11 ++-- account_global_discount/__manifest__.py | 4 +- .../i18n/account_global_discount.pot | 31 ++++++++++- .../migrations/13.0.1.0.0/post-migration.py | 18 ------- .../migrations/13.0.1.0.0/pre-migration.py | 40 -------------- .../migrations/13.0.2.0.0/post-migration.py | 46 ---------------- .../models/account_move.py | 52 ++++++++++++------- .../models/global_discount.py | 3 +- .../readme/CONTRIBUTORS.rst | 1 + .../static/description/index.html | 7 +-- .../tests/test_global_discount.py | 6 ++- 11 files changed, 81 insertions(+), 138 deletions(-) delete mode 100644 account_global_discount/migrations/13.0.1.0.0/post-migration.py delete mode 100644 account_global_discount/migrations/13.0.1.0.0/pre-migration.py delete mode 100644 account_global_discount/migrations/13.0.2.0.0/post-migration.py diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst index 6748399f266..fce8f94956c 100644 --- a/account_global_discount/README.rst +++ b/account_global_discount/README.rst @@ -14,13 +14,13 @@ Account 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%2Faccount--invoicing-lightgray.png?logo=github - :target: https://github.com/OCA/account-invoicing/tree/13.0/account_global_discount + :target: https://github.com/OCA/account-invoicing/tree/14.0/account_global_discount :alt: OCA/account-invoicing .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-invoicing-13-0/account-invoicing-13-0-account_global_discount + :target: https://translation.odoo-community.org/projects/account-invoicing-14-0/account-invoicing-14-0-account_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/95/13.0 + :target: https://runbot.odoo-community.org/runbot/95/14.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -84,7 +84,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. @@ -107,6 +107,7 @@ Contributors * Rafael Blasco * Ernesto Tejeda * Víctor Martínez +* Omar Castiñeira Maintainers ~~~~~~~~~~~ @@ -121,6 +122,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/account-invoicing `_ project on GitHub. +This module is part of the `OCA/account-invoicing `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 12adfa9c0db..a6222980bdb 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,10 +3,10 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "13.0.2.0.1", + "version": "14.0.1.0.0", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", - "website": "https://github.com/OCA/server-backend", + "website": "https://github.com/OCA/account-invoicing", "license": "AGPL-3", "depends": ["account", "base_global_discount"], "data": [ diff --git a/account_global_discount/i18n/account_global_discount.pot b/account_global_discount/i18n/account_global_discount.pot index 57b13837ac3..d15d186b28c 100644 --- a/account_global_discount/i18n/account_global_discount.pot +++ b/account_global_discount/i18n/account_global_discount.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 13.0\n" +"Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -32,8 +32,10 @@ msgid "Account" msgstr "" #. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__amount_untaxed_before_global_discounts #: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_untaxed_before_global_discounts #: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__base_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__amount_untaxed_before_global_discounts msgid "Amount Untaxed Before Discounts" msgstr "" @@ -100,6 +102,10 @@ msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__display_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_report__display_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__display_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__display_name +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__display_name msgid "Display Name" msgstr "" @@ -122,6 +128,10 @@ msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_report__id +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__id +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__id msgid "ID" msgstr "" @@ -138,25 +148,36 @@ msgstr "" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_invoice_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__invoice_global_discount_ids #: model:ir.model.fields,field_description:account_global_discount.field_account_move__invoice_global_discount_ids #: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__invoice_global_discount_ids msgid "Invoice Global Discount" msgstr "" #. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__global_discount_ids #: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__global_discount_ids #: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form msgid "Invoice Global Discounts" msgstr "" #. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__global_discount_ids_readonly #: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids_readonly +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__global_discount_ids_readonly msgid "Invoice Global Discounts (readonly)" msgstr "" +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_report +msgid "Invoices Statistics" +msgstr "" + #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move -msgid "Journal Entries" +msgid "Journal Entry" msgstr "" #. module: account_global_discount @@ -166,6 +187,10 @@ msgstr "" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount____last_update +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_report____last_update +#: model:ir.model.fields,field_description:account_global_discount.field_account_move____last_update +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line____last_update +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount____last_update msgid "Last Modified on" msgstr "" @@ -185,7 +210,9 @@ msgid "Taxes" msgstr "" #. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__amount_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__amount_global_discount msgid "Total Global Discounts" msgstr "" diff --git a/account_global_discount/migrations/13.0.1.0.0/post-migration.py b/account_global_discount/migrations/13.0.1.0.0/post-migration.py deleted file mode 100644 index a7a3d4050c9..00000000000 --- a/account_global_discount/migrations/13.0.1.0.0/post-migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2020 Tecnativa - Ernesto Tejeda -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from openupgradelib import openupgrade - - -@openupgrade.migrate() -def migrate(env, version): - # _onchange_invoice_line_ids method is called to recalculate the - # value of the base_before_global_discounts field in - # account.move.line, to update the invoice_global_discount_ids - # table table in account.move and to create the necessary discount - # journal entries. - domain = [ - ("type", "in", env["account.move"].get_invoice_types()), - ("state", "=", "draft"), - ("global_discount_ids", "!=", False), - ] - env["account.move"].search(domain)._onchange_invoice_line_ids() diff --git a/account_global_discount/migrations/13.0.1.0.0/pre-migration.py b/account_global_discount/migrations/13.0.1.0.0/pre-migration.py deleted file mode 100644 index c712376ee6c..00000000000 --- a/account_global_discount/migrations/13.0.1.0.0/pre-migration.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2020 Tecnativa - Ernesto Tejeda -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openupgradelib import openupgrade # pylint: disable=W7936 -from psycopg2 import sql - - -@openupgrade.migrate() -def migrate(env, version): - openupgrade.rename_columns( - env.cr, - { - "account_invoice_global_discount": [("invoice_id", None)], - "account_invoice_global_discount_rel": [("invoice_id", None)], - }, - ) - openupgrade.logged_query( - env.cr, "ALTER TABLE account_invoice_global_discount ADD invoice_id INT4" - ) - openupgrade.logged_query( - env.cr, "ALTER TABLE account_invoice_global_discount_rel ADD invoice_id INT4" - ) - openupgrade.logged_query( - env.cr, - sql.SQL( - """UPDATE account_invoice_global_discount aigd - SET invoice_id = ai.move_id - FROM account_invoice ai - WHERE ai.id = aigd.{}""" - ).format(sql.Identifier(openupgrade.get_legacy_name("invoice_id"))), - ) - openupgrade.logged_query( - env.cr, - sql.SQL( - """UPDATE account_invoice_global_discount_rel aigdr - SET invoice_id = ai.move_id - FROM account_invoice ai - WHERE ai.id = aigdr.{}""" - ).format(sql.Identifier(openupgrade.get_legacy_name("invoice_id"))), - ) diff --git a/account_global_discount/migrations/13.0.2.0.0/post-migration.py b/account_global_discount/migrations/13.0.2.0.0/post-migration.py deleted file mode 100644 index 5a6efea713a..00000000000 --- a/account_global_discount/migrations/13.0.2.0.0/post-migration.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2021 Tecnativa - Pedro M. Baeza -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from openupgradelib import openupgrade - -from odoo.tools import float_compare - - -@openupgrade.migrate() -def migrate(env, version): - """Fill existing global discount entries with the source global discount - and missing tax links. - """ - lines = env["account.move.line"].search([("global_discount_item", "=", True)]) - for line in lines: - discount = line.move_id.invoice_global_discount_ids.filtered( - lambda x: x.account_id == line.account_id - and x.account_analytic_id == line.analytic_account_id - and ( - not float_compare( - x.discount_amount, - line.debit, - precision_rounding=line.move_id.currency_id.rounding, - ) - or not float_compare( - x.discount_amount, - -line.credit, - precision_rounding=line.move_id.currency_id.rounding, - ) - ) - and x not in line.move_id.line_ids.mapped("invoice_global_discount_id") - )[:1] - if discount: - for tax in line.tax_ids - discount.tax_ids: - env.cr.execute( - "DELETE FROM account_move_line_account_tax_rel " - "WHERE account_move_line_id = %s AND account_tax_id = %s ", - (line.id, tax.id), - ) - for tax in discount.tax_ids - line.tax_ids: - env.cr.execute( - "INSERT INTO account_move_line_account_tax_rel " - "(account_move_line_id, account_tax_id) VALUES (%s, %s)", - (line.id, tax.id), - ) - # Fill it for existing lines, although this is not working with new ones - line.invoice_global_discount_id = discount.id diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index a7332797b97..c1578314195 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -28,8 +28,10 @@ class AccountMove(models.Model): " 'out_refund': ['sale'], " " 'in_refund': ['purchase'], " " 'in_invoice': ['purchase']" - "}.get(type, [])), ('account_id', '!=', False), '|', " + "}.get(move_type, [])), ('account_id', '!=', False), '|', " "('company_id', '=', company_id), ('company_id', '=', False)]", + readonly=True, + states={"draft": [("readonly", False)]}, ) amount_global_discount = fields.Monetary( string="Total Global Discounts", @@ -37,6 +39,7 @@ class AccountMove(models.Model): currency_field="currency_id", readonly=True, compute_sudo=True, + store=True, ) amount_untaxed_before_global_discounts = fields.Monetary( string="Amount Untaxed Before Discounts", @@ -44,6 +47,7 @@ class AccountMove(models.Model): currency_field="currency_id", readonly=True, compute_sudo=True, + store=True, ) invoice_global_discount_ids = fields.One2many( comodel_name="account.invoice.global.discount", @@ -90,8 +94,8 @@ def _update_tax_lines_for_global_discount(self): tax_line.debit = amount > 0.0 and amount or 0.0 tax_line.credit = amount < 0.0 and -amount or 0.0 # Apply onchanges - tax_line._onchange_amount_currency() tax_line._onchange_balance() + tax_line._onchange_amount_currency() def _prepare_global_discount_vals(self, global_discount, base, tax_ids): """Prepare the dictionary values for an invoice global discount @@ -173,7 +177,7 @@ def _recompute_global_discount_lines(self): model = "account.move.line" create_method = in_draft_mode and self.env[model].new or self.env[model].create for discount in self.invoice_global_discount_ids.filtered("discount"): - sign = -1 if self.type in {"in_invoice", "out_refund"} else 1 + sign = -1 if self.move_type in {"in_invoice", "out_refund"} else 1 disc_amount = sign * discount.discount_amount create_method( { @@ -185,10 +189,13 @@ def _recompute_global_discount_lines(self): % (discount.name, ", ".join(discount.tax_ids.mapped("name"))), "debit": disc_amount > 0.0 and disc_amount or 0.0, "credit": disc_amount < 0.0 and -disc_amount or 0.0, + "amount_currency": (disc_amount > 0.0 and disc_amount or 0.0) + - (disc_amount < 0.0 and -disc_amount or 0.0), "account_id": discount.account_id.id, "analytic_account_id": discount.account_analytic_id.id, "exclude_from_invoice_tab": True, "tax_ids": [(4, x.id) for x in discount.tax_ids], + "partner_id": self.commercial_partner_id.id, } ) @@ -197,15 +204,19 @@ def _onchange_partner_id(self): res = super()._onchange_partner_id() discounts = False if ( - self.type in ["out_invoice", "out_refund"] + self.move_type in ["out_invoice", "out_refund"] and self.partner_id.customer_global_discount_ids ): - discounts = self.partner_id.customer_global_discount_ids + discounts = self.partner_id.customer_global_discount_ids.filtered( + lambda d: d.company_id == self.company_id + ) elif ( - self.type in ["in_refund", "in_invoice"] + self.move_type in ["in_refund", "in_invoice"] and self.partner_id.supplier_global_discount_ids ): - discounts = self.partner_id.supplier_global_discount_ids + discounts = self.partner_id.supplier_global_discount_ids.filtered( + lambda d: d.company_id == self.company_id + ) if discounts: self.global_discount_ids = discounts # We need to manually launch the onchange, as the recursivity is explicitly @@ -234,7 +245,6 @@ def _compute_amount_one(self): self.amount_untaxed_before_global_discounts = self.amount_untaxed self.amount_untaxed = self.amount_untaxed + self.amount_global_discount self.amount_total = self.amount_untaxed + self.amount_tax - amount_total_company_signed = self.amount_total amount_untaxed_signed = self.amount_untaxed if ( self.currency_id @@ -242,14 +252,10 @@ def _compute_amount_one(self): and self.currency_id != self.company_id.currency_id ): date = self.invoice_date or fields.Date.today() - amount_total_company_signed = self.currency_id._convert( - self.amount_total, self.company_id.currency_id, self.company_id, date - ) amount_untaxed_signed = self.currency_id._convert( self.amount_untaxed, self.company_id.currency_id, self.company_id, date ) - sign = self.type in ["in_refund", "out_refund"] and -1 or 1 - self.amount_total_company_signed = amount_total_company_signed * sign + sign = self.move_type in ["in_invoice", "out_refund"] and -1 or 1 self.amount_total_signed = self.amount_total * sign self.amount_untaxed_signed = amount_untaxed_signed * sign @@ -316,9 +322,10 @@ class AccountMoveLine(models.Model): string="Invoice Global Discount", ) base_before_global_discounts = fields.Monetary( - string="Amount Untaxed Before Discounts", readonly=True, + string="Amount Untaxed Before Discounts", + readonly=True, ) - # TODO: To be removed on v14 if invoice_global_discount_id is properly filled + # TODO: To be removed on future versions if invoice_global_discount_id is properly filled # Provided for compatibility in stable branch global_discount_item = fields.Boolean() @@ -335,15 +342,21 @@ class AccountInvoiceGlobalDiscount(models.Model): index=True, readonly=True, domain=[ - ("type", "in", ["out_invoice", "out_refund", "in_invoice", "in_refund"]) + ( + "move_type", + "in", + ["out_invoice", "out_refund", "in_invoice", "in_refund"], + ) ], ) global_discount_id = fields.Many2one( - comodel_name="global.discount", string="Global Discount", + comodel_name="global.discount", + string="Global Discount", ) discount = fields.Float(string="Discount (number)") discount_display = fields.Char( - compute="_compute_discount_display", string="Discount", + compute="_compute_discount_display", + string="Discount", ) base = fields.Float(string="Base before discount", digits="Product Price") base_discounted = fields.Float(string="Base after discount", digits="Product Price") @@ -362,7 +375,8 @@ class AccountInvoiceGlobalDiscount(models.Model): domain="[('user_type_id.type', 'not in', ['receivable', 'payable'])]", ) account_analytic_id = fields.Many2one( - comodel_name="account.analytic.account", string="Analytic account", + comodel_name="account.analytic.account", + string="Analytic account", ) company_id = fields.Many2one(related="invoice_id.company_id", readonly=True) diff --git a/account_global_discount/models/global_discount.py b/account_global_discount/models/global_discount.py index 8634d5476ac..228ed6401ad 100644 --- a/account_global_discount/models/global_discount.py +++ b/account_global_discount/models/global_discount.py @@ -12,7 +12,8 @@ class GlobalDiscount(models.Model): domain="[('user_type_id.type', 'not in', ['receivable', 'payable'])]", ) account_analytic_id = fields.Many2one( - comodel_name="account.analytic.account", string="Analytic account", + comodel_name="account.analytic.account", + string="Analytic account", ) def _get_global_discount_vals(self, base, account_id=False, **kwargs): diff --git a/account_global_discount/readme/CONTRIBUTORS.rst b/account_global_discount/readme/CONTRIBUTORS.rst index 8a3c8075c37..bf340291ae6 100644 --- a/account_global_discount/readme/CONTRIBUTORS.rst +++ b/account_global_discount/readme/CONTRIBUTORS.rst @@ -6,3 +6,4 @@ * Rafael Blasco * Ernesto Tejeda * Víctor Martínez +* Omar Castiñeira diff --git a/account_global_discount/static/description/index.html b/account_global_discount/static/description/index.html index 3643ec9f691..973641558e0 100644 --- a/account_global_discount/static/description/index.html +++ b/account_global_discount/static/description/index.html @@ -367,7 +367,7 @@

    Account Global Discount

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

    Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runbot

    +

    Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runbot

    Apply global discounts to invoices

    Table of contents

    @@ -436,7 +436,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.

    @@ -459,6 +459,7 @@

    Contributors

  • Víctor Martínez
  • +
  • Omar Castiñeira <omar@comunitea.com>
  • @@ -468,7 +469,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/account-invoicing project on GitHub.

    +

    This module is part of the OCA/account-invoicing project on GitHub.

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

    diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index 9e6516ba72c..83a2a08c4a4 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -108,7 +108,8 @@ def setUpClass(cls): cls.invoice_line = cls.env["account.move.line"] invoice_form = Form( cls.env["account.move"].with_context( - default_type="in_invoice", test_account_global_discount=True, + default_move_type="in_invoice", + test_account_global_discount=True, ) ) invoice_form.partner_id = cls.partner_1 @@ -348,7 +349,8 @@ def test_09_customer_invoice(self): ) invoice_form = Form( self.env["account.move"].with_context( - default_type="out_invoice", test_account_global_discount=True, + default_move_type="out_invoice", + test_account_global_discount=True, ) ) invoice_form.partner_id = self.partner_1 From f31bb83b709731c5250753b8dfcb7083d65d0856 Mon Sep 17 00:00:00 2001 From: "Omar (Comunitea)" Date: Tue, 9 Nov 2021 20:56:26 +0100 Subject: [PATCH 23/34] [IMP] account_global_discount: securization for multi-company --- account_global_discount/models/global_discount.py | 3 +++ .../views/global_discount_views.xml | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/account_global_discount/models/global_discount.py b/account_global_discount/models/global_discount.py index 228ed6401ad..035de6452e2 100644 --- a/account_global_discount/models/global_discount.py +++ b/account_global_discount/models/global_discount.py @@ -5,15 +5,18 @@ class GlobalDiscount(models.Model): _inherit = "global.discount" + _check_company_auto = True account_id = fields.Many2one( comodel_name="account.account", string="Account", domain="[('user_type_id.type', 'not in', ['receivable', 'payable'])]", + check_company=True, ) account_analytic_id = fields.Many2one( comodel_name="account.analytic.account", string="Analytic account", + check_company=True, ) def _get_global_discount_vals(self, base, account_id=False, **kwargs): diff --git a/account_global_discount/views/global_discount_views.xml b/account_global_discount/views/global_discount_views.xml index 12ba9c6f042..b44888894f8 100644 --- a/account_global_discount/views/global_discount_views.xml +++ b/account_global_discount/views/global_discount_views.xml @@ -7,9 +7,13 @@ - + @@ -21,9 +25,13 @@ - + From cee27d8d01ae9bee0d9d00cda022d588f9e3861d Mon Sep 17 00:00:00 2001 From: "Omar (Comunitea)" Date: Tue, 11 Jan 2022 15:03:05 +0100 Subject: [PATCH 24/34] [IMP] account_global_discount: Global discounts into invoice analysis report --- account_global_discount/__init__.py | 1 + account_global_discount/report/__init__.py | 1 + .../report/account_invoice_report.py | 13 +++++++++++++ 3 files changed, 15 insertions(+) create mode 100644 account_global_discount/report/__init__.py create mode 100644 account_global_discount/report/account_invoice_report.py diff --git a/account_global_discount/__init__.py b/account_global_discount/__init__.py index 0650744f6bc..bf588bc8b80 100644 --- a/account_global_discount/__init__.py +++ b/account_global_discount/__init__.py @@ -1 +1,2 @@ from . import models +from . import report diff --git a/account_global_discount/report/__init__.py b/account_global_discount/report/__init__.py new file mode 100644 index 00000000000..52e62702b3c --- /dev/null +++ b/account_global_discount/report/__init__.py @@ -0,0 +1 @@ +from . import account_invoice_report diff --git a/account_global_discount/report/account_invoice_report.py b/account_global_discount/report/account_invoice_report.py new file mode 100644 index 00000000000..4f93904e94b --- /dev/null +++ b/account_global_discount/report/account_invoice_report.py @@ -0,0 +1,13 @@ +from odoo import api, models + + +class AccountInvoiceReport(models.Model): + _inherit = "account.invoice.report" + + @api.model + def _where(self): + where_str = super()._where() + return where_str.replace( + "NOT line.exclude_from_invoice_tab", + "(NOT line.exclude_from_invoice_tab OR global_discount_item = true)", + ) From d77b2710c177bd7230bb12ee9163edfd7550f60f Mon Sep 17 00:00:00 2001 From: Francesco Foresti Date: Wed, 2 Mar 2022 16:04:36 +0000 Subject: [PATCH 25/34] Added translation using Weblate (Italian) Currently translated at 100.0% (36 of 36 strings) Translation: account-invoicing-14.0/account-invoicing-14.0-account_global_discount Translate-URL: https://translation.odoo-community.org/projects/account-invoicing-14-0/account-invoicing-14-0-account_global_discount/it/ --- account_global_discount/i18n/it.po | 233 +++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 account_global_discount/i18n/it.po diff --git a/account_global_discount/i18n/it.po b/account_global_discount/i18n/it.po new file mode 100644 index 00000000000..57f932fe506 --- /dev/null +++ b/account_global_discount/i18n/it.po @@ -0,0 +1,233 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_global_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-03-02 18:31+0000\n" +"Last-Translator: Francesco Foresti \n" +"Language-Team: none\n" +"Language: it\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 4.3.2\n" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "" +"Global Discounts\n" +"
    " +msgstr "" +"Sconti Globali\n" +"
    " + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "Subtotal w/o disc." +msgstr "Subtotale senza sconto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_id +msgid "Account" +msgstr "Conto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_untaxed_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__base_before_global_discounts +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__amount_untaxed_before_global_discounts +msgid "Amount Untaxed Before Discounts" +msgstr "Importo Imponibile Pre-Sconti" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__account_analytic_id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__account_analytic_id +msgid "Analytic account" +msgstr "Conto Analitico" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted +msgid "Base after discount" +msgstr "Base dopo lo sconto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base +msgid "Base before discount" +msgstr "Base prima dello sconto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id +msgid "Company" +msgstr "Azienda" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__currency_id +msgid "Currency" +msgstr "Valuta" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_display +msgid "Discount" +msgstr "Sconto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount +msgid "Discount (number)" +msgstr "Sconto (numero)" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__name +msgid "Discount Name" +msgstr "Nome Sconto" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__discount_amount +msgid "Discounted Amount" +msgstr "Importo Scontato" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +msgid "Discounts..." +msgstr "Sconti..." + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__display_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_report__display_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__display_name +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__display_name +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__display_name +msgid "Display Name" +msgstr "Nome da visualizzare" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__global_discount_id +msgid "Global Discount" +msgstr "Sconto Globale" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__global_discount_item +msgid "Global Discount Item" +msgstr "Oggetto Sconto Globale" + +#. module: account_global_discount +#: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +msgid "Global Discounts" +msgstr "Sconti Globali" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__id +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_report__id +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__id +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__id +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount__id +msgid "ID" +msgstr "ID" + +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_move.py:0 +#, python-format +msgid "Incompatible taxes found for global discounts." +msgstr "Rilevate imposte incompatibili per gli sconti globali." + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__invoice_id +msgid "Invoice" +msgstr "Fattura" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__invoice_global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__invoice_global_discount_id +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__invoice_global_discount_ids +msgid "Invoice Global Discount" +msgstr "Sconto Globale Fattura" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__global_discount_ids +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +msgid "Invoice Global Discounts" +msgstr "Sconti Globali Fattura" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__global_discount_ids_readonly +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids_readonly +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__global_discount_ids_readonly +msgid "Invoice Global Discounts (readonly)" +msgstr "Sconti Globali Fattura (Sola lettura)" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_invoice_report +msgid "Invoices Statistics" +msgstr "Statistiche Fatture" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_move +msgid "Journal Entry" +msgstr "Registrazione contabile" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_move_line +msgid "Journal Item" +msgstr "Movimento contabile" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount____last_update +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_report____last_update +#: model:ir.model.fields,field_description:account_global_discount.field_account_move____last_update +#: model:ir.model.fields,field_description:account_global_discount.field_account_move_line____last_update +#: model:ir.model.fields,field_description:account_global_discount.field_global_discount____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids +msgid "Taxes" +msgstr "Imposte" + +#. module: account_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_bank_statement_line__amount_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount +#: model:ir.model.fields,field_description:account_global_discount.field_account_payment__amount_global_discount +msgid "Total Global Discounts" +msgstr "Totale Sconti Globali" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +msgid "Untaxed Amount Before Disc." +msgstr "Imponibile pre-sconto" + +#. module: account_global_discount +#: code:addons/account_global_discount/models/account_move.py:0 +#, python-format +msgid "With global discounts, taxes in lines are required." +msgstr "Con gli sconti globali, sono necessarie le imposte sulle righe." From d197a4ee833287f5249023f716a0c741c77fd028 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Sun, 3 Jul 2022 11:34:58 +0200 Subject: [PATCH 26/34] [FIX] *: Change _recompute_tax_lines args according upstream https://github.com/odoo/odoo/commit/c9d5f069739d897145f843ee93b7eefd1d39 has changed the number of arguments on the method `_recompute_tax_lines`, so we need to adapt the overrides to such change. --- account_global_discount/__manifest__.py | 2 +- account_global_discount/models/account_move.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index a6222980bdb..3b4030943c6 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "14.0.1.0.0", + "version": "14.0.1.0.1", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/account-invoicing", diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index c1578314195..3ce1ed30177 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -55,7 +55,9 @@ class AccountMove(models.Model): readonly=True, ) - def _recompute_tax_lines(self, recompute_tax_base_amount=False): + def _recompute_tax_lines( + self, recompute_tax_base_amount=False, tax_rep_lines_to_recompute=None + ): """Inject the global discounts recomputation if recompute_tax_base_amount is false, as on contrary, only the tax_base_amount field is recalculated, not affecting global discount computation. @@ -65,7 +67,10 @@ def _recompute_tax_lines(self, recompute_tax_base_amount=False): if not recompute_tax_base_amount: # TODO: To be changed to invoice_global_discount_id when properly filled self.line_ids -= self.line_ids.filtered("global_discount_item") - res = super()._recompute_tax_lines(recompute_tax_base_amount) + res = super()._recompute_tax_lines( + recompute_tax_base_amount, + tax_rep_lines_to_recompute=tax_rep_lines_to_recompute, + ) if not recompute_tax_base_amount: self._update_tax_lines_for_global_discount() self._set_global_discounts_by_tax() From a53ccc8c6a9723e650c6a38a356c09ef11a1b268 Mon Sep 17 00:00:00 2001 From: Pedro Castro Silva Date: Wed, 17 Aug 2022 09:35:20 +0000 Subject: [PATCH 27/34] Translated using Weblate (Portuguese) Currently translated at 100.0% (35 of 35 strings) Translation: account-invoicing-14.0/account-invoicing-14.0-account_global_discount Translate-URL: https://translation.odoo-community.org/projects/account-invoicing-14-0/account-invoicing-14-0-account_global_discount/pt/ --- account_global_discount/i18n/pt.po | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/account_global_discount/i18n/pt.po b/account_global_discount/i18n/pt.po index 57252865240..eedfd349439 100644 --- a/account_global_discount/i18n/pt.po +++ b/account_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 13:00+0000\n" +"PO-Revision-Date: 2022-08-17 12:07+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: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document @@ -22,6 +22,8 @@ msgid "" "Global Discounts\n" "
    " msgstr "" +"Descontos Globais\n" +"
    " #. module: account_global_discount #: model_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document @@ -49,12 +51,12 @@ msgstr "Conta analítica" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base_discounted msgid "Base after discount" -msgstr "" +msgstr "Base após desconto" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__base msgid "Base before discount" -msgstr "" +msgstr "Base antes de desconto" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__company_id @@ -115,7 +117,7 @@ msgstr "Desconto Global" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move_line__global_discount_item msgid "Global Discount Item" -msgstr "" +msgstr "Item de Desconto Global" #. module: account_global_discount #: model:ir.ui.menu,name:account_global_discount.menu_account_global_discount @@ -155,12 +157,12 @@ msgstr "Descontos Globais de Fatura" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__global_discount_ids_readonly msgid "Invoice Global Discounts (readonly)" -msgstr "" +msgstr "Descontos Globais de Faturas (apenas leitura)" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move msgid "Journal Entries" -msgstr "" +msgstr "Entradas no Diário" #. module: account_global_discount #: model:ir.model,name:account_global_discount.model_account_move_line @@ -185,7 +187,7 @@ msgstr "Atualizado pela última vez em" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_invoice_global_discount__tax_ids msgid "Taxes" -msgstr "" +msgstr "Impostos" #. module: account_global_discount #: model:ir.model.fields,field_description:account_global_discount.field_account_move__amount_global_discount From e0645d43ec3749be90215cba2960d9f5ea088adc Mon Sep 17 00:00:00 2001 From: Kiril Vangelovski Date: Fri, 21 Oct 2022 18:03:38 +0200 Subject: [PATCH 28/34] [MIG] account_global_discount: Migration to 15.0 --- account_global_discount/__manifest__.py | 2 +- account_global_discount/models/account_move.py | 6 ++++-- account_global_discount/tests/test_global_discount.py | 2 +- account_global_discount/views/account_invoice_views.xml | 4 ++-- account_global_discount/views/report_account_invoice.xml | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py index 3b4030943c6..08bf04d8eb2 100644 --- a/account_global_discount/__manifest__.py +++ b/account_global_discount/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Global Discount", - "version": "14.0.1.0.1", + "version": "15.0.1.0.0", "category": "Accounting", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/account-invoicing", diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py index 3ce1ed30177..674bb5c2280 100644 --- a/account_global_discount/models/account_move.py +++ b/account_global_discount/models/account_move.py @@ -277,9 +277,10 @@ def _compute_amount_one(self): ) def _compute_amount(self): """Modify totals computation for including global discounts.""" - super()._compute_amount() + __ = super()._compute_amount() for record in self: record._compute_amount_one() + return __ @api.model_create_multi def create(self, vals_list): @@ -295,7 +296,7 @@ def create(self, vals_list): def _check_balanced(self): """Add the check of proper taxes for global discounts.""" - super()._check_balanced() + __ = super()._check_balanced() test_condition = not config["test_enable"] or self.env.context.get( "test_account_global_discount" ) @@ -317,6 +318,7 @@ def _check_balanced(self): ) else: taxes_keys[tuple(inv_line.tax_ids.ids)] = True + return __ class AccountMoveLine(models.Model): diff --git a/account_global_discount/tests/test_global_discount.py b/account_global_discount/tests/test_global_discount.py index 83a2a08c4a4..7f1ea5e3419 100644 --- a/account_global_discount/tests/test_global_discount.py +++ b/account_global_discount/tests/test_global_discount.py @@ -6,7 +6,7 @@ from odoo.tests import Form, common -class TestGlobalDiscount(common.SavepointCase): +class TestGlobalDiscount(common.TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() diff --git a/account_global_discount/views/account_invoice_views.xml b/account_global_discount/views/account_invoice_views.xml index c3dc6c1afe9..87263fc851d 100644 --- a/account_global_discount/views/account_invoice_views.xml +++ b/account_global_discount/views/account_invoice_views.xml @@ -26,7 +26,7 @@ groups="!base_global_discount.group_global_discount" /> - + - +