From 91f3290f9b536568386e72bf75dd75294dd5d2c1 Mon Sep 17 00:00:00 2001 From: Yoshi Tashiro Date: Tue, 31 Dec 2024 14:29:50 +0000 Subject: [PATCH 1/2] [ADD] account_tax_round_down 15.0 --- account_tax_round_down/README.rst | 80 ++++ account_tax_round_down/__init__.py | 1 + account_tax_round_down/__manifest__.py | 15 + account_tax_round_down/i18n/ja.po | 65 +++ account_tax_round_down/models/__init__.py | 5 + account_tax_round_down/models/account_move.py | 28 ++ account_tax_round_down/models/account_tax.py | 56 +++ account_tax_round_down/models/res_company.py | 13 + .../models/res_config_settings.py | 13 + account_tax_round_down/models/res_currency.py | 18 + account_tax_round_down/readme/CONFIGURE.md | 3 + account_tax_round_down/readme/DESCRIPTION.md | 17 + .../static/description/index.html | 430 ++++++++++++++++++ account_tax_round_down/tests/__init__.py | 1 + .../tests/test_account_tax_round_down.py | 108 +++++ .../views/res_config_settings_views.xml | 30 ++ .../odoo/addons/account_tax_round_down | 1 + setup/account_tax_round_down/setup.py | 6 + 18 files changed, 890 insertions(+) create mode 100644 account_tax_round_down/README.rst create mode 100644 account_tax_round_down/__init__.py create mode 100644 account_tax_round_down/__manifest__.py create mode 100644 account_tax_round_down/i18n/ja.po create mode 100644 account_tax_round_down/models/__init__.py create mode 100644 account_tax_round_down/models/account_move.py create mode 100644 account_tax_round_down/models/account_tax.py create mode 100644 account_tax_round_down/models/res_company.py create mode 100644 account_tax_round_down/models/res_config_settings.py create mode 100644 account_tax_round_down/models/res_currency.py create mode 100644 account_tax_round_down/readme/CONFIGURE.md create mode 100644 account_tax_round_down/readme/DESCRIPTION.md create mode 100644 account_tax_round_down/static/description/index.html create mode 100644 account_tax_round_down/tests/__init__.py create mode 100644 account_tax_round_down/tests/test_account_tax_round_down.py create mode 100644 account_tax_round_down/views/res_config_settings_views.xml create mode 120000 setup/account_tax_round_down/odoo/addons/account_tax_round_down create mode 100644 setup/account_tax_round_down/setup.py diff --git a/account_tax_round_down/README.rst b/account_tax_round_down/README.rst new file mode 100644 index 0000000..2626ca1 --- /dev/null +++ b/account_tax_round_down/README.rst @@ -0,0 +1,80 @@ +====================== +Account Tax Round Down +====================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:292bfb7523858a769a7b92123ccea8654a6d66bb6debe0566d10194e8bba2f56 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-qrtl%2Fmi7--custom-lightgray.png?logo=github + :target: https://github.com/qrtl/mi7-custom/tree/15.0/account_tax_round_down + :alt: qrtl/mi7-custom + +|badge1| |badge2| |badge3| + +This module does the following: + +- Provides the function of rounding down the tax amount in the invoice, + covering: + + - the total presentation in invoice form/print + - the tax amount calculation of account move line + +Note that, due to the structure of compute_all() method, the round-down +does not work perfectly in case the document involves multiple taxes +with different price_include settings (which is not expected to happen +under normal circumstances in the Japanese business environment). + +Background: +----------- + +In Japan there is sometimes a tacit industry-wide convention of rounding +down the tax amount instead of applying the default rounding +("HALF-UP"). + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Go to Accounting (or Invoicing) > Configuration > Settings, and select +"Round-down Tax Amounts" for companies where tax amounts should be +rownded down (selection is done by default). + +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 +------- + +* Quartile Limited + +Maintainers +----------- + +This module is part of the `qrtl/mi7-custom `_ project on GitHub. + +You are welcome to contribute. diff --git a/account_tax_round_down/__init__.py b/account_tax_round_down/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/account_tax_round_down/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_tax_round_down/__manifest__.py b/account_tax_round_down/__manifest__.py new file mode 100644 index 0000000..a29a558 --- /dev/null +++ b/account_tax_round_down/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2022 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Account Tax Round Down", + "version": "15.0.1.0.0", + "category": "Tools", + "license": "AGPL-3", + "author": "Quartile Limited", + "website": "https://www.quartile.co", + "depends": ["account"], + "data": [ + "views/res_config_settings_views.xml", + ], + "installable": True, +} diff --git a/account_tax_round_down/i18n/ja.po b/account_tax_round_down/i18n/ja.po new file mode 100644 index 0000000..0ea7526 --- /dev/null +++ b/account_tax_round_down/i18n/ja.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_tax_round_down +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-09-19 06:28+0000\n" +"PO-Revision-Date: 2022-09-19 06:28+0000\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_tax_round_down +#: model_terms:ir.ui.view,arch_db:account_tax_round_down.res_config_settings_view_form +msgid "" +"Round-down Tax Amounts\n" +" " +msgstr "" + +#. module: account_tax_round_down +#: model_terms:ir.ui.view,arch_db:account_tax_round_down.res_config_settings_view_form +msgid "Apply rounding method 'DOWN' to tax amounts" +msgstr "税額に丸め方法「切捨」を適用" + +#. module: account_tax_round_down +#: model:ir.model,name:account_tax_round_down.model_res_company +msgid "Companies" +msgstr "会社" + +#. module: account_tax_round_down +#: model:ir.model,name:account_tax_round_down.model_res_config_settings +msgid "Config Settings" +msgstr "コンフィグ設定" + +#. module: account_tax_round_down +#: model:ir.model,name:account_tax_round_down.model_res_currency +msgid "Currency" +msgstr "通貨" + +#. module: account_tax_round_down +#: model:ir.model.fields,help:account_tax_round_down.field_res_company__need_tax_round_down +#: model:ir.model.fields,help:account_tax_round_down.field_res_config_settings__need_tax_round_down +msgid "If selected, rounding method 'DOWN' will be applied to tax amounts." +msgstr "選択した場合、税額の丸め方法に切捨を適用します。" + +#. module: account_tax_round_down +#: model:ir.model,name:account_tax_round_down.model_account_move +msgid "Journal Entry" +msgstr "仕訳" + +#. module: account_tax_round_down +#: model:ir.model.fields,field_description:account_tax_round_down.field_res_company__need_tax_round_down +#: model:ir.model.fields,field_description:account_tax_round_down.field_res_config_settings__need_tax_round_down +msgid "Need Tax Round Down" +msgstr "税丸め切捨適用対象" + +#. module: account_tax_round_down +#: model:ir.model,name:account_tax_round_down.model_account_tax +msgid "Tax" +msgstr "税" diff --git a/account_tax_round_down/models/__init__.py b/account_tax_round_down/models/__init__.py new file mode 100644 index 0000000..186e452 --- /dev/null +++ b/account_tax_round_down/models/__init__.py @@ -0,0 +1,5 @@ +from . import account_move +from . import account_tax +from . import res_company +from . import res_config_settings +from . import res_currency diff --git a/account_tax_round_down/models/account_move.py b/account_tax_round_down/models/account_move.py new file mode 100644 index 0000000..9d8bad1 --- /dev/null +++ b/account_tax_round_down/models/account_move.py @@ -0,0 +1,28 @@ +# Copyright 2022 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class AccountMove(models.Model): + _inherit = "account.move" + + # For journal entries + def _recompute_tax_lines( + self, recompute_tax_base_amount=False, tax_rep_lines_to_recompute=None + ): + self.ensure_one() + if self.company_id.need_tax_round_down: + self = self.with_context(rounding_method="DOWN") + return super()._recompute_tax_lines( + recompute_tax_base_amount=recompute_tax_base_amount, + tax_rep_lines_to_recompute=tax_rep_lines_to_recompute, + ) + + # For invoice form and print total presentation. + # Extending _get_tax_totals() would also work for invoices, however not for sales + # orders. Therefore _compute_tax_totals_json() is extended for consistency reason. + def _compute_tax_totals_json(self): + if self.env.company.need_tax_round_down: + self = self.with_context(rounding_method="DOWN") + return super()._compute_tax_totals_json() diff --git a/account_tax_round_down/models/account_tax.py b/account_tax_round_down/models/account_tax.py new file mode 100644 index 0000000..3a3596b --- /dev/null +++ b/account_tax_round_down/models/account_tax.py @@ -0,0 +1,56 @@ +# Copyright 2022 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class AccountTax(models.Model): + _inherit = "account.tax" + + # This adjustment also affects the tax-inclusive price presentation in the eCommerce + # shop. + def compute_all( + self, + price_unit, + currency=None, + quantity=1.0, + product=None, + partner=None, + is_refund=False, + handle_price_include=True, + include_caba_tags=False, + ): + if not self: + company = self.env.company + else: + company = self[0].company_id + if company.need_tax_round_down: + rounding_method = "DOWN" + if self.mapped("price_include") == [True]: + # In case all taxes have 'Included in Price' selected, then we reverse + # the rounding method, since the excluded amount is calculated first, + # and then the tax amount is calculated by subtracting the excluded + # amount from the total. + # We cannot cover the pattern where multiple taxes with different + # price_include settings are involved due to the structure of + # compute_all() method. + rounding_method = "UP" + # This matters when currency is NOT given + self = self.with_context(rounding_method=rounding_method) + if currency: + # To avoid unwanted rounding down on price_unit. + currency = currency.with_context(rounding_method="HALF-UP") + price_unit = currency.round(price_unit) + # If currency is given, the context is not passed down via self, + # and it should be assigned here. + currency = currency.with_context(rounding_method=rounding_method) + return super().compute_all( + price_unit, + currency=currency, + quantity=quantity, + product=product, + partner=partner, + is_refund=is_refund, + handle_price_include=handle_price_include, + include_caba_tags=include_caba_tags, + ) diff --git a/account_tax_round_down/models/res_company.py b/account_tax_round_down/models/res_company.py new file mode 100644 index 0000000..c6e9e2a --- /dev/null +++ b/account_tax_round_down/models/res_company.py @@ -0,0 +1,13 @@ +# Copyright 2022 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + need_tax_round_down = fields.Boolean( + default=True, + help="If selected, rounding method 'DOWN' will be applied to tax amounts.", + ) diff --git a/account_tax_round_down/models/res_config_settings.py b/account_tax_round_down/models/res_config_settings.py new file mode 100644 index 0000000..343131e --- /dev/null +++ b/account_tax_round_down/models/res_config_settings.py @@ -0,0 +1,13 @@ +# Copyright 2022 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + need_tax_round_down = fields.Boolean( + related="company_id.need_tax_round_down", + readonly=False, + ) diff --git a/account_tax_round_down/models/res_currency.py b/account_tax_round_down/models/res_currency.py new file mode 100644 index 0000000..bbb9230 --- /dev/null +++ b/account_tax_round_down/models/res_currency.py @@ -0,0 +1,18 @@ +# Copyright 2022 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models, tools + + +class Currency(models.Model): + _inherit = "res.currency" + + def round(self, amount): + self.ensure_one() + if "rounding_method" in self._context: + return tools.float_round( + amount, + precision_rounding=self.rounding, + rounding_method=self._context.get("rounding_method"), + ) + return super().round(amount) diff --git a/account_tax_round_down/readme/CONFIGURE.md b/account_tax_round_down/readme/CONFIGURE.md new file mode 100644 index 0000000..0eb98de --- /dev/null +++ b/account_tax_round_down/readme/CONFIGURE.md @@ -0,0 +1,3 @@ +Go to Accounting (or Invoicing) \> Configuration \> Settings, and select +"Round-down Tax Amounts" for companies where tax amounts should be +rownded down (selection is done by default). diff --git a/account_tax_round_down/readme/DESCRIPTION.md b/account_tax_round_down/readme/DESCRIPTION.md new file mode 100644 index 0000000..54da884 --- /dev/null +++ b/account_tax_round_down/readme/DESCRIPTION.md @@ -0,0 +1,17 @@ +This module does the following: + +- Provides the function of rounding down the tax amount in the invoice, + covering: + - the total presentation in invoice form/print + - the tax amount calculation of account move line + +Note that, due to the structure of compute_all() method, the round-down +does not work perfectly in case the document involves multiple taxes +with different price_include settings (which is not expected to happen +under normal circumstances in the Japanese business environment). + +## Background: + +In Japan there is sometimes a tacit industry-wide convention of rounding +down the tax amount instead of applying the default rounding +("HALF-UP"). diff --git a/account_tax_round_down/static/description/index.html b/account_tax_round_down/static/description/index.html new file mode 100644 index 0000000..49d54c9 --- /dev/null +++ b/account_tax_round_down/static/description/index.html @@ -0,0 +1,430 @@ + + + + + + +Account Tax Round Down + + + +
+

