diff --git a/sale_order_invoice_amount/README.rst b/sale_order_invoice_amount/README.rst new file mode 100644 index 00000000000..c2f6e08c47b --- /dev/null +++ b/sale_order_invoice_amount/README.rst @@ -0,0 +1,82 @@ +========================= +Sale Order Invoice Amount +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:ca0433736b2583065c0392babb99c010f343b58cf693c21111ccf1f579ec26b8 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/sale-workflow/tree/18.0/sale_order_invoice_amount + :alt: OCA/sale-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_order_invoice_amount + :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/sale-workflow&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adapts the computation of invoiced and to invoice amounts in +Sales Orders. When the company option "Enable amount invoiced based on +quantity" is activated, the values are calculated from the quantities +invoiced vs ordered, instead of relying only on invoice monetary +amounts. + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* ForgeFlow + +Contributors +------------ + +- Mateu Griful +- Lois Rilo +- Meritxell Abellan + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/sale-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_order_invoice_amount/__init__.py b/sale_order_invoice_amount/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/sale_order_invoice_amount/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_order_invoice_amount/__manifest__.py b/sale_order_invoice_amount/__manifest__.py new file mode 100644 index 00000000000..e9413dd53de --- /dev/null +++ b/sale_order_invoice_amount/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright (C) 2021 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +{ + "name": "Sale Order Invoice Amount", + "version": "18.0.1.0.0", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/sale-workflow", + "category": "Sales", + "license": "LGPL-3", + "summary": "Improves Sales Order invoiced/to invoice amount calculation " + "based on product quantities when the company setting is enabled.", + "depends": [ + "sale", + ], + "data": [ + "views/sale_order_view.xml", + ], + "installable": True, + "assets": { + "web.assets_backend": [ + "sale_order_invoice_amount/static/src/xml/tax_totals.xml", + ], + }, +} diff --git a/sale_order_invoice_amount/i18n/es.po b/sale_order_invoice_amount/i18n/es.po new file mode 100644 index 00000000000..3558c1f4522 --- /dev/null +++ b/sale_order_invoice_amount/i18n/es.po @@ -0,0 +1,48 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_invoice_amount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_order_invoice_amount +#. odoo-javascript +#: code:addons/sale_order_invoice_amount/static/src/xml/tax_totals.xml:0 +#: model:ir.model.fields,field_description:sale_order_invoice_amount.field_sale_order__invoiced_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_order_tree_invoiced_amount +#, python-format +msgid "Invoiced Amount" +msgstr "Importe facturado" + +#. module: sale_order_invoice_amount +#: model:ir.model.fields,help:sale_order_invoice_amount.field_sale_order__invoiced_amount +msgid "Order amount already invoiced." +msgstr "Importe del pedido ya facturado." + +#. module: sale_order_invoice_amount +#: model:ir.model.fields,help:sale_order_invoice_amount.field_sale_order__uninvoiced_amount +msgid "Order amount to be invoiced" +msgstr "Importe del pedido pendiente de facturar" + +#. module: sale_order_invoice_amount +#: model:ir.model,name:sale_order_invoice_amount.model_sale_order +msgid "Sales Order" +msgstr "Pedidos de venta" + +#. module: sale_order_invoice_amount +#. odoo-javascript +#: code:addons/sale_order_invoice_amount/static/src/xml/tax_totals.xml:0 +#: model:ir.model.fields,field_description:sale_order_invoice_amount.field_sale_order__uninvoiced_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_order_tree_invoiced_amount +#, python-format +msgid "Uninvoiced Amount" +msgstr "Importe no facturado" diff --git a/sale_order_invoice_amount/i18n/fr.po b/sale_order_invoice_amount/i18n/fr.po new file mode 100644 index 00000000000..a6a4af8753a --- /dev/null +++ b/sale_order_invoice_amount/i18n/fr.po @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_invoice_amount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-28 13:16+0000\n" +"Last-Translator: Nicolas JEUDY \n" +"Language-Team: none\n" +"Language: fr\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.17\n" + +#. module: sale_order_invoice_amount +#. odoo-javascript +#: code:addons/sale_order_invoice_amount/static/src/xml/tax_totals.xml:0 +#: model:ir.model.fields,field_description:sale_order_invoice_amount.field_sale_order__invoiced_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_order_tree_invoiced_amount +#, python-format +msgid "Invoiced Amount" +msgstr "Montant facturé" + +#. module: sale_order_invoice_amount +#: model:ir.model.fields,help:sale_order_invoice_amount.field_sale_order__invoiced_amount +msgid "Order amount already invoiced." +msgstr "Montant de commande déjà facturé." + +#. module: sale_order_invoice_amount +#: model:ir.model.fields,help:sale_order_invoice_amount.field_sale_order__uninvoiced_amount +msgid "Order amount to be invoiced" +msgstr "Montant de commande à facturer" + +#. module: sale_order_invoice_amount +#: model:ir.model,name:sale_order_invoice_amount.model_sale_order +msgid "Sales Order" +msgstr "Bon de commande" + +#. module: sale_order_invoice_amount +#. odoo-javascript +#: code:addons/sale_order_invoice_amount/static/src/xml/tax_totals.xml:0 +#: model:ir.model.fields,field_description:sale_order_invoice_amount.field_sale_order__uninvoiced_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_order_tree_invoiced_amount +#, python-format +msgid "Uninvoiced Amount" +msgstr "Montant à facturer" diff --git a/sale_order_invoice_amount/i18n/it.po b/sale_order_invoice_amount/i18n/it.po new file mode 100644 index 00000000000..75ee2256b79 --- /dev/null +++ b/sale_order_invoice_amount/i18n/it.po @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_invoice_amount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-12-04 09:34+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 4.17\n" + +#. module: sale_order_invoice_amount +#. odoo-javascript +#: code:addons/sale_order_invoice_amount/static/src/xml/tax_totals.xml:0 +#: model:ir.model.fields,field_description:sale_order_invoice_amount.field_sale_order__invoiced_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_order_tree_invoiced_amount +#, python-format +msgid "Invoiced Amount" +msgstr "Importo fatturato" + +#. module: sale_order_invoice_amount +#: model:ir.model.fields,help:sale_order_invoice_amount.field_sale_order__invoiced_amount +msgid "Order amount already invoiced." +msgstr "Importo ordine già fatturato." + +#. module: sale_order_invoice_amount +#: model:ir.model.fields,help:sale_order_invoice_amount.field_sale_order__uninvoiced_amount +msgid "Order amount to be invoiced" +msgstr "Importo ordine da fatturare" + +#. module: sale_order_invoice_amount +#: model:ir.model,name:sale_order_invoice_amount.model_sale_order +msgid "Sales Order" +msgstr "Ordine di vendita" + +#. module: sale_order_invoice_amount +#. odoo-javascript +#: code:addons/sale_order_invoice_amount/static/src/xml/tax_totals.xml:0 +#: model:ir.model.fields,field_description:sale_order_invoice_amount.field_sale_order__uninvoiced_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_order_tree_invoiced_amount +#, python-format +msgid "Uninvoiced Amount" +msgstr "Importo non fatturato" diff --git a/sale_order_invoice_amount/i18n/sale_order_invoice_amount.pot b/sale_order_invoice_amount/i18n/sale_order_invoice_amount.pot new file mode 100644 index 00000000000..d4fd6f4fd3c --- /dev/null +++ b/sale_order_invoice_amount/i18n/sale_order_invoice_amount.pot @@ -0,0 +1,62 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_invoice_amount +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_order_invoice_amount +#. odoo-javascript +#: code:addons/sale_order_invoice_amount/static/src/xml/tax_totals.xml:0 +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_order_tree_invoiced_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_quotation_tree_invoiced_amount +#, python-format +msgid "Already invoiced" +msgstr "" + +#. module: sale_order_invoice_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.sale_config_settings_form_view +msgid "Amount invoiced based on quantity" +msgstr "" + +#. module: sale_order_invoice_amount +#. odoo-javascript +#: code:addons/sale_order_invoice_amount/static/src/xml/tax_totals.xml:0 +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.view_quotation_tree_invoiced_amount +#, python-format +msgid "Amount to invoice" +msgstr "" + +#. module: sale_order_invoice_amount +#: model:ir.model,name:sale_order_invoice_amount.model_res_company +msgid "Companies" +msgstr "" + +#. module: sale_order_invoice_amount +#: model:ir.model,name:sale_order_invoice_amount.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: sale_order_invoice_amount +#: model_terms:ir.ui.view,arch_db:sale_order_invoice_amount.sale_config_settings_form_view +msgid "Enable calculating amount invoiced based on quantity" +msgstr "" + +#. module: sale_order_invoice_amount +#: model:ir.model.fields,field_description:sale_order_invoice_amount.field_res_company__enable_amount_invoiced_based_on_quantity +#: model:ir.model.fields,field_description:sale_order_invoice_amount.field_res_config_settings__enable_amount_invoiced_based_on_quantity +msgid "Enable computing amount invoiced based on quantity" +msgstr "" + +#. module: sale_order_invoice_amount +#: model:ir.model,name:sale_order_invoice_amount.model_sale_order +msgid "Sales Order" +msgstr "" diff --git a/sale_order_invoice_amount/models/__init__.py b/sale_order_invoice_amount/models/__init__.py new file mode 100644 index 00000000000..6aacb753131 --- /dev/null +++ b/sale_order_invoice_amount/models/__init__.py @@ -0,0 +1 @@ +from . import sale_order diff --git a/sale_order_invoice_amount/models/sale_order.py b/sale_order_invoice_amount/models/sale_order.py new file mode 100644 index 00000000000..27b9e6e4f38 --- /dev/null +++ b/sale_order_invoice_amount/models/sale_order.py @@ -0,0 +1,39 @@ +# Copyright (C) 2021 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) +from odoo import api, models +from odoo.tools.misc import formatLang + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + @api.depends( + "order_line.tax_id", + "order_line.price_unit", + "amount_total", + "amount_untaxed", + "state", + "invoice_ids", + "invoice_ids.amount_total_in_currency_signed", + "amount_total", + "invoice_ids.state", + ) + def _compute_tax_totals(self): + res = super()._compute_tax_totals() + for order in self: + lang_env = order.with_context(lang=order.partner_id.lang).env + order.tax_totals.update( + { + "amount_invoiced": order.amount_invoiced, + "formatted_amount_invoiced": formatLang( + lang_env, order.amount_invoiced, currency_obj=order.currency_id + ), + "amount_to_invoice": order.amount_to_invoice, + "formatted_amount_to_invoice": formatLang( + lang_env, + order.amount_to_invoice, + currency_obj=order.currency_id, + ), + } + ) + return res diff --git a/sale_order_invoice_amount/pyproject.toml b/sale_order_invoice_amount/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/sale_order_invoice_amount/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/sale_order_invoice_amount/readme/CONTRIBUTORS.md b/sale_order_invoice_amount/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..28dbee6518b --- /dev/null +++ b/sale_order_invoice_amount/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- Mateu Griful \<\> +- Lois Rilo \<\> +- Meritxell Abellan \<\> diff --git a/sale_order_invoice_amount/readme/DESCRIPTION.md b/sale_order_invoice_amount/readme/DESCRIPTION.md new file mode 100644 index 00000000000..02f89cc1c5d --- /dev/null +++ b/sale_order_invoice_amount/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module adapts the computation of invoiced and to invoice amounts in Sales Orders. +When the company option "Enable amount invoiced based on quantity" is activated, the values are calculated from the quantities invoiced vs ordered, instead of relying only on invoice monetary amounts. diff --git a/sale_order_invoice_amount/static/description/icon.png b/sale_order_invoice_amount/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/sale_order_invoice_amount/static/description/icon.png differ diff --git a/sale_order_invoice_amount/static/description/index.html b/sale_order_invoice_amount/static/description/index.html new file mode 100644 index 00000000000..252a56427d5 --- /dev/null +++ b/sale_order_invoice_amount/static/description/index.html @@ -0,0 +1,426 @@ + + + + + +Sale Order Invoice Amount + + + +
+

Sale Order Invoice Amount

+ + +

Beta License: LGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

+

This module adapts the computation of invoiced and to invoice amounts in +Sales Orders. When the company option “Enable amount invoiced based on +quantity” is activated, the values are calculated from the quantities +invoiced vs ordered, instead of relying only on invoice monetary +amounts.

+

Table of contents

+ +
+

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

+
    +
  • ForgeFlow
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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

+

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

+

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

+
+
+
+ + diff --git a/sale_order_invoice_amount/static/src/xml/tax_totals.xml b/sale_order_invoice_amount/static/src/xml/tax_totals.xml new file mode 100644 index 00000000000..79f818528e2 --- /dev/null +++ b/sale_order_invoice_amount/static/src/xml/tax_totals.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sale_order_invoice_amount/tests/__init__.py b/sale_order_invoice_amount/tests/__init__.py new file mode 100644 index 00000000000..15963146727 --- /dev/null +++ b/sale_order_invoice_amount/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_order_invoice_amount diff --git a/sale_order_invoice_amount/tests/test_sale_order_invoice_amount.py b/sale_order_invoice_amount/tests/test_sale_order_invoice_amount.py new file mode 100644 index 00000000000..ccdcd72f693 --- /dev/null +++ b/sale_order_invoice_amount/tests/test_sale_order_invoice_amount.py @@ -0,0 +1,430 @@ +# Copyright (C) 2021 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import fields +from odoo.tests import common, tagged + + +@tagged("post_install", "-at_install") +class TestSaleOrderInvoiceAmount(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Partners + partner_model = cls.env["res.partner"] + cls.res_partner_1 = partner_model.create({"name": "Wood Corner"}) + cls.res_partner_address_1 = partner_model.create( + {"name": "Willie Burke", "parent_id": cls.res_partner_1.id} + ) + cls.res_partner_2 = partner_model.create({"name": "Partner 12"}) + # Products + product_model = cls.env["product.product"] + cls.product_1 = product_model.create( + {"name": "Desk Combination", "type": "consu"} + ) + cls.product_2 = product_model.create( + {"name": "Conference Chair", "type": "consu"} + ) + cls.product_3 = product_model.create( + {"name": "Repair Services", "type": "service"} + ) + cls.currency_eur = cls.env.ref("base.EUR") + cls.currency_eur.active = True + cls.currency_cad = cls.env.ref("base.CAD") + cls.currency_cad.active = True + cls.env["res.currency.rate"].search( + [("currency_id", "in", [cls.currency_eur.id, cls.currency_cad.id])] + ).unlink() + cls.env["res.currency.rate"].create( + [ + { + "name": fields.Date.from_string("2024-01-01"), + "rate": 1.500000, # Fixing the rate for test support. + "currency_id": cls.currency_eur.id, + }, + { + "name": fields.Date.from_string("2024-01-01"), + "rate": 2.00000, # Fixing the rate for test support. + "currency_id": cls.currency_cad.id, + }, + ] + ) + cls.res_partner_2 = cls.env["res.partner"].create({"name": "Partner 12"}) + # Sale Order + cls.tax = cls.env["account.tax"].create( + {"name": "Tax 15", "type_tax_use": "sale", "amount": 21} + ) + cls.sale_order_1 = cls.env["sale.order"].create( + {"partner_id": cls.res_partner_1.id} + ) + sale_order_line_model = cls.env["sale.order.line"] + cls.order_line_1 = sale_order_line_model.create( + { + "order_id": cls.sale_order_1.id, + "product_id": cls.product_1.id, + "product_uom": cls.product_1.uom_id.id, + "product_uom_qty": 10.0, + "price_unit": 10.0, + "tax_id": cls.tax, + } + ) + cls.order_line_2 = sale_order_line_model.create( + { + "order_id": cls.sale_order_1.id, + "product_id": cls.product_2.id, + "product_uom": cls.product_2.uom_id.id, + "product_uom_qty": 25.0, + "price_unit": 4.0, + "tax_id": cls.tax, + } + ) + cls.order_line_3 = sale_order_line_model.create( + { + "order_id": cls.sale_order_1.id, + "product_id": cls.product_3.id, + "product_uom": cls.product_3.uom_id.id, + "product_uom_qty": 20.0, + "price_unit": 5.0, + "tax_id": cls.tax, + } + ) + + def test_01_sale_order_invoiced_amount(self): + self.assertEqual( + self.sale_order_1.amount_invoiced, + 0.0, + "Invoiced Amount should be 0.0", + ) + + self.sale_order_1.action_confirm() + aml1 = self.order_line_1._prepare_invoice_line() + aml2 = self.order_line_2._prepare_invoice_line() + test_invoice = self.env["account.move"].create( + { + "move_type": "out_invoice", + "invoice_date": fields.Date.from_string("2024-01-01"), + "date": fields.Date.from_string("2024-01-01"), + "partner_id": self.res_partner_1.id, + "line_ids": [ + ( + 0, + 0, + aml1, + ), + ( + 0, + 0, + aml2, + ), + ], + } + ) + test_invoice.action_post() + self.assertEqual( + self.sale_order_1.amount_invoiced, + 242.0, + "Invoiced Amount should be 242.0", + ) + self.assertEqual( + self.sale_order_1.amount_to_invoice, + 121.0, + "Uninvoiced Amount should be 121.0, as the lines keep uninvoiced.", + ) + tax_totals = self.sale_order_1.tax_totals + self.assertEqual( + tax_totals["amount_invoiced"], + 242.0, + ) + self.assertEqual( + tax_totals["amount_to_invoice"], + 121.00, + ) + self.assertEqual( + tax_totals["formatted_amount_invoiced"], + "$\xa0242.00", + ) + self.assertEqual( + tax_totals["formatted_amount_to_invoice"], + "$\xa0121.00", + ) + + def test_02_sale_order_invoiced_amount_different_currencies_invoice(self): + self.assertEqual( + self.sale_order_1.amount_invoiced, + 0.0, + "Invoiced Amount should be 0.0", + ) + self.sale_order_1.action_confirm() + + price_foreign_currency_1 = self.sale_order_1.currency_id._convert( + 10.0, + self.currency_eur, + self.sale_order_1.company_id, + fields.Date.from_string("2024-01-01"), + ) + price_foreign_currency_2 = self.sale_order_1.currency_id._convert( + 4.0, + self.currency_eur, + self.sale_order_1.company_id, + fields.Date.from_string("2024-01-01"), + ) + aml1 = self.order_line_1._prepare_invoice_line( + **{ + "price_unit": price_foreign_currency_1, + "currency_id": self.currency_eur.id, + } + ) + aml2 = self.order_line_2._prepare_invoice_line( + **{ + "price_unit": price_foreign_currency_2, + "currency_id": self.currency_eur.id, + } + ) + test_invoice = self.env["account.move"].create( + { + "move_type": "out_invoice", + "invoice_date": fields.Date.from_string("2024-01-01"), + "date": fields.Date.from_string("2024-01-01"), + "partner_id": self.res_partner_1.id, + "line_ids": [ + ( + 0, + 0, + aml1, + ), + ( + 0, + 0, + aml2, + ), + ], + "currency_id": self.currency_eur.id, + } + ) + test_invoice.action_post() + self.assertAlmostEqual( + self.sale_order_1.amount_invoiced, + 242.0, + delta=1, + ) + self.assertEqual( + self.sale_order_1.amount_to_invoice, + 121.0, + "Uninvoiced Amount should be 121, as the lines keep uninvoiced.", + ) + + def test_03_sale_order_invoiced_amount_different_currencies_sale(self): + self.currency_cad.active = True + self.sale_order_1 = self.env["sale.order"].create( + {"partner_id": self.res_partner_1.id, "currency_id": self.currency_eur.id} + ) + self.order_line_1 = self.env["sale.order.line"].create( + { + "order_id": self.sale_order_1.id, + "product_id": self.product_1.id, + "product_uom": self.product_1.uom_id.id, + "product_uom_qty": 10.0, + "price_unit": 10.0, + "tax_id": self.tax, + "currency_id": self.currency_eur.id, + } + ) + self.order_line_2 = self.env["sale.order.line"].create( + { + "order_id": self.sale_order_1.id, + "product_id": self.product_2.id, + "product_uom": self.product_2.uom_id.id, + "product_uom_qty": 25.0, + "price_unit": 4.0, + "tax_id": self.tax, + "currency_id": self.currency_eur.id, + } + ) + self.order_line_3 = self.env["sale.order.line"].create( + { + "order_id": self.sale_order_1.id, + "product_id": self.product_3.id, + "product_uom": self.product_3.uom_id.id, + "product_uom_qty": 20.0, + "price_unit": 5.0, + "tax_id": self.tax, + "currency_id": self.currency_eur.id, + } + ) + + self.assertEqual( + self.sale_order_1.amount_invoiced, + 0.0, + "Invoiced Amount should be 0.0", + ) + self.sale_order_1.action_confirm() + self.sale_order_1.currency_id = self.currency_eur + price_foreign_currency_1 = self.sale_order_1.currency_id._convert( + 10.0, + self.currency_cad, + self.sale_order_1.company_id, + fields.Date.from_string("2024-01-01"), + ) + price_foreign_currency_2 = self.sale_order_1.currency_id._convert( + 4.0, + self.currency_cad, + self.sale_order_1.company_id, + fields.Date.from_string("2024-01-01"), + ) + aml1 = self.order_line_1._prepare_invoice_line( + **{ + "price_unit": price_foreign_currency_1, + "currency_id": self.currency_cad.id, + } + ) + aml2 = self.order_line_2._prepare_invoice_line( + **{ + "price_unit": price_foreign_currency_2, + "currency_id": self.currency_cad.id, + } + ) + test_invoice = self.env["account.move"].create( + { + "move_type": "out_invoice", + "invoice_date": fields.Date.from_string("2024-01-01"), + "date": fields.Date.from_string("2024-01-01"), + "partner_id": self.res_partner_1.id, + "line_ids": [ + ( + 0, + 0, + aml1, + ), + ( + 0, + 0, + aml2, + ), + ], + "currency_id": self.currency_cad.id, + } + ) + test_invoice.action_post() + self.assertAlmostEqual(self.sale_order_1.amount_invoiced, 242.0, delta=0.2) + self.assertEqual( + self.sale_order_1.amount_to_invoice, + 121.0, + "Uninvoiced Amount should be 121, as the lines keep uninvoiced.", + ) + test_invoice.button_cancel() + price_foreign_currency_1 = self.sale_order_1.currency_id._convert( + 10.0, + self.currency_cad, + self.sale_order_1.company_id, + fields.Date.from_string("2024-01-01"), + ) + price_foreign_currency_2 = self.sale_order_1.currency_id._convert( + 4.0, + self.currency_cad, + self.sale_order_1.company_id, + fields.Date.from_string("2024-01-01"), + ) + price_foreign_currency_3 = self.sale_order_1.currency_id._convert( + 5.0, + self.currency_cad, + self.sale_order_1.company_id, + fields.Date.from_string("2024-01-01"), + ) + aml1 = self.order_line_1._prepare_invoice_line( + **{ + "price_unit": price_foreign_currency_1, + "currency_id": self.currency_cad.id, + } + ) + aml2 = self.order_line_2._prepare_invoice_line( + **{ + "price_unit": price_foreign_currency_2, + "currency_id": self.currency_cad.id, + } + ) + aml3 = self.order_line_3._prepare_invoice_line( + **{ + "price_unit": price_foreign_currency_3, + "currency_id": self.currency_cad.id, + } + ) + test_invoice = self.env["account.move"].create( + [ + { + "move_type": "out_invoice", + "invoice_date": fields.Date.from_string("2024-01-01"), + "date": fields.Date.from_string("2024-01-01"), + "partner_id": self.res_partner_1.id, + "line_ids": [ + ( + 0, + 0, + aml1, + ), + ( + 0, + 0, + aml2, + ), + ( + 0, + 0, + aml3, + ), + ], + "currency_id": self.currency_cad.id, + } + ] + ) + test_invoice.action_post() + self.assertAlmostEqual(self.sale_order_1.amount_invoiced, 363.0, delta=0.2) + self.assertEqual( + self.sale_order_1.amount_to_invoice, + 0.0, + "Uninvoiced Amount should be calculated.", + ) + + def test_05_sale_order_invoiced(self): + self.assertEqual( + self.sale_order_1.amount_invoiced, + 0.0, + "Invoiced Amount should be 0.0", + ) + + self.sale_order_1.action_confirm() + aml1 = self.order_line_1._prepare_invoice_line() + aml1["price_unit"] = 15.0 + aml1["quantity"] = 5.0 + aml2 = self.order_line_2._prepare_invoice_line() + test_invoice = self.env["account.move"].create( + { + "move_type": "out_invoice", + "invoice_date": fields.Date.from_string("2024-01-01"), + "date": fields.Date.from_string("2024-01-01"), + "partner_id": self.res_partner_1.id, + "line_ids": [ + ( + 0, + 0, + aml1, + ), + ( + 0, + 0, + aml2, + ), + ], + } + ) + test_invoice.action_post() + self.assertEqual( + self.sale_order_1.amount_invoiced, + 211.75, + "Invoiced Amount should be 211.75", + ) + self.assertEqual( + self.sale_order_1.amount_to_invoice, + 181.5, + "Uninvoiced Amount should be 181.5, as the lines keep uninvoiced.", + ) diff --git a/sale_order_invoice_amount/views/sale_order_view.xml b/sale_order_invoice_amount/views/sale_order_view.xml new file mode 100644 index 00000000000..45bec53dbee --- /dev/null +++ b/sale_order_invoice_amount/views/sale_order_view.xml @@ -0,0 +1,33 @@ + + + + sale.order.tree.amount_invoiced + sale.order + + + + + + + + + + quotation.tree.invoiced_amount + sale.order + + + + + + + + +