diff --git a/account_global_discount/README.rst b/account_global_discount/README.rst new file mode 100644 index 00000000000..ea648dfabf2 --- /dev/null +++ b/account_global_discount/README.rst @@ -0,0 +1,134 @@ +======================= +Account Global Discount +======================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:2ca255fdd44114cd7497a3f2adf9e073832d649ae20b802c09d6ac7dc415050e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/17.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-17-0/account-invoicing-17-0-account_global_discount + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Apply global discounts to invoices + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to: + +1. Go to *Settings > Parameters > Global Discounts*. +2. Add a new discount percentage. +3. Choose the discount scope (sales or purchases). +4. You can also restrict it to a certain company if needed. + +You can assign global discounts to partners as well: + +1. Go to a partner that is a company. +2. Go to the *Sales & Purchases* tab. +3. In section sale, you can set sale discounts. +4. In section purchase, you can set purchase discounts. + +Usage +===== + +To use this module, you need to: + +1. Go to *Invoicing > Customers > Invoices*. +2. 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. +3. Add several invoice lines. +4. At the bottom of the form you will see how global discounts affect + the total values. +5. 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. +6. 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 +====================== + +- 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. +- No tax tags are populated for the global discount move lines, only + tax_ids. + +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 to smash 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 + - Ernesto Tejeda + - Víctor Martínez + +- Omar Castiñeira + +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..7a6b621ac57 --- /dev/null +++ b/account_global_discount/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import report +from .hooks import _pre_init_global_discount_fields diff --git a/account_global_discount/__manifest__.py b/account_global_discount/__manifest__.py new file mode 100644 index 00000000000..454712a926e --- /dev/null +++ b/account_global_discount/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2019 Tecnativa S.L. - David Vidal +# 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": "17.0.1.0.0", + "category": "Accounting", + "author": "Tecnativa, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-invoicing", + "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", + ], + "application": False, + "installable": True, + "pre_init_hook": "_pre_init_global_discount_fields", +} diff --git a/account_global_discount/hooks.py b/account_global_discount/hooks.py new file mode 100644 index 00000000000..8674427236e --- /dev/null +++ b/account_global_discount/hooks.py @@ -0,0 +1,30 @@ +from odoo.tools.sql import column_exists + + +def _pre_init_global_discount_fields(env): + if not column_exists(env.cr, "account_move", "amount_global_discount"): + env.cr.execute( + """ + ALTER TABLE "account_move" + ADD COLUMN "amount_global_discount" double precision DEFAULT 0 + """ + ) + env.cr.execute( + """ + ALTER TABLE "account_move" ALTER COLUMN "amount_global_discount" DROP DEFAULT + """ + ) + if not column_exists( + env.cr, "account_move", "amount_untaxed_before_global_discounts" + ): + env.cr.execute( + """ + ALTER TABLE "account_move" + ADD COLUMN "amount_untaxed_before_global_discounts" double precision + """ + ) + env.cr.execute( + """ + update account_move set amount_untaxed_before_global_discounts = amount_untaxed + """ + ) 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..7d95a3d64ae --- /dev/null +++ b/account_global_discount/i18n/account_global_discount.pot @@ -0,0 +1,218 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_global_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.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_terms:ir.ui.view,arch_db:account_global_discount.report_invoice_document +msgid "" +"Global Discounts\n" +"
" +msgstr "" + +#. module: account_global_discount +#: 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 +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 "" + +#. 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_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 before discount" +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_display +msgid "Discount" +msgstr "" + +#. module: account_global_discount +#: 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 +msgid "Discounted Amount" +msgstr "" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +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 +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.view_move_form +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 +#. odoo-python +#: 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.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_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 Entry" +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_global_discount__tax_ids +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 "" + +#. module: account_global_discount +#: model_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +msgid "Untaxed Amount Before Disc." +msgstr "" + +#. module: account_global_discount +#. odoo-python +#: 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 new file mode 100644 index 00000000000..5281ff89d13 --- /dev/null +++ b/account_global_discount/i18n/es.po @@ -0,0 +1,223 @@ +# 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: \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: 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 "" +"Descuentos Globales\n" +"
" + +#. module: account_global_discount +#: 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 +msgid "Account" +msgstr "Cuenta" + +#. 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 "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_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 before discount" +msgstr "" + +#. 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_display +msgid "Discount" +msgstr "Descuento" + +#. module: account_global_discount +#: 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 +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..." +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 +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 "Artículo de Descuento 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.view_move_form +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 +#: code:addons/account_global_discount/models/account_move.py:0 +#, python-format +msgid "Incompatible taxes found for global discounts." +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 +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_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_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" +msgstr "Asientos contables" + +#. 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_global_discount__tax_ids +msgid "Taxes" +msgstr "" + +#. module: account_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.view_move_form +msgid "Untaxed Amount Before Disc." +msgstr "Base Imponible Antes Desc." + +#. 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 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" + +#, fuzzy +#~ msgid "Invoice Tax" +#~ msgstr "Factura" diff --git a/account_global_discount/i18n/it.po b/account_global_discount/i18n/it.po new file mode 100644 index 00000000000..7c8db455216 --- /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: 2024-07-31 14:11+0000\n" +"Last-Translator: mymage \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 5.6.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 visualizzato" + +#. 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." diff --git a/account_global_discount/i18n/nl_NL.po b/account_global_discount/i18n/nl_NL.po new file mode 100644 index 00000000000..cff6bdcb664 --- /dev/null +++ b/account_global_discount/i18n/nl_NL.po @@ -0,0 +1,218 @@ +# 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_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 before discount" +msgstr "" + +#. 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,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_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 +#: 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" +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 "Taxes" +msgstr "" + +#. 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." + +#~ 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 new file mode 100644 index 00000000000..eedfd349439 --- /dev/null +++ b/account_global_discount/i18n/pt.po @@ -0,0 +1,221 @@ +# 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: 2022-08-17 12:07+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 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 "" +"Descontos Globais\n" +"
" + +#. 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_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" + +#. 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_discounted +msgid "Base after discount" +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 "Base antes de 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_terms:ir.ui.view,arch_db:account_global_discount.view_move_form +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.model.fields,field_description:account_global_discount.field_account_move_line__global_discount_item +msgid "Global Discount Item" +msgstr "Item de 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.view_move_form +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_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.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_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_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 "Descontos Globais de Faturas (apenas leitura)" + +#. module: account_global_discount +#: model:ir.model,name:account_global_discount.model_account_move +msgid "Journal Entries" +msgstr "Entradas no Diário" + +#. 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 "Taxes" +msgstr "Impostos" + +#. module: account_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.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_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 "Base discounted" +#~ msgstr "Base com desconto" + +#~ msgid "Discounted amount" +#~ msgstr "Montante descontado" + +#~ msgid "Tax" +#~ msgstr "Imposto" + +#~ msgid "Global Discounts
" +#~ msgstr "Descontos Globais
" + +#~ msgid "Invoice Tax" +#~ msgstr "Imposto de Fatura" diff --git a/account_global_discount/models/__init__.py b/account_global_discount/models/__init__.py new file mode 100644 index 00000000000..4a3305a1e70 --- /dev/null +++ b/account_global_discount/models/__init__.py @@ -0,0 +1,2 @@ +from . import account_move +from . import global_discount diff --git a/account_global_discount/models/account_move.py b/account_global_discount/models/account_move.py new file mode 100644 index 00000000000..d18a7cc0d83 --- /dev/null +++ b/account_global_discount/models/account_move.py @@ -0,0 +1,391 @@ +# Copyright 2019 Tecnativa - David Vidal +# 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 +from odoo.tools import config + + +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", + 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(move_type, [])), ('account_id', '!=', False), '|', " + "('company_id', '=', company_id), ('company_id', '=', False)]", + compute="_compute_global_discount_ids", + store=True, + readonly=False, + ) + amount_global_discount = fields.Monetary( + string="Total Global Discounts", + compute="_compute_amount", + currency_field="currency_id", + readonly=True, + compute_sudo=True, + store=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, + store=True, + ) + invoice_global_discount_ids = fields.One2many( + comodel_name="account.invoice.global.discount", + inverse_name="invoice_id", + readonly=True, + ) + + 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, + "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. + + 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 line: line.display_type not in ["line_section", "line_note"] + ): + if inv_line.product_id.bypass_global_discount: + continue + taxes_keys.setdefault(tuple(inv_line.tax_ids.ids), 0) + taxes_keys[tuple(inv_line.tax_ids.ids)] += inv_line.price_subtotal + # 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 = [] + discount_line_base = 0 + for key in taxes_keys: + if tax_line.tax_line_id.id in key: + discount_line_base = taxes_keys[key] + taxes_keys[key] = 0 # mark for not duplicating + break # we leave in key variable the proper taxes value + if not discount_line_base: + continue + for global_discount in self.global_discount_ids: + vals = self._prepare_global_discount_vals( + global_discount, discount_line_base, key + ) + create_method(vals) + discount_line_base = vals["base_discounted"] + _self._set_global_discounts_by_zero_tax(taxes_keys, create_method) + + def _set_global_discounts_by_zero_tax(self, taxes_keys, create_method): + # 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) + base_total = 0 + zero_taxes = self.env["account.tax"] + for line in self.line_ids.filtered("tax_ids"): + if line.product_id.bypass_global_discount: + continue + key = tuple(line.tax_ids.ids) + if taxes_keys.get(key): + base_total += line.price_subtotal + zero_taxes |= line.tax_ids + for global_discount in self.global_discount_ids: + if not base_total: + break + vals = self._prepare_global_discount_vals( + global_discount, base_total, zero_taxes.ids + ) + create_method(vals) + base_total = vals["base_discounted"] + + 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"): + sign = -1 if self.move_type in {"in_invoice", "out_refund"} else 1 + disc_amount = sign * discount.discount_amount + disc_amount_company_currency = disc_amount + if self.currency_id != self.company_id.currency_id: + disc_amount_company_currency = self.currency_id._convert( + disc_amount, + self.company_id.currency_id, + self.company_id, + self.date or fields.Date.context_today(self), + ) + create_method( + { + "invoice_global_discount_id": discount.id, + "move_id": self.id, + "name": "{} - {}".format( + discount.name, ", ".join(discount.tax_ids.mapped("name")) + ), + "debit": disc_amount_company_currency > 0.0 + and disc_amount_company_currency + or 0.0, + "credit": disc_amount_company_currency < 0.0 + and -disc_amount_company_currency + 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, + "tax_ids": [(4, x.id) for x in discount.tax_ids], + "partner_id": self.commercial_partner_id.id, + "currency_id": self.currency_id.id, + "price_unit": -1 * abs(disc_amount_company_currency), + } + ) + + @api.depends("partner_id", "company_id", "move_type") + def _compute_global_discount_ids(self): + for move in self: + discounts = self.env["global.discount"] + move_discounts = self.env["global.discount"] + if ( + move.move_type in ["out_invoice", "out_refund"] + and move.partner_id.customer_global_discount_ids + ): + move_discounts = move.partner_id.customer_global_discount_ids + elif ( + move.move_type in ["in_refund", "in_invoice"] + and move.partner_id.supplier_global_discount_ids + ): + move_discounts = move.partner_id.supplier_global_discount_ids + for discount in move_discounts: + if discount.company_id == move.company_id: + discounts |= discount + move.global_discount_ids = discounts + + 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 + 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_global_discount + ) + + @api.depends( + "line_ids.matched_debit_ids.debit_move_id.move_id.payment_id.is_matched", + "line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual", + "line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual_currency", + "line_ids.matched_credit_ids.credit_move_id.move_id.payment_id.is_matched", + "line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual", + "line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual_currency", + "line_ids.balance", + "line_ids.currency_id", + "line_ids.amount_currency", + "line_ids.amount_residual", + "line_ids.amount_residual_currency", + "line_ids.payment_id.state", + "line_ids.full_reconcile_id", + "invoice_global_discount_ids", + "global_discount_ids", + ) + def _compute_amount(self): + """Modify totals computation for including global discounts.""" + res = super()._compute_amount() + for record in self: + record._compute_amount_one() + return res + + def _clean_global_discount_lines(self): + self.ensure_one() + gbl_disc_lines = self.env["account.move.line"].search( + [ + ("move_id", "=", self.id), + "|", + ("global_discount_item", "=", True), + ("invoice_global_discount_id", "!=", False), + ] + ) + if gbl_disc_lines: + move_container = {"records": self} + with self._check_balanced(move_container), self._sync_dynamic_lines( + move_container + ): + gbl_disc_lines.unlink() + + @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, as some data + like ``tax_ids`` is not set until the final step. + """ + moves = super().create(vals_list) + for move in moves: + if move.move_type in ["out_refund", "in_refund"]: + move._clean_global_discount_lines() + move._set_global_discounts_by_tax() + move._recompute_global_discount_lines() + return moves + + def write(self, vals): + res = super().write(vals) + if "invoice_line_ids" in vals or "global_discount_ids" in vals: + for move in self: + move._clean_global_discount_lines() + move._set_global_discounts_by_tax() + move._recompute_global_discount_lines() + move_container = {"records": self} + self._global_discount_check(move_container) + return res + + def _global_discount_check(self, container): + test_condition = not config["test_enable"] or self.env.context.get( + "test_account_global_discount" + ) + for move in container["records"]: + if not move.is_invoice() or not move.global_discount_ids: + continue + taxes_keys = {} + for inv_line in move.invoice_line_ids: + if inv_line.display_type != "product": + continue + 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 + return True + + +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, + ) + # TODO: To be removed on future versions if invoice_global_discount_id + # is properly filled Provided for compatibility in stable branch + # UPD: can be removed past version 16.0 + 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) + invoice_id = fields.Many2one( + "account.move", + string="Invoice", + ondelete="cascade", + index=True, + readonly=True, + domain=[ + ( + "move_type", + "in", + ["out_invoice", "out_refund", "in_invoice", "in_refund"], + ) + ], + ) + global_discount_id = fields.Many2one( + comodel_name="global.discount", + string="Global Discount", + ) + discount = fields.Float(string="Discount (number)") + discount_display = fields.Char( + 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", + compute_sudo=True, + ) + tax_ids = fields.Many2many(comodel_name="account.tax", string="Taxes") + account_id = fields.Many2one( + comodel_name="account.account", + required=True, + string="Account", + domain=( + "[('account_type', 'not in', ['asset_receivable', 'liability_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/global_discount.py b/account_global_discount/models/global_discount.py new file mode 100644 index 00000000000..99d20682814 --- /dev/null +++ b/account_global_discount/models/global_discount.py @@ -0,0 +1,29 @@ +# 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" + _check_company_auto = True + + account_id = fields.Many2one( + comodel_name="account.account", + string="Account", + domain=( + "[('account_type', 'not in', ['asset_receivable', 'liability_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): + """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/pyproject.toml b/account_global_discount/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/account_global_discount/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/account_global_discount/readme/CONFIGURE.md b/account_global_discount/readme/CONFIGURE.md new file mode 100644 index 00000000000..fea613f52f1 --- /dev/null +++ b/account_global_discount/readme/CONFIGURE.md @@ -0,0 +1,13 @@ +To configure this module, you need to: + +1. Go to *Settings \> Parameters \> Global Discounts*. +2. Add a new discount percentage. +3. Choose the discount scope (sales or purchases). +4. You can also restrict it to a certain company if needed. + +You can assign global discounts to partners as well: + +1. Go to a partner that is a company. +2. Go to the *Sales & Purchases* tab. +3. In section sale, you can set sale discounts. +4. In section purchase, you can set purchase discounts. diff --git a/account_global_discount/readme/CONTRIBUTORS.md b/account_global_discount/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..32eaae2c42e --- /dev/null +++ b/account_global_discount/readme/CONTRIBUTORS.md @@ -0,0 +1,11 @@ +- [Tecnativa](https://www.tecnativa.com) + - Pedro M. Baeza + - David Vidal + - Carlos Dauden + - Rafael Blasco + - Ernesto Tejeda + - Víctor Martínez +- Omar Castiñeira \<\> + +- [Studio73](https://www.studio73.es/) + - Miguel Gandia diff --git a/account_global_discount/readme/DESCRIPTION.md b/account_global_discount/readme/DESCRIPTION.md new file mode 100644 index 00000000000..1b2a3d1ac43 --- /dev/null +++ b/account_global_discount/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Apply global discounts to invoices diff --git a/account_global_discount/readme/ROADMAP.md b/account_global_discount/readme/ROADMAP.md new file mode 100644 index 00000000000..430b8e36e1c --- /dev/null +++ b/account_global_discount/readme/ROADMAP.md @@ -0,0 +1,6 @@ +- 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. +- No tax tags are populated for the global discount move lines, only + tax_ids. diff --git a/account_global_discount/readme/USAGE.md b/account_global_discount/readme/USAGE.md new file mode 100644 index 00000000000..68b48a37ae8 --- /dev/null +++ b/account_global_discount/readme/USAGE.md @@ -0,0 +1,17 @@ +To use this module, you need to: + +1. Go to *Invoicing \> Customers \> Invoices*. +2. 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. +3. Add several invoice lines. +4. At the bottom of the form you will see how global discounts affect + the total values. +5. 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. +6. 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/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)", + ) 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..11d680f82f1 --- /dev/null +++ b/account_global_discount/security/security.xml @@ -0,0 +1,19 @@ + + + + + Global Discount multi-company + + ['|',('company_id','=',False),('company_id', 'in', company_ids)] + + + + + + diff --git a/account_global_discount/static/description/icon.png b/account_global_discount/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/account_global_discount/static/description/icon.png differ 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..43ed5a7a419 --- /dev/null +++ b/account_global_discount/static/description/index.html @@ -0,0 +1,485 @@ + + + + + +Account Global Discount + + + +
+