Account Tax Round Down

+ + +

Beta License: AGPL-3 qrtl/mi7-custom

+

This module does the following:

+
    +
  • Provides the function of rounding down the tax amount in the invoice, +covering:
      +
    • the total presentation in invoice form/print
    • +
    • the tax amount calculation of account move line
    • +
    +
  • +
+

Note that, due to the structure of compute_all() method, the round-down +does not work perfectly in case the document involves multiple taxes +with different price_include settings (which is not expected to happen +under normal circumstances in the Japanese business environment).

+
+

Background:

+

In Japan there is sometimes a tacit industry-wide convention of rounding +down the tax amount instead of applying the default rounding +(“HALF-UP”).

+

Table of contents

+ +
+

Configuration

+

Go to Accounting (or Invoicing) > Configuration > Settings, and select +“Round-down Tax Amounts” for companies where tax amounts should be +rownded down (selection is done by default).

+
+
+

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.

+
+ +
+
+

Authors

+
    +
  • Quartile Limited
  • +
+
+
+

Maintainers

+

This module is part of the qrtl/mi7-custom project on GitHub.

+

You are welcome to contribute.

+
+
+ + diff --git a/account_tax_round_down/tests/__init__.py b/account_tax_round_down/tests/__init__.py new file mode 100644 index 0000000..5d5c6fe --- /dev/null +++ b/account_tax_round_down/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_tax_round_down diff --git a/account_tax_round_down/tests/test_account_tax_round_down.py b/account_tax_round_down/tests/test_account_tax_round_down.py new file mode 100644 index 0000000..2814d0b --- /dev/null +++ b/account_tax_round_down/tests/test_account_tax_round_down.py @@ -0,0 +1,108 @@ +# Copyright 2022 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestAccountTaxRoundDown(TransactionCase): + @classmethod + def setUpClass(cls): + super(TestAccountTaxRoundDown, cls).setUpClass() + cls.tax = cls.env["account.tax"].create( + { + "name": "Tax 10", + "type_tax_use": "sale", + "amount": 10, + } + ) + cls.company_jpy = cls.env["res.company"].create( + { + "name": "Japan company", + "currency_id": cls.env.ref("base.JPY").id, + "country_id": cls.env.ref("base.jp").id, + } + ) + cls.journal = cls.env["account.journal"].create( + {"code": "test", "name": "test", "type": "sale"} + ) + cls.partner = cls.env.ref("base.res_partner_3") + cls.env.ref("account.data_account_type_receivable") + cls.acc_revenue = cls.env["account.account"].create( + { + "code": "X2022", + "name": "Tax Test", + "user_type_id": cls.env.ref("account.data_account_type_revenue").id, + } + ) + cls.product = cls.env["product.product"].create( + { + "name": "Test", + "categ_id": cls.env.ref("product.product_category_all").id, + "standard_price": 50, + "list_price": 100, + "type": "service", + "uom_id": cls.env.ref("uom.product_uom_unit").id, + "uom_po_id": cls.env.ref("uom.product_uom_unit").id, + "description": "Test", + } + ) + + def _create_invoice(self, journal_id, partner_id, invoice_line_data): + invoice = self.env["account.move"].create( + dict( + name="Test Customer Invoice", + journal_id=journal_id.id, + partner_id=partner_id.id, + invoice_line_ids=invoice_line_data, + move_type="out_invoice", + company_id=self.company_jpy.id, + ) + ) + return invoice + + # Even if we don't install account_get_tax_totals_fix, + # tax rounding is working when we add the tax first and then change price unit. + # That why this test will not be failed. + def test_account_tax_round_down_with_true(self): + settings = self.env["res.config.settings"].create( + {"tax_calculation_rounding_method": "round_globally"} + ) + settings.execute() + invoice_line_data = [ + ( + 0, + 0, + { + "product_id": self.product.id, + "quantity": 1, + "account_id": self.acc_revenue.id, + "name": "product test", + "price_unit": 23456, + "tax_ids": [(6, 0, self.tax.ids)], + }, + ) + ] + invoice = self._create_invoice(self.journal, self.partner, invoice_line_data) + self.assertEqual(invoice.amount_tax, 2345) + self.assertEqual(invoice.amount_total, 25801) + + def test_account_tax_round_down_with_false(self): + self.company_jpy.write({"need_tax_round_down": False}) + invoice_line_data = [ + ( + 0, + 0, + { + "product_id": self.product.id, + "quantity": 1, + "account_id": self.acc_revenue.id, + "name": "product test", + "price_unit": 23456, + "tax_ids": [(6, 0, self.tax.ids)], + }, + ) + ] + + invoice = self._create_invoice(self.journal, self.partner, invoice_line_data) + self.assertEqual(invoice.amount_tax, 2346) + self.assertEqual(invoice.amount_total, 25802) diff --git a/account_tax_round_down/views/res_config_settings_views.xml b/account_tax_round_down/views/res_config_settings_views.xml new file mode 100644 index 0000000..41b31fa --- /dev/null +++ b/account_tax_round_down/views/res_config_settings_views.xml @@ -0,0 +1,30 @@ + + + + res.config.settings.view.form.inherit.account + res.config.settings + + + +
+
+ +
+
+ Round-down Tax Amounts + +
+ Apply rounding method 'DOWN' to tax amounts +
+
+
+
+
+
+
diff --git a/setup/account_tax_round_down/odoo/addons/account_tax_round_down b/setup/account_tax_round_down/odoo/addons/account_tax_round_down new file mode 120000 index 0000000..854ee11 --- /dev/null +++ b/setup/account_tax_round_down/odoo/addons/account_tax_round_down @@ -0,0 +1 @@ +../../../../account_tax_round_down \ No newline at end of file diff --git a/setup/account_tax_round_down/setup.py b/setup/account_tax_round_down/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/account_tax_round_down/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 8a2041d06059ea05ba9024357dfed364ce899291 Mon Sep 17 00:00:00 2001 From: Yoshi Tashiro Date: Wed, 1 Jan 2025 13:50:53 +0000 Subject: [PATCH 2/2] [4986][ADD] account_tax_round_down --- account_tax_round_down/README.rst | 96 ++++++--- account_tax_round_down/__manifest__.py | 8 +- account_tax_round_down/models/account_move.py | 38 ++-- account_tax_round_down/models/account_tax.py | 83 +++++--- account_tax_round_down/models/res_company.py | 4 +- .../models/res_config_settings.py | 2 +- account_tax_round_down/models/res_currency.py | 7 +- account_tax_round_down/readme/CONFIGURE.md | 5 +- account_tax_round_down/readme/CONTEXT.md | 3 + account_tax_round_down/readme/CONTRIBUTORS.md | 2 + account_tax_round_down/readme/DESCRIPTION.md | 19 +- account_tax_round_down/readme/ROADMAP.md | 2 + account_tax_round_down/readme/USAGE.md | 7 + .../static/description/index.html | 103 +++++---- .../tests/test_account_tax_round_down.py | 199 +++++++++++------- 15 files changed, 350 insertions(+), 228 deletions(-) create mode 100644 account_tax_round_down/readme/CONTEXT.md create mode 100644 account_tax_round_down/readme/CONTRIBUTORS.md create mode 100644 account_tax_round_down/readme/ROADMAP.md create mode 100644 account_tax_round_down/readme/USAGE.md diff --git a/account_tax_round_down/README.rst b/account_tax_round_down/README.rst index 2626ca1..cda0ce2 100644 --- a/account_tax_round_down/README.rst +++ b/account_tax_round_down/README.rst @@ -16,51 +16,68 @@ Account Tax Round Down .. |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-qrtl%2Fmi7--custom-lightgray.png?logo=github - :target: https://github.com/qrtl/mi7-custom/tree/15.0/account_tax_round_down - :alt: qrtl/mi7-custom +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github + :target: https://github.com/OCA/account-financial-tools/tree/16.0/account_tax_round_down + :alt: OCA/account-financial-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-financial-tools-16-0/account-financial-tools-16-0-account_tax_round_down + :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-financial-tools&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module provides the function of rounding down the tax amount in +invoices (and also other business documents such as sales orders and +purchase orders). -|badge1| |badge2| |badge3| +**Table of contents** -This module does the following: +.. contents:: + :local: -- Provides the function of rounding down the tax amount in the invoice, - covering: +Use Cases / Context +=================== - - the total presentation in invoice form/print - - the tax amount calculation of account move line +By default, Odoo uses the "HALF-UP" method to round tax amounts. +However, in some regions, such as Japan, some industries have a common +practice of rounding down instead. This module accommodates such local +conventions by offering an alternate rounding method. -Note that, due to the structure of compute_all() method, the round-down -does not work perfectly in case the document involves multiple taxes -with different price_include settings (which is not expected to happen -under normal circumstances in the Japanese business environment). +Configuration +============= -Background: ------------ +Go to *Accounting (or Invoicing) > Configuration > Settings*, and select +"Round-down Tax Amounts" for companies whose tax amounts should be +rounded down. -In Japan there is sometimes a tacit industry-wide convention of rounding -down the tax amount instead of applying the default rounding -("HALF-UP"). +Usage +===== -**Table of contents** +With the round-down setting enabled, if you create an invoice in JPY +with the following line: -.. contents:: - :local: +- Quantity: 1 +- Unit Price: 15 +- Tax: 10% (excluded) -Configuration -============= +the calculated tax amount will be 1 instead of 2. + +Known issues / Roadmap +====================== -Go to Accounting (or Invoicing) > Configuration > Settings, and select -"Round-down Tax Amounts" for companies where tax amounts should be -rownded down (selection is done by default). +Due to the structure of the compute_all() method in the tax model, +rounding down may not yield the expected results in cases where +tax-inclusive pricing is used. Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -70,11 +87,28 @@ Credits Authors ------- -* Quartile Limited +* Quartile + +Contributors +------------ + +- Quartile + + - Yoshi Tashiro Maintainers ----------- -This module is part of the `qrtl/mi7-custom `_ project on GitHub. +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-financial-tools `_ project on GitHub. -You are welcome to contribute. +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_tax_round_down/__manifest__.py b/account_tax_round_down/__manifest__.py index a29a558..aa79c50 100644 --- a/account_tax_round_down/__manifest__.py +++ b/account_tax_round_down/__manifest__.py @@ -1,12 +1,12 @@ -# Copyright 2022 Quartile Limited +# Copyright 2022-2024 Quartile # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { "name": "Account Tax Round Down", - "version": "15.0.1.0.0", + "version": "16.0.1.0.0", "category": "Tools", "license": "AGPL-3", - "author": "Quartile Limited", - "website": "https://www.quartile.co", + "website": "https://github.com/OCA/account-financial-tools", + "author": "Quartile, Odoo Community Association (OCA)", "depends": ["account"], "data": [ "views/res_config_settings_views.xml", diff --git a/account_tax_round_down/models/account_move.py b/account_tax_round_down/models/account_move.py index 9d8bad1..a4799dd 100644 --- a/account_tax_round_down/models/account_move.py +++ b/account_tax_round_down/models/account_move.py @@ -1,28 +1,30 @@ -# Copyright 2022 Quartile Limited +# Copyright 2022-2024 Quartile Limited # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from contextlib import contextmanager + from odoo import models class AccountMove(models.Model): _inherit = "account.move" - # For journal entries - def _recompute_tax_lines( - self, recompute_tax_base_amount=False, tax_rep_lines_to_recompute=None + @contextmanager + def _sync_dynamic_line( + self, + existing_key_fname, + needed_vals_fname, + needed_dirty_fname, + line_type, + container, ): - self.ensure_one() - if self.company_id.need_tax_round_down: - self = self.with_context(rounding_method="DOWN") - return super()._recompute_tax_lines( - recompute_tax_base_amount=recompute_tax_base_amount, - tax_rep_lines_to_recompute=tax_rep_lines_to_recompute, - ) - - # For invoice form and print total presentation. - # Extending _get_tax_totals() would also work for invoices, however not for sales - # orders. Therefore _compute_tax_totals_json() is extended for consistency reason. - def _compute_tax_totals_json(self): - if self.env.company.need_tax_round_down: + if line_type == "tax" and self.env.company.need_tax_round_down: self = self.with_context(rounding_method="DOWN") - return super()._compute_tax_totals_json() + with super()._sync_dynamic_line( + existing_key_fname, + needed_vals_fname, + needed_dirty_fname, + line_type, + container, + ) as ret: + yield ret diff --git a/account_tax_round_down/models/account_tax.py b/account_tax_round_down/models/account_tax.py index 3a3596b..7aff893 100644 --- a/account_tax_round_down/models/account_tax.py +++ b/account_tax_round_down/models/account_tax.py @@ -1,14 +1,47 @@ -# Copyright 2022 Quartile Limited +# Copyright 2022-2024 Quartile Limited # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import models +from odoo import api, models class AccountTax(models.Model): _inherit = "account.tax" - # This adjustment also affects the tax-inclusive price presentation in the eCommerce - # shop. + @api.model + def _prepare_tax_totals(self, base_lines, currency, tax_lines=None): + if self.env.company.need_tax_round_down: + self = self.with_context(rounding_method="DOWN") + return super()._prepare_tax_totals(base_lines, currency, tax_lines) + + def _compute_amount( + self, + base_amount, + price_unit, + quantity=1.0, + product=None, + partner=None, + fixed_multiplicator=1, + ): + """Handle the case where round-down is required with the round_per_line setting. + + Due to how tax amount rounding is done inside the compute_all() method under the + round_per_line setting (i.e. float_round() helper function is used instead of + the round() method of the currency model), we want to apply the desired rounding + using the model method beforehand. + """ + amount = super()._compute_amount( + base_amount, price_unit, quantity, product, partner, fixed_multiplicator + ) + company = self.company_id + if ( + company.need_tax_round_down + and company.tax_calculation_rounding_method == "round_per_line" + and self.amount_type == "percent" + ): + currency = self.env.context.get("currency") or company.currency + amount = currency.with_context(rounding_method="DOWN").round(amount) + return amount + def compute_all( self, price_unit, @@ -19,38 +52,18 @@ def compute_all( is_refund=False, handle_price_include=True, include_caba_tags=False, + fixed_multiplicator=1, ): - if not self: - company = self.env.company - else: - company = self[0].company_id - if company.need_tax_round_down: - rounding_method = "DOWN" - if self.mapped("price_include") == [True]: - # In case all taxes have 'Included in Price' selected, then we reverse - # the rounding method, since the excluded amount is calculated first, - # and then the tax amount is calculated by subtracting the excluded - # amount from the total. - # We cannot cover the pattern where multiple taxes with different - # price_include settings are involved due to the structure of - # compute_all() method. - rounding_method = "UP" - # This matters when currency is NOT given - self = self.with_context(rounding_method=rounding_method) - if currency: - # To avoid unwanted rounding down on price_unit. - currency = currency.with_context(rounding_method="HALF-UP") - price_unit = currency.round(price_unit) - # If currency is given, the context is not passed down via self, - # and it should be assigned here. - currency = currency.with_context(rounding_method=rounding_method) + # Just to pass the currency context used in _compute_amount() + self = self.with_context(currency=currency) return super().compute_all( price_unit, - currency=currency, - quantity=quantity, - product=product, - partner=partner, - is_refund=is_refund, - handle_price_include=handle_price_include, - include_caba_tags=include_caba_tags, + currency, + quantity, + product, + partner, + is_refund, + handle_price_include, + include_caba_tags, + fixed_multiplicator, ) diff --git a/account_tax_round_down/models/res_company.py b/account_tax_round_down/models/res_company.py index c6e9e2a..9cd5da3 100644 --- a/account_tax_round_down/models/res_company.py +++ b/account_tax_round_down/models/res_company.py @@ -1,4 +1,4 @@ -# Copyright 2022 Quartile Limited +# Copyright 2022 Quartile # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import fields, models @@ -8,6 +8,6 @@ class ResCompany(models.Model): _inherit = "res.company" need_tax_round_down = fields.Boolean( - default=True, + default=False, help="If selected, rounding method 'DOWN' will be applied to tax amounts.", ) diff --git a/account_tax_round_down/models/res_config_settings.py b/account_tax_round_down/models/res_config_settings.py index 343131e..6c4e860 100644 --- a/account_tax_round_down/models/res_config_settings.py +++ b/account_tax_round_down/models/res_config_settings.py @@ -1,4 +1,4 @@ -# Copyright 2022 Quartile Limited +# Copyright 2022 Quartile # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import fields, models diff --git a/account_tax_round_down/models/res_currency.py b/account_tax_round_down/models/res_currency.py index bbb9230..e990c6b 100644 --- a/account_tax_round_down/models/res_currency.py +++ b/account_tax_round_down/models/res_currency.py @@ -1,4 +1,4 @@ -# Copyright 2022 Quartile Limited +# Copyright 2022 Quartile # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import models, tools @@ -9,10 +9,11 @@ class Currency(models.Model): def round(self, amount): self.ensure_one() - if "rounding_method" in self._context: + rounding_method = self.env.context.get("rounding_method") + if rounding_method: return tools.float_round( amount, precision_rounding=self.rounding, - rounding_method=self._context.get("rounding_method"), + rounding_method=rounding_method, ) return super().round(amount) diff --git a/account_tax_round_down/readme/CONFIGURE.md b/account_tax_round_down/readme/CONFIGURE.md index 0eb98de..35f0906 100644 --- a/account_tax_round_down/readme/CONFIGURE.md +++ b/account_tax_round_down/readme/CONFIGURE.md @@ -1,3 +1,2 @@ -Go to Accounting (or Invoicing) \> Configuration \> Settings, and select -"Round-down Tax Amounts" for companies where tax amounts should be -rownded down (selection is done by default). +Go to *Accounting (or Invoicing) \> Configuration \> Settings*, and select "Round-down +Tax Amounts" for companies whose tax amounts should be rounded down. diff --git a/account_tax_round_down/readme/CONTEXT.md b/account_tax_round_down/readme/CONTEXT.md new file mode 100644 index 0000000..0c18a49 --- /dev/null +++ b/account_tax_round_down/readme/CONTEXT.md @@ -0,0 +1,3 @@ +By default, Odoo uses the "HALF-UP" method to round tax amounts. However, in some +regions, such as Japan, some industries have a common practice of rounding down instead. +This module accommodates such local conventions by offering an alternate rounding method. diff --git a/account_tax_round_down/readme/CONTRIBUTORS.md b/account_tax_round_down/readme/CONTRIBUTORS.md new file mode 100644 index 0000000..5ce4a82 --- /dev/null +++ b/account_tax_round_down/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Quartile \<\> + - Yoshi Tashiro diff --git a/account_tax_round_down/readme/DESCRIPTION.md b/account_tax_round_down/readme/DESCRIPTION.md index 54da884..8a2fb48 100644 --- a/account_tax_round_down/readme/DESCRIPTION.md +++ b/account_tax_round_down/readme/DESCRIPTION.md @@ -1,17 +1,2 @@ -This module does the following: - -- Provides the function of rounding down the tax amount in the invoice, - covering: - - the total presentation in invoice form/print - - the tax amount calculation of account move line - -Note that, due to the structure of compute_all() method, the round-down -does not work perfectly in case the document involves multiple taxes -with different price_include settings (which is not expected to happen -under normal circumstances in the Japanese business environment). - -## Background: - -In Japan there is sometimes a tacit industry-wide convention of rounding -down the tax amount instead of applying the default rounding -("HALF-UP"). +This module provides the function of rounding down the tax amount in invoices (and also +other business documents such as sales orders and purchase orders). diff --git a/account_tax_round_down/readme/ROADMAP.md b/account_tax_round_down/readme/ROADMAP.md new file mode 100644 index 0000000..d51f781 --- /dev/null +++ b/account_tax_round_down/readme/ROADMAP.md @@ -0,0 +1,2 @@ +Due to the structure of the compute_all() method in the tax model, rounding down may +not yield the expected results in cases where tax-inclusive pricing is used. diff --git a/account_tax_round_down/readme/USAGE.md b/account_tax_round_down/readme/USAGE.md new file mode 100644 index 0000000..cf190fd --- /dev/null +++ b/account_tax_round_down/readme/USAGE.md @@ -0,0 +1,7 @@ +With the round-down setting enabled, if you create an invoice in JPY with the following line: + +- Quantity: 1 +- Unit Price: 15 +- Tax: 10% (excluded) + +the calculated tax amount will be 1 instead of 2. diff --git a/account_tax_round_down/static/description/index.html b/account_tax_round_down/static/description/index.html index 49d54c9..41e6023 100644 --- a/account_tax_round_down/static/description/index.html +++ b/account_tax_round_down/static/description/index.html @@ -1,4 +1,3 @@ - @@ -369,61 +368,91 @@

Account Tax Round Down

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:292bfb7523858a769a7b92123ccea8654a6d66bb6debe0566d10194e8bba2f56 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 qrtl/mi7-custom

-

This module does the following:

+

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

+

This module provides the function of rounding down the tax amount in +invoices (and also other business documents such as sales orders and +purchase orders).

+

Table of contents

+
    -
  • Provides the function of rounding down the tax amount in the invoice, -covering: -

    Note that, due to the structure of compute_all() method, the round-down -does not work perfectly in case the document involves multiple taxes -with different price_include settings (which is not expected to happen -under normal circumstances in the Japanese business environment).

    -
    -

    Background:

    -

    In Japan there is sometimes a tacit industry-wide convention of rounding -down the tax amount instead of applying the default rounding -(“HALF-UP”).

    -

    Table of contents

    -
    +
    +
    +

    Use Cases / Context

    +

    By default, Odoo uses the “HALF-UP” method to round tax amounts. +However, in some regions, such as Japan, some industries have a common +practice of rounding down instead. This module accommodates such local +conventions by offering an alternate rounding method.

    +
    +
    +

    Configuration

    +

    Go to Accounting (or Invoicing) > Configuration > Settings, and select +“Round-down Tax Amounts” for companies whose tax amounts should be +rounded down.

    +
    +
    +

    Usage

    +

    With the round-down setting enabled, if you create an invoice in JPY +with the following line:

    +

    the calculated tax amount will be 1 instead of 2.

    -
    -

    Configuration

    -

    Go to Accounting (or Invoicing) > Configuration > Settings, and select -“Round-down Tax Amounts” for companies where tax amounts should be -rownded down (selection is done by default).

    +
    +

    Known issues / Roadmap

    +

    Due to the structure of the compute_all() method in the tax model, +rounding down may not yield the expected results in cases where +tax-inclusive pricing is used.

    -

    Bug Tracker

    -

    Bugs are tracked on GitHub Issues. +

    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.

    +feedback.

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

    -
    +

    Credits

    -

    Authors

    +

    Authors

    +
      +
    • Quartile
    • +
    +
    +
    +

    Contributors

    -

    Maintainers

    -

    This module is part of the qrtl/mi7-custom project on GitHub.

    -

    You are welcome to contribute.

    +

    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-financial-tools project on GitHub.

    +

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

    +