Account Global Discount

+ + +

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

+

Apply global discounts to invoices

+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to:

+
    +
  1. Go to Settings > Parameters > Global Discounts.
  2. +
  3. Add a new discount percentage.
  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, you can set sale discounts.
  6. +
  7. In section purchase, you can set purchase discounts.
  8. +
+
+
+

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.
  12. +
+
+
+

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.
  • +
  • No tax tags are populated for the global discount move lines, only +tax_ids.
  • +
+
+
+

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 to smash 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
    • +
    • Ernesto Tejeda
    • +
    • Víctor Martínez
    • +
    +
  • +
  • Omar Castiñeira <omar@comunitea.com>
  • +
+
+
+

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/__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..949a333fde1 --- /dev/null +++ b/account_global_discount/tests/test_global_discount.py @@ -0,0 +1,483 @@ +# 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, tagged + +from odoo.addons.account.tests.common import AccountTestInvoicingCommon + + +@tagged("post_install", "-at_install") +class TestGlobalDiscount(AccountTestInvoicingCommon): + @classmethod + def setUpClass(cls, chart_template_ref=None): + super().setUpClass(chart_template_ref=chart_template_ref) + cls.env.ref("base_global_discount.group_global_discount").write( + {"users": [(4, cls.env.user.id)]} + ) + cls.currency_eur = cls.env.ref("base.EUR") + cls.currency_usd = cls.env.ref("base.USD") + cls.currency_usd.active = True + # Make sure the currency of the company is USD, as this not always happens + # To be removed in V17: https://github.com/odoo/odoo/pull/107113 + cls.company = cls.env.company + cls.env.cr.execute( + "UPDATE res_company SET currency_id = %s WHERE id = %s", + (cls.env.ref("base.USD").id, cls.company.id), + ) + cls.account = cls.env["account.account"].create( + { + "name": "Test account", + "code": "TEST", + "account_type": "income_other", + "reconcile": True, + } + ) + cls.account_receivable = cls.env["account.account"].create( + { + "name": "Test receivable account", + "code": "ACCRV", + "account_type": "asset_receivable", + "reconcile": True, + } + ) + cls.account_payable = cls.env["account.account"].create( + { + "name": "Test receivable account", + "code": "ACCPAY", + "account_type": "liability_payable", + "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, + "sequence": 3, + } + ) + cls.global_discount_2 = cls.global_discount_obj.create( + { + "name": "Test Discount 2", + "discount_scope": "purchase", + "discount": 30, + "account_id": cls.account.id, + "sequence": 2, + } + ) + cls.global_discount_3 = cls.global_discount_obj.create( + { + "name": "Test Discount 3", + "discount_scope": "purchase", + "discount": 50, + "account_id": cls.account.id, + "sequence": 1, + } + ) + 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.tax_purchase_a + cls.tax.amount = 15 + cls.tax_0 = cls.tax_purchase_b + cls.tax_0.amount = 0 + cls.journal = cls.env["account.journal"].create( + {"name": "Test purchase journal", "code": "TPUR", "type": "purchase"} + ) + cls.product_3 = cls.env["product.product"].create( + { + "name": "Test Product 3", + "type": "service", + "bypass_global_discount": True, + } + ) + cls.invoice_line = cls.env["account.move.line"] + invoice_form = Form( + cls.env["account.move"].with_context( + default_move_type="in_invoice", + test_account_global_discount=True, + ) + ) + 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 + line_form.quantity = 1 + line_form.tax_ids.clear() + line_form.tax_ids.add(cls.tax) + with invoice_form.invoice_line_ids.new() as line_form: + line_form.name = "Line 1" + line_form.product_id = cls.product_3 + line_form.price_unit = 200.0 + line_form.quantity = 1 + line_form.tax_ids.clear() + line_form.tax_ids.add(cls.tax_0) + cls.invoice = invoice_form.save() + + def test_01_global_invoice_succesive_discounts(self): + """Add global discounts to the invoice""" + 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. 3) = 100 + 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) + 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 + 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) + 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 + 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) + 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_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) + # 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 + 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) + 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.tax.copy(default={"amount": 20.0, "name": "Tax 2"}) + 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) + 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.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) + # Check journal items validity + lines = self.invoice.line_ids + line_15 = lines.filtered( + lambda x: x.invoice_global_discount_id and x.tax_ids == self.tax + ) + self.assertAlmostEqual(line_15.credit, 40) + line_20 = lines.filtered( + lambda x: x.invoice_global_discount_id and x.tax_ids == tax2 + ) + self.assertAlmostEqual(line_20.credit, 20) + + def test_04_multiple_taxes_same_line(self): + tax2 = self.tax.copy( + default={"amount": -20.0, "name": "Tax 2"} + ) # negative for testing more use cases + 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) + # 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.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) + + def test_05_incompatible_taxes(self): + # Line 1 with tax and tax2 + # Line 2 with only tax2 + tax2 = self.tax.copy( + default={"amount": -20.0, "name": "Tax 2"} + ) # negative for testing more use cases + with self.assertRaises(exceptions.UserError): + 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): + with self.assertRaises(exceptions.UserError): + 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() + + def test_07_line_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.edit(0) as line_form: + line_form.tax_ids.clear() + line_form.tax_ids.add(self.tax_0) + discounts = self.invoice.invoice_global_discount_ids + self.assertEqual(len(discounts), 1) + self.assertAlmostEqual(discounts.discount_amount, 40) + + 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: + 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) + 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) + + 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.tax_sale_a.copy(default={"amount": 15.0, "name": "Tax 2"}) + invoice = ( + self.env["account.move"] + .with_context(test_account_global_discount=True) + .create( + { + "move_type": "out_invoice", + "partner_id": self.partner_1.id, + "global_discount_ids": [(6, 0, global_discount.ids)], + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Line 1", + "price_unit": 200.0, + "quantity": 1, + "tax_ids": [(6, 0, tax.ids)], + }, + ) + ], + } + ) + ) + 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.invoice_global_discount_id and x.tax_ids == tax + ) + self.assertAlmostEqual(line_15.debit, 100) + + def test_10_customer_invoice_currency(self): + """Multi-currency""" + eur = self.env.ref("base.EUR") + usd = self.env.ref("base.USD") + self.assertEqual(self.env.user.company_id.currency_id, usd) + with Form(self.invoice) as invoice_form: + invoice_form.currency_id = eur + invoice = invoice_form.save() + self.assertAlmostEqual(invoice.amount_total, 230.0) + self.assertAlmostEqual(invoice.amount_untaxed, 200.0) + self.assertAlmostEqual(invoice.amount_global_discount, 0) + base_line = invoice.line_ids.filtered( + lambda line: ( + line.tax_ids + and not line.invoice_global_discount_id + and not line.product_id + ) + ) + self.assertEqual(len(base_line), 1) + self.assertAlmostEqual( + base_line.balance, + eur._convert( + invoice.amount_untaxed, usd, self.env.user.company_id, invoice.date + ), + ) + tax_line = invoice.line_ids.filtered( + lambda line: line.tax_line_id and not line.invoice_global_discount_id + ) + self.assertEqual(len(tax_line), 1) + tax_line_balance_before_discount = tax_line.balance + self.assertAlmostEqual( + tax_line_balance_before_discount, + eur._convert( + invoice.amount_untaxed * (self.tax.amount / 100), + usd, + self.env.user.company_id, + invoice.date, + ), + ) + self.assertAlmostEqual( + tax_line.tax_base_amount, + eur._convert( + invoice.amount_untaxed, + usd, + self.env.user.company_id, + invoice.date, + ), + ) + discount_line = invoice.line_ids.filtered("invoice_global_discount_id") + self.assertFalse(discount_line) + with Form(self.invoice) as invoice_form: + invoice_form.global_discount_ids.add(self.global_discount_1) + invoice = invoice_form.save() + # Check that when we add a global discount it will be based on the + # correct currency + self.assertAlmostEqual(invoice.amount_total, 184) + self.assertAlmostEqual(invoice.amount_untaxed, 160.0) + self.assertAlmostEqual(invoice.amount_global_discount, -40.0) + base_line = invoice.line_ids.filtered( + lambda line: ( + line.tax_ids + and not line.invoice_global_discount_id + and not line.product_id + ) + ) + self.assertEqual(len(base_line), 1) + self.assertAlmostEqual( + base_line.balance, + eur._convert( + invoice.amount_untaxed_before_global_discounts, + usd, + self.env.user.company_id, + invoice.date, + ), + ) + tax_line = invoice.line_ids.filtered( + lambda line: line.tax_line_id and not line.invoice_global_discount_id + ) + self.assertEqual(len(tax_line), 1) + self.assertAlmostEqual( + tax_line.tax_base_amount, + eur._convert( + invoice.amount_untaxed, + usd, + self.env.user.company_id, + invoice.date, + ), + ) + self.assertAlmostEqual( + tax_line.balance, + eur._convert( + invoice.amount_untaxed * (self.tax.amount / 100), + usd, + self.env.user.company_id, + invoice.date, + ), + ) + self.assertLess(tax_line.balance, tax_line_balance_before_discount) + discount_line = invoice.line_ids.filtered("invoice_global_discount_id") + self.assertEqual(len(discount_line), 1) + self.assertAlmostEqual( + discount_line.balance, + eur._convert( + invoice.amount_global_discount, + usd, + self.env.user.company_id, + invoice.date, + ), + ) 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..d569c9011c2 --- /dev/null +++ b/account_global_discount/views/account_invoice_views.xml @@ -0,0 +1,138 @@ + + + + + account.move + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..b44888894f8 --- /dev/null +++ b/account_global_discount/views/global_discount_views.xml @@ -0,0 +1,48 @@ + + + + + global.discount + + + + + + + + + + global.discount + + + + + + + + + + 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..57e01f14ea8 --- /dev/null +++ b/account_global_discount/views/report_account_invoice.xml @@ -0,0 +1,35 @@ + + + +