diff --git a/account_tax_round_down/tests/test_account_tax_round_down.py b/account_tax_round_down/tests/test_account_tax_round_down.py index 2814d0b..a85a956 100644 --- a/account_tax_round_down/tests/test_account_tax_round_down.py +++ b/account_tax_round_down/tests/test_account_tax_round_down.py @@ -1,108 +1,153 @@ -# Copyright 2022 Quartile Limited +# Copyright 2022-2024 Quartile # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import Command, fields from odoo.tests.common import TransactionCase class TestAccountTaxRoundDown(TransactionCase): @classmethod def setUpClass(cls): - super(TestAccountTaxRoundDown, cls).setUpClass() - cls.tax = cls.env["account.tax"].create( + super().setUpClass() + cls.company = cls.env["res.company"].create( { - "name": "Tax 10", - "type_tax_use": "sale", - "amount": 10, + "name": "test company", + "currency_id": cls.env.ref("base.JPY").id, + "country_id": cls.env.ref("base.jp").id, + "tax_calculation_rounding_method": "round_per_line", + "need_tax_round_down": False, } ) - cls.company_jpy = cls.env["res.company"].create( + cls.env.company = cls.company + cls.tax = cls.env["account.tax"].create( { - "name": "Japan company", - "currency_id": cls.env.ref("base.JPY").id, + "name": "tax 10", + "type_tax_use": "sale", + "amount": 10, "country_id": cls.env.ref("base.jp").id, } ) cls.journal = cls.env["account.journal"].create( {"code": "test", "name": "test", "type": "sale"} ) - cls.partner = cls.env.ref("base.res_partner_3") - cls.env.ref("account.data_account_type_receivable") - cls.acc_revenue = cls.env["account.account"].create( + cls.account_income = cls.env["account.account"].create( { - "code": "X2022", - "name": "Tax Test", - "user_type_id": cls.env.ref("account.data_account_type_revenue").id, + "code": "test1", + "name": "income", + "account_type": "income", } ) - cls.product = cls.env["product.product"].create( + account_receivable = cls.env["account.account"].create( { - "name": "Test", - "categ_id": cls.env.ref("product.product_category_all").id, - "standard_price": 50, - "list_price": 100, - "type": "service", - "uom_id": cls.env.ref("uom.product_uom_unit").id, - "uom_po_id": cls.env.ref("uom.product_uom_unit").id, - "description": "Test", + "code": "test2", + "name": "receivable", + "reconcile": True, + "account_type": "asset_receivable", + } + ) + cls.partner = cls.env["res.partner"].create( + { + "name": "test partner", + "property_account_receivable_id": account_receivable.id, } ) - def _create_invoice(self, journal_id, partner_id, invoice_line_data): - invoice = self.env["account.move"].create( - dict( - name="Test Customer Invoice", - journal_id=journal_id.id, - partner_id=partner_id.id, - invoice_line_ids=invoice_line_data, - move_type="out_invoice", - company_id=self.company_jpy.id, + def _prepare_invoice_line_vals(self, price_unit=15): + return { + "name": "test", + "account_id": self.account_income.id, + "quantity": 1, + "price_unit": price_unit, + "tax_ids": [Command.set(self.tax.ids)], + } + + def _create_invoice(self): + return ( + self.env["account.move"] + .with_company(self.company) + .create( + { + "move_type": "out_invoice", + "invoice_date": fields.Date.today(), + "partner_id": self.partner.id, + "currency_id": self.env.ref("base.JPY").id, + "invoice_line_ids": [ + Command.create(self._prepare_invoice_line_vals()) + ], + } ) ) - return invoice - # Even if we don't install account_get_tax_totals_fix, - # tax rounding is working when we add the tax first and then change price unit. - # That why this test will not be failed. - def test_account_tax_round_down_with_true(self): - settings = self.env["res.config.settings"].create( - {"tax_calculation_rounding_method": "round_globally"} + def test_round_per_line_round_down_false(self): + company = self.company + self.assertEqual(company.tax_calculation_rounding_method, "round_per_line") + self.assertEqual(company.need_tax_round_down, False) + invoice = self._create_invoice() + self.assertEqual(invoice.amount_tax, 2) + self.assertEqual(invoice.amount_total, 17) + invoice.write( + { + "invoice_line_ids": [Command.create(self._prepare_invoice_line_vals())], + } ) - settings.execute() - invoice_line_data = [ - ( - 0, - 0, - { - "product_id": self.product.id, - "quantity": 1, - "account_id": self.acc_revenue.id, - "name": "product test", - "price_unit": 23456, - "tax_ids": [(6, 0, self.tax.ids)], - }, - ) - ] - invoice = self._create_invoice(self.journal, self.partner, invoice_line_data) - self.assertEqual(invoice.amount_tax, 2345) - self.assertEqual(invoice.amount_total, 25801) + self.assertEqual(invoice.amount_tax, 4) + self.assertEqual(invoice.amount_total, 34) - def test_account_tax_round_down_with_false(self): - self.company_jpy.write({"need_tax_round_down": False}) - invoice_line_data = [ - ( - 0, - 0, - { - "product_id": self.product.id, - "quantity": 1, - "account_id": self.acc_revenue.id, - "name": "product test", - "price_unit": 23456, - "tax_ids": [(6, 0, self.tax.ids)], - }, - ) - ] + def test_round_per_line_round_down_true(self): + company = self.company + self.assertEqual(company.tax_calculation_rounding_method, "round_per_line") + company.need_tax_round_down = True + invoice = self._create_invoice() + self.assertEqual(invoice.amount_tax, 1) + self.assertEqual(invoice.amount_total, 16) + invoice.write( + { + "invoice_line_ids": [Command.create(self._prepare_invoice_line_vals())], + } + ) + self.assertEqual(invoice.amount_tax, 2) + self.assertEqual(invoice.amount_total, 32) + + def test_round_globally_round_down_false(self): + company = self.company + company.tax_calculation_rounding_method = "round_globally" + self.assertEqual(company.need_tax_round_down, False) + invoice = self._create_invoice() + self.assertEqual(invoice.amount_tax, 2) + self.assertEqual(invoice.amount_total, 17) + invoice.write( + { + "invoice_line_ids": [Command.create(self._prepare_invoice_line_vals())], + } + ) + self.assertEqual(invoice.amount_tax, 3) + self.assertEqual(invoice.amount_total, 33) + invoice.write( + { + "invoice_line_ids": [Command.create(self._prepare_invoice_line_vals())], + } + ) + self.assertEqual(invoice.amount_tax, 5) + self.assertEqual(invoice.amount_total, 50) - invoice = self._create_invoice(self.journal, self.partner, invoice_line_data) - self.assertEqual(invoice.amount_tax, 2346) - self.assertEqual(invoice.amount_total, 25802) + def test_round_globally_round_down_true(self): + company = self.company + company.tax_calculation_rounding_method = "round_globally" + company.need_tax_round_down = True + invoice = self._create_invoice() + self.assertEqual(invoice.amount_tax, 1) + self.assertEqual(invoice.amount_total, 16) + invoice.write( + { + "invoice_line_ids": [Command.create(self._prepare_invoice_line_vals())], + } + ) + self.assertEqual(invoice.amount_tax, 3) + self.assertEqual(invoice.amount_total, 33) + invoice.write( + { + "invoice_line_ids": [Command.create(self._prepare_invoice_line_vals())], + } + ) + self.assertEqual(invoice.amount_tax, 4) + self.assertEqual(invoice.amount_total, 49)