From 731dd336a85d70d554793cb040e8e7ec12ba0864 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Tue, 9 Apr 2024 15:34:45 -0300 Subject: [PATCH] [ADD] l10n_br_account_withholding: add new module --- l10n_br_account_withholding/README.rst | 152 ++++++ l10n_br_account_withholding/__init__.py | 1 + l10n_br_account_withholding/__manifest__.py | 25 + .../demo/account_move.xml | 12 + .../demo/account_move_line.xml | 12 + .../demo/l10n_br_fiscal_tax_group.xml | 12 + .../models/__init__.py | 3 + .../models/account_move.py | 177 ++++++ .../models/account_move_line.py | 31 ++ .../models/l10n_br_fiscal_tax_group.py | 22 + .../readme/CONFIGURE.md | 5 + l10n_br_account_withholding/readme/CONTEXT.md | 11 + .../readme/CONTRIBUTORS.md | 8 + .../readme/DESCRIPTION.md | 1 + l10n_br_account_withholding/readme/INSTALL.md | 6 + l10n_br_account_withholding/readme/ROADMAP.md | 0 l10n_br_account_withholding/readme/USAGE.md | 5 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 505 ++++++++++++++++++ l10n_br_account_withholding/tests/__init__.py | 1 + .../tests/test_account_wh_invoice.py | 312 +++++++++++ .../views/account_move.xml | 48 ++ .../views/account_move_line.xml | 30 ++ .../views/l10n_br_fiscal_tax_group.xml | 41 ++ .../odoo/addons/l10n_br_account_withholding | 1 + setup/l10n_br_account_withholding/setup.py | 6 + 26 files changed, 1427 insertions(+) create mode 100644 l10n_br_account_withholding/README.rst create mode 100644 l10n_br_account_withholding/__init__.py create mode 100644 l10n_br_account_withholding/__manifest__.py create mode 100644 l10n_br_account_withholding/demo/account_move.xml create mode 100644 l10n_br_account_withholding/demo/account_move_line.xml create mode 100644 l10n_br_account_withholding/demo/l10n_br_fiscal_tax_group.xml create mode 100644 l10n_br_account_withholding/models/__init__.py create mode 100644 l10n_br_account_withholding/models/account_move.py create mode 100644 l10n_br_account_withholding/models/account_move_line.py create mode 100644 l10n_br_account_withholding/models/l10n_br_fiscal_tax_group.py create mode 100644 l10n_br_account_withholding/readme/CONFIGURE.md create mode 100644 l10n_br_account_withholding/readme/CONTEXT.md create mode 100644 l10n_br_account_withholding/readme/CONTRIBUTORS.md create mode 100644 l10n_br_account_withholding/readme/DESCRIPTION.md create mode 100644 l10n_br_account_withholding/readme/INSTALL.md create mode 100644 l10n_br_account_withholding/readme/ROADMAP.md create mode 100644 l10n_br_account_withholding/readme/USAGE.md create mode 100644 l10n_br_account_withholding/static/description/icon.png create mode 100644 l10n_br_account_withholding/static/description/index.html create mode 100644 l10n_br_account_withholding/tests/__init__.py create mode 100644 l10n_br_account_withholding/tests/test_account_wh_invoice.py create mode 100644 l10n_br_account_withholding/views/account_move.xml create mode 100644 l10n_br_account_withholding/views/account_move_line.xml create mode 100644 l10n_br_account_withholding/views/l10n_br_fiscal_tax_group.xml create mode 120000 setup/l10n_br_account_withholding/odoo/addons/l10n_br_account_withholding create mode 100644 setup/l10n_br_account_withholding/setup.py diff --git a/l10n_br_account_withholding/README.rst b/l10n_br_account_withholding/README.rst new file mode 100644 index 000000000000..bccdfec9dfb8 --- /dev/null +++ b/l10n_br_account_withholding/README.rst @@ -0,0 +1,152 @@ +=========================== +L10n Br Account Withholding +=========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:e52d436b428bc771a6019c908363b06b2f47dab04a7d7ccef76c6a57080fb960 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fl10n--brazil-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-brazil/tree/14.0/l10n_br_account_withholding + :alt: OCA/l10n-brazil +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-brazil-14-0/l10n-brazil-14-0-l10n_br_account_withholding + :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/l10n-brazil&target_branch=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Automate your Brazilian tax compliance with the +``l10n_br_account_withholding`` module for Odoo. Streamline creation of +payable invoices for taxes withheld on supplier purchases. Ensure +accuracy and efficiency in managing tax obligations. + +**Table of contents** + +.. contents:: + :local: + +Use Cases / Context +=================== + +Key Actions +----------- + +- **Automate Tax Compliance:** Create payable accounts for taxes + withheld on supplier purchases automatically. +- **Reduce Errors:** Minimize manual entry errors and ensure accuracy + in tax withholdings. +- **Streamline Efficiency:** Enhance your financial processes for + handling tax withholdings from suppliers. + +Application +----------- + +Use this module to: + +- Withhold taxes on payments to suppliers effortlessly. +- Achieve efficient tax compliance and streamlined reporting. + +Installation +============ + +To get started with the ``l10n_br_account_withholding`` module, follow +these simple steps: + +1. **Module Installation:** + + - Navigate to the Odoo Apps dashboard within your Odoo instance. + - Search for ``l10n_br_account_withholding`` in the Apps search bar. + - Click on the ``Install`` button next to the module. Odoo will + handle the installation process automatically. + +Configuration +============= + +Follow these steps to configure tax withholding invoice generation in +Odoo: + +1. **Navigate to Fiscal Settings:** Go to + ``Fiscal -> Configurations -> Tax Groups``. Search for withheld taxes + of the input type. + +2. **Configure Withheld Taxes:** For each tax that requires a + withholding tax invoice, ensure it's properly configured. If a + supplier isn't specified, set one. Also, specify the journal for the + tax invoice generation. If no journal is specified, the module will + use the journal from the originating purchase invoice. + +Usage +===== + +Follow these steps to use tax withholding invoice generation in Odoo: + +1. **Creating a Purchase Invoice:** When creating a purchase invoice, + apply the withheld tax to the necessary lines. + +2. **Invoice Confirmation:** Upon confirming the purchase invoice, the + module automatically generates the withholding tax invoices. + +Known issues / Roadmap +====================== + + + +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 +------- + +* Escodoo +* Akretion + +Contributors +------------ + +- ``Escodoo ``\ \_: + + - Marcel Savegnago marcel.savegnago@escodoo.com.br + +- ``Akretion ``\ \_: + + - Renato Lima renato.lima@akretion.com.br + +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/l10n-brazil `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_br_account_withholding/__init__.py b/l10n_br_account_withholding/__init__.py new file mode 100644 index 000000000000..0650744f6bc6 --- /dev/null +++ b/l10n_br_account_withholding/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/l10n_br_account_withholding/__manifest__.py b/l10n_br_account_withholding/__manifest__.py new file mode 100644 index 000000000000..1d57b15c6e66 --- /dev/null +++ b/l10n_br_account_withholding/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2024 Escodoo +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "L10n Br Account Withholding", + "summary": """ + Brazilian Withholding Invoice Generator""", + "version": "14.0.1.0.0", + "license": "AGPL-3", + "author": "Escodoo,Akretion,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/l10n-brazil", + "depends": [ + "l10n_br_account", + ], + "data": [ + "views/account_move_line.xml", + "views/l10n_br_fiscal_tax_group.xml", + "views/account_move.xml", + ], + "demo": [ + "demo/account_move_line.xml", + "demo/l10n_br_fiscal_tax_group.xml", + "demo/account_move.xml", + ], +} diff --git a/l10n_br_account_withholding/demo/account_move.xml b/l10n_br_account_withholding/demo/account_move.xml new file mode 100644 index 000000000000..8223ad861ba2 --- /dev/null +++ b/l10n_br_account_withholding/demo/account_move.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/l10n_br_account_withholding/demo/account_move_line.xml b/l10n_br_account_withholding/demo/account_move_line.xml new file mode 100644 index 000000000000..78ebb0d396a9 --- /dev/null +++ b/l10n_br_account_withholding/demo/account_move_line.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/l10n_br_account_withholding/demo/l10n_br_fiscal_tax_group.xml b/l10n_br_account_withholding/demo/l10n_br_fiscal_tax_group.xml new file mode 100644 index 000000000000..d32dab5cc87c --- /dev/null +++ b/l10n_br_account_withholding/demo/l10n_br_fiscal_tax_group.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/l10n_br_account_withholding/models/__init__.py b/l10n_br_account_withholding/models/__init__.py new file mode 100644 index 000000000000..e958216983e8 --- /dev/null +++ b/l10n_br_account_withholding/models/__init__.py @@ -0,0 +1,3 @@ +from . import account_move +from . import l10n_br_fiscal_tax_group +from . import account_move_line diff --git a/l10n_br_account_withholding/models/account_move.py b/l10n_br_account_withholding/models/account_move.py new file mode 100644 index 000000000000..7b9d1f597f00 --- /dev/null +++ b/l10n_br_account_withholding/models/account_move.py @@ -0,0 +1,177 @@ +# Copyright 2022 Renato Lima - Akretion +# Copyright 2024 Marcel Savegnago - Escodoo (https://www.escodoo.com.br) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models + +from odoo.addons.l10n_br_account.models.account_move import AccountMove as AccountMoveBR + + +class AccountMove(models.Model): + + _inherit = "account.move" + + wh_invoice_count = fields.Integer( + string="WH Invoice Count", compute="_compute_wh_invoice_ids", readonly=True + ) + wh_invoice_ids = fields.Many2many( + comodel_name="account.move", + string="WH Invoices", + compute="_compute_wh_invoice_ids", + readonly=True, + copy=False, + ) + + @api.depends("line_ids.wh_move_line_id") + def _compute_wh_invoice_ids(self): + """ + Update withholding invoice IDs and their count for an invoice. + + Search for account move lines linked by 'wh_move_line_id' and update + 'wh_invoice_ids' and 'wh_invoice_count' on the invoice. + + :return: None. + """ + for invoice in self: + wh_invoices = ( + self.env["account.move.line"] + .search([("wh_move_line_id", "in", invoice.line_ids.ids)]) + .move_id.ids + ) + invoice.wh_invoice_ids = wh_invoices + invoice.wh_invoice_count = len(wh_invoices) + + def action_view_wh_invoice(self): + """ + Open the view for withholding invoices associated with the current record. + + Map 'wh_invoice_ids' to retrieve the related invoices. Prepare and return an + action dict to open the invoice view. If no invoices are found, return an + action to close the window. + + :return: A dictionary with action details to open related invoices or close + the window. + """ + invoices = self.mapped("wh_invoice_ids") + action = self.env["ir.actions.actions"]._for_xml_id( + "account.action_move_in_invoice_type" + ) + if len(invoices): + action["domain"] = [("id", "in", invoices.ids)] + else: + action = {"type": "ir.actions.act_window_close"} + context = { + "default_move_type": "in_invoice", + } + action["context"] = context + return action + + def _prepare_wh_invoice(self, move_line, fiscal_group): + """ + Prepare a withholding tax invoice based on the provided move line and fiscal + group. + + :param move_line: The move line. + :param fiscal_group: The fiscal group. + + :return: Dictionary of invoice values. + """ + wh_date_invoice = move_line.move_id.date + wh_due_invoice = wh_date_invoice.replace(day=fiscal_group.wh_due_day) + values = { + "partner_id": fiscal_group.partner_id.id, + "date": wh_date_invoice, + "invoice_date": wh_date_invoice, + "invoice_date_due": wh_due_invoice + relativedelta(months=1), + "move_type": "in_invoice", + "journal_id": fiscal_group.journal_id.id or move_line.journal_id.id, + "invoice_origin": move_line.move_id.name, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": move_line.name, + "price_unit": abs(move_line.balance), + "account_id": move_line.account_id.id, + "wh_move_line_id": move_line.id, + "analytic_account_id": move_line.analytic_account_id.id, + }, + ) + ], + } + return values + + def create_wh_invoices(self): + """ + Create withholding tax invoices for applicable lines in the move. + + Iterate over each move in the recordset. For each tax line in the move + that matches the criteria, create a withholding tax invoice if the line + belongs to a supplier invoice and is associated with a tax that requires + withholding. Prepare and post these invoices. + + :return: None. Generate withholding tax invoices and post them. + """ + for move in self: + for line in move.line_ids.filtered(lambda line: line.tax_line_id): + # Create Wh Invoice only for supplier invoice + if line.move_id and line.move_id.move_type != "in_invoice": + continue + + account_tax_group = line.tax_line_id.tax_group_id + if account_tax_group and account_tax_group.fiscal_tax_group_id: + fiscal_group = account_tax_group.fiscal_tax_group_id + if ( + fiscal_group.generate_wh_invoice + and fiscal_group.tax_withholding + ): + wh_invoice = self.env["account.move"].create( + self._prepare_wh_invoice(line, fiscal_group) + ) + wh_invoice.message_post_with_view( + "mail.message_origin_link", + values={"self": wh_invoice, "origin": move}, + subtype_id=self.env.ref("mail.mt_note").id, + ) + wh_invoice.action_post() + + def _withholding_validate(self): + """ + Validate withholding by updating related invoices' states and clearing their + withholding move line references. + + For each record in the context, search for related invoices based on the + withholding move line IDs associated with the record's line items. Set any + posted invoices to draft, cancel any draft invoices, clear the withholding + move line ID reference from the invoice lines, and invalidate the cache to + ensure data coherency. + + :return: None + """ + for m in self: + wh_invoices = ( + self.env["account.move.line"] + .search( + [ + ("wh_move_line_id", "in", m.mapped("line_ids").ids), + ] + ) + .mapped("move_id") + ) + wh_invoices.filtered(lambda i: i.state == "posted").button_draft() + wh_invoices.filtered(lambda i: i.state == "draft").button_cancel() + wh_invoices.line_ids.wh_move_line_id = False + wh_invoices.invalidate_cache() + + def button_draft(self): + res = super(AccountMoveBR, self).button_draft + self._withholding_validate() + return res + + def _post(self, soft=True): + res = super(AccountMoveBR, self)._post(soft) + self.create_wh_invoices() + return res diff --git a/l10n_br_account_withholding/models/account_move_line.py b/l10n_br_account_withholding/models/account_move_line.py new file mode 100644 index 000000000000..50b82d4884f8 --- /dev/null +++ b/l10n_br_account_withholding/models/account_move_line.py @@ -0,0 +1,31 @@ +# Copyright 2024 Marcel Savegnago - Escodoo (https://www.escodoo.com.br) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, fields, models +from odoo.exceptions import UserError + +from odoo.addons.l10n_br_account.models.account_move_line import ( + AccountMoveLine as AccountMoveLineBR, +) + + +class AccountMoveLine(models.Model): + + _inherit = "account.move.line" + + wh_move_line_id = fields.Many2one( + comodel_name="account.move.line", + string="WH Account Move Line", + ondelete="restrict", + ) + + def write(self, values): + res = super(AccountMoveLineBR, self).write(values) + for line in self: + if line.wh_move_line_id and ( + "quantity" in values or "price_unit" in values + ): + raise UserError( + _("You cannot edit an invoice related to a withholding entry") + ) + return res diff --git a/l10n_br_account_withholding/models/l10n_br_fiscal_tax_group.py b/l10n_br_account_withholding/models/l10n_br_fiscal_tax_group.py new file mode 100644 index 000000000000..0173cf407ed1 --- /dev/null +++ b/l10n_br_account_withholding/models/l10n_br_fiscal_tax_group.py @@ -0,0 +1,22 @@ +# Copyright 2024 Marcel Savegnago - Escodoo (https://www.escodoo.com.br) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class FiscalTaxGroup(models.Model): + + _inherit = "l10n_br_fiscal.tax.group" + + journal_id = fields.Many2one( + comodel_name="account.journal", + string="Account Journal", + company_dependent=True, + domain="[('type', '=', 'purchase')]", + ) + + generate_wh_invoice = fields.Boolean( + string="Generate WH Invoice", + default=False, + company_dependent=True, + ) diff --git a/l10n_br_account_withholding/readme/CONFIGURE.md b/l10n_br_account_withholding/readme/CONFIGURE.md new file mode 100644 index 000000000000..31e3023321fa --- /dev/null +++ b/l10n_br_account_withholding/readme/CONFIGURE.md @@ -0,0 +1,5 @@ +Follow these steps to configure tax withholding invoice generation in Odoo: + +1. **Navigate to Fiscal Settings:** Go to `Fiscal -> Configurations -> Tax Groups`. Search for withheld taxes of the input type. + +2. **Configure Withheld Taxes:** For each tax that requires a withholding tax invoice, ensure it's properly configured. If a supplier isn't specified, set one. Also, specify the journal for the tax invoice generation. If no journal is specified, the module will use the journal from the originating purchase invoice. diff --git a/l10n_br_account_withholding/readme/CONTEXT.md b/l10n_br_account_withholding/readme/CONTEXT.md new file mode 100644 index 000000000000..50d39e92faa6 --- /dev/null +++ b/l10n_br_account_withholding/readme/CONTEXT.md @@ -0,0 +1,11 @@ +## Key Actions + +- **Automate Tax Compliance:** Create payable accounts for taxes withheld on supplier purchases automatically. +- **Reduce Errors:** Minimize manual entry errors and ensure accuracy in tax withholdings. +- **Streamline Efficiency:** Enhance your financial processes for handling tax withholdings from suppliers. + +## Application + +Use this module to: +- Withhold taxes on payments to suppliers effortlessly. +- Achieve efficient tax compliance and streamlined reporting. diff --git a/l10n_br_account_withholding/readme/CONTRIBUTORS.md b/l10n_br_account_withholding/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..40d4211bcdff --- /dev/null +++ b/l10n_br_account_withholding/readme/CONTRIBUTORS.md @@ -0,0 +1,8 @@ +* `Escodoo `_: + + * Marcel Savegnago + + +* `Akretion `_: + + * Renato Lima diff --git a/l10n_br_account_withholding/readme/DESCRIPTION.md b/l10n_br_account_withholding/readme/DESCRIPTION.md new file mode 100644 index 000000000000..e59c8fb0d7c9 --- /dev/null +++ b/l10n_br_account_withholding/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Automate your Brazilian tax compliance with the `l10n_br_account_withholding` module for Odoo. Streamline creation of payable invoices for taxes withheld on supplier purchases. Ensure accuracy and efficiency in managing tax obligations. diff --git a/l10n_br_account_withholding/readme/INSTALL.md b/l10n_br_account_withholding/readme/INSTALL.md new file mode 100644 index 000000000000..d50a94a55176 --- /dev/null +++ b/l10n_br_account_withholding/readme/INSTALL.md @@ -0,0 +1,6 @@ +To get started with the `l10n_br_account_withholding` module, follow these simple steps: + +1. **Module Installation:** + - Navigate to the Odoo Apps dashboard within your Odoo instance. + - Search for `l10n_br_account_withholding` in the Apps search bar. + - Click on the `Install` button next to the module. Odoo will handle the installation process automatically. diff --git a/l10n_br_account_withholding/readme/ROADMAP.md b/l10n_br_account_withholding/readme/ROADMAP.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_account_withholding/readme/USAGE.md b/l10n_br_account_withholding/readme/USAGE.md new file mode 100644 index 000000000000..4c513b60b633 --- /dev/null +++ b/l10n_br_account_withholding/readme/USAGE.md @@ -0,0 +1,5 @@ +Follow these steps to use tax withholding invoice generation in Odoo: + +1. **Creating a Purchase Invoice:** When creating a purchase invoice, apply the withheld tax to the necessary lines. + +2. **Invoice Confirmation:** Upon confirming the purchase invoice, the module automatically generates the withholding tax invoices. diff --git a/l10n_br_account_withholding/static/description/icon.png b/l10n_br_account_withholding/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/l10n_br_account_withholding/static/description/index.html b/l10n_br_account_withholding/static/description/index.html new file mode 100644 index 000000000000..9eebe7ae7b80 --- /dev/null +++ b/l10n_br_account_withholding/static/description/index.html @@ -0,0 +1,505 @@ + + + + + + +L10n Br Account Withholding + + + +
+

L10n Br Account Withholding

+ + +

Beta License: AGPL-3 OCA/l10n-brazil Translate me on Weblate Try me on Runboat

+

Automate your Brazilian tax compliance with the +l10n_br_account_withholding module for Odoo. Streamline creation of +payable invoices for taxes withheld on supplier purchases. Ensure +accuracy and efficiency in managing tax obligations.

+

Table of contents

+ +
+

Use Cases / Context

+
+

Key Actions

+
    +
  • Automate Tax Compliance: Create payable accounts for taxes +withheld on supplier purchases automatically.
  • +
  • Reduce Errors: Minimize manual entry errors and ensure accuracy +in tax withholdings.
  • +
  • Streamline Efficiency: Enhance your financial processes for +handling tax withholdings from suppliers.
  • +
+
+
+

Application

+

Use this module to:

+
    +
  • Withhold taxes on payments to suppliers effortlessly.
  • +
  • Achieve efficient tax compliance and streamlined reporting.
  • +
+
+
+
+

Installation

+

To get started with the l10n_br_account_withholding module, follow +these simple steps:

+
    +
  1. Module Installation:
      +
    • Navigate to the Odoo Apps dashboard within your Odoo instance.
    • +
    • Search for l10n_br_account_withholding in the Apps search bar.
    • +
    • Click on the Install button next to the module. Odoo will +handle the installation process automatically.
    • +
    +
  2. +
+
+
+

Configuration

+

Follow these steps to configure tax withholding invoice generation in +Odoo:

+
    +
  1. Navigate to Fiscal Settings: Go to +Fiscal -> Configurations -> Tax Groups. Search for withheld taxes +of the input type.
  2. +
  3. Configure Withheld Taxes: For each tax that requires a +withholding tax invoice, ensure it’s properly configured. If a +supplier isn’t specified, set one. Also, specify the journal for the +tax invoice generation. If no journal is specified, the module will +use the journal from the originating purchase invoice.
  4. +
+
+
+

Usage

+

Follow these steps to use tax withholding invoice generation in Odoo:

+
    +
  1. Creating a Purchase Invoice: When creating a purchase invoice, +apply the withheld tax to the necessary lines.
  2. +
  3. Invoice Confirmation: Upon confirming the purchase invoice, the +module automatically generates the withholding tax invoices.
  4. +
+
+ +
+

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

+
    +
  • Escodoo
  • +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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

+

This module is part of the OCA/l10n-brazil project on GitHub.

+

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

+
+
+
+ + diff --git a/l10n_br_account_withholding/tests/__init__.py b/l10n_br_account_withholding/tests/__init__.py new file mode 100644 index 000000000000..eda2cfcc70fb --- /dev/null +++ b/l10n_br_account_withholding/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_wh_invoice diff --git a/l10n_br_account_withholding/tests/test_account_wh_invoice.py b/l10n_br_account_withholding/tests/test_account_wh_invoice.py new file mode 100644 index 000000000000..f93ff659c122 --- /dev/null +++ b/l10n_br_account_withholding/tests/test_account_wh_invoice.py @@ -0,0 +1,312 @@ +# Copyright 2024 Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields +from odoo.tests.common import tagged + +from odoo.addons.l10n_br_account.tests.common import AccountMoveBRCommon + + +@tagged("post_install", "-at_install") +class AccountMoveWithWhInvoice(AccountMoveBRCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.pis_tax_definition_empresa_lc_wh_invoice = cls.env[ + "l10n_br_fiscal.tax.definition" + ].create( + { + "company_id": cls.company_data["company"].id, + "tax_group_id": cls.env.ref("l10n_br_fiscal.tax_group_pis").id, + "is_taxed": True, + "is_debit_credit": True, + "custom_tax": True, + "tax_id": cls.env.ref("l10n_br_fiscal.tax_pis_wh_0_65").id, + "state": "approved", + } + ) + + cls.cofins_tax_definition_empresa_lc_wh_invoice = cls.env[ + "l10n_br_fiscal.tax.definition" + ].create( + { + "company_id": cls.company_data["company"].id, + "tax_group_id": cls.env.ref("l10n_br_fiscal.tax_group_cofins").id, + "is_taxed": True, + "is_debit_credit": True, + "custom_tax": True, + "tax_id": cls.env.ref("l10n_br_fiscal.tax_cofins_wh_3").id, + "state": "approved", + } + ) + + cls.env.ref("l10n_br_fiscal.fo_compras").deductible_taxes = True + cls.move_in_compra_para_revenda = cls.init_invoice( + "in_invoice", + products=[cls.product_a], + document_type=cls.env.ref("l10n_br_fiscal.document_55"), + fiscal_operation=cls.env.ref("l10n_br_fiscal.fo_compras"), + fiscal_operation_lines=[ + cls.env.ref("l10n_br_fiscal.fo_compras_compras_comercializacao") + ], + document_serie="1", + document_number="54", + ) + cls.env.ref("l10n_br_fiscal.tax_group_pis_wh").generate_wh_invoice = True + cls.env.ref("l10n_br_fiscal.tax_group_cofins_wh").generate_wh_invoice = True + + @classmethod + def setup_company_data(cls, company_name, chart_template=None, **kwargs): + if company_name == "company_1_data": + company_name = "empresa 1 Lucro Presumido" + else: + company_name = "empresa 2 Lucro Presumido" + chart_template = cls.env.ref("l10n_br_coa_generic.l10n_br_coa_generic_template") + res = super().setup_company_data( + company_name, + chart_template, + tax_framework="3", + is_industry=True, + industry_type="00", + profit_calculation="presumed", + ripi=True, + piscofins_id=cls.env.ref("l10n_br_fiscal.tax_pis_cofins_columativo").id, + icms_regulation_id=cls.env.ref("l10n_br_fiscal.tax_icms_regulation").id, + cnae_main_id=cls.env.ref("l10n_br_fiscal.cnae_3101200").id, + document_type_id=cls.env.ref("l10n_br_fiscal.document_55").id, + **kwargs + ) + res["company"].partner_id.state_id = cls.env.ref("base.state_br_sp").id + chart_template.load_fiscal_taxes() + return res + + def test_compra_para_revenda(self): + """ + Test move with deductible taxes and withholding taxes + """ + product_line_vals_1 = { + "name": self.product_a.name, + "product_id": self.product_a.id, + "account_id": self.product_a.property_account_expense_id.id, + "partner_id": self.partner_a.id, + "product_uom_id": self.product_a.uom_id.id, + "quantity": 1.0, + "discount": 0.0, + "price_unit": 1000.0, + "price_subtotal": 1000.0, + "price_total": 1000.0, + "tax_line_id": False, + "currency_id": self.company_data["currency"].id, + "amount_currency": 1050.0, + "debit": 1050.0, + "credit": 0.0, + "date_maturity": False, + "tax_exigible": True, + } + + tax_line_vals_cofins_wh = { + "name": "COFINS WH Entrada", + "product_id": False, + "account_id": self.env["account.account"] + .search([("name", "=", "COFINS a Recolher")], order="id DESC", limit=1) + .id, + "partner_id": self.partner_a.id, + "product_uom_id": False, + "quantity": 1.0, + "discount": 0.0, + "price_unit": 0.0, + "price_subtotal": 0.0, + "price_total": 0.0, + "tax_ids": [], + "tax_line_id": self.env["account.tax"] + .search([("name", "=", "COFINS WH Entrada")], order="id DESC", limit=1) + .id, + "currency_id": self.company_data["currency"].id, + "amount_currency": -30.0, + "debit": 0.0, + "credit": 30.0, + "date_maturity": False, + "tax_exigible": True, + } + + tax_line_vals_icms = { + "name": "ICMS Entrada", + "product_id": False, + "account_id": self.env["account.account"] + .search([("name", "=", "ICMS a Compensar")], order="id DESC", limit=1) + .id, + "partner_id": self.partner_a.id, + "product_uom_id": False, + "quantity": 1.0, + "discount": 0.0, + "price_unit": 0.0, + "price_subtotal": 0.0, + "price_total": 0.0, + "tax_ids": [], + "tax_line_id": self.env["account.tax"] + .search([("name", "=", "ICMS Entrada")], order="id DESC", limit=1) + .id, + "currency_id": self.company_data["currency"].id, + "amount_currency": 120.0, + "debit": 120.0, + "credit": 0.0, + "date_maturity": False, + "tax_exigible": True, + } + + tax_line_vals_icms_comp = { + "name": "ICMS Entrada Dedutível", + "product_id": False, + "account_id": self.env["account.account"] + .search([("name", "=", "ICMS s/ Vendas")], order="id DESC", limit=1) + .id, + "partner_id": self.partner_a.id, + "product_uom_id": False, + "quantity": 1.0, + "discount": 0.0, + "price_unit": 0.0, + "price_subtotal": 0.0, + "price_total": 0.0, + "tax_ids": [], + "tax_line_id": self.env["account.tax"] + .search([("name", "=", "ICMS Entrada Dedutível")], order="id desc", limit=1) + .id, + "currency_id": self.company_data["currency"].id, + "amount_currency": -120.0, + "debit": 0.0, + "credit": 120.0, + "date_maturity": False, + "tax_exigible": True, + } + + tax_line_vals_ipi = { + "name": "IPI Entrada", + "product_id": False, + "account_id": self.env["account.account"] + .search([("name", "=", "IPI a Compensar")], order="id DESC", limit=1) + .id, + "partner_id": self.partner_a.id, + "product_uom_id": False, + "quantity": 1.0, + "discount": 0.0, + "price_unit": 0.0, + "price_subtotal": 0.0, + "price_total": 0.0, + "tax_ids": [], + "tax_line_id": self.env["account.tax"] + .search([("name", "=", "IPI Entrada")], order="id desc", limit=1) + .id, + "currency_id": self.company_data["currency"].id, + "amount_currency": 50.0, + "debit": 50.0, + "credit": 0.0, + "date_maturity": False, + "tax_exigible": True, + } + + tax_line_vals_ipi_comp = { + "name": "IPI Entrada Dedutível", + "product_id": False, + "account_id": self.env["account.account"] + .search([("name", "=", "IPI s/ Vendas")], order="id DESC", limit=1) + .id, + "partner_id": self.partner_a.id, + "product_uom_id": False, + "quantity": 1.0, + "discount": 0.0, + "price_unit": 0.0, + "price_subtotal": 0.0, + "price_total": 0.0, + "tax_ids": [], + "tax_line_id": self.env["account.tax"] + .search([("name", "=", "IPI Entrada Dedutível")], order="id desc", limit=1) + .id, + "currency_id": self.company_data["currency"].id, + "amount_currency": -50.0, + "debit": 0.0, + "credit": 50.0, + "date_maturity": False, + "tax_exigible": True, + } + + tax_line_vals_pis_wh = { + "name": "PIS WH Entrada", + "product_id": False, + "account_id": self.env["account.account"] + .search([("name", "=", "PIS a Recolher")], order="id DESC", limit=1) + .id, + "partner_id": self.partner_a.id, + "product_uom_id": False, + "quantity": 1.0, + "discount": 0.0, + "price_unit": 0.0, + "price_subtotal": 0.0, + "price_total": 0.0, + "tax_ids": [], + "tax_line_id": self.env["account.tax"] + .search([("name", "=", "PIS WH Entrada")], order="id desc", limit=1) + .id, + "currency_id": self.company_data["currency"].id, + "amount_currency": -6.5, + "debit": 0.0, + "credit": 6.5, + "date_maturity": False, + "tax_exigible": True, + } + + term_line_vals_1 = { + "name": "54/1-1", + "product_id": False, + "account_id": self.company_data["default_account_payable"].id, + "partner_id": self.partner_a.id, + "product_uom_id": False, + "quantity": 1.0, + "discount": 0.0, + "price_unit": 0.0, + "price_subtotal": 0.0, + "price_total": 0.0, + "tax_ids": [], + "tax_line_id": False, + "currency_id": self.company_data["currency"].id, + "amount_currency": -1013.5, + "debit": 0.0, + "credit": 1013.5, + "date_maturity": fields.Date.from_string("2019-01-01"), + "tax_exigible": True, + } + + move_vals = { + "partner_id": self.partner_a.id, + "currency_id": self.company_data["currency"].id, + "journal_id": self.company_data["default_journal_purchase"].id, + "date": fields.Date.from_string("2019-01-01"), + "fiscal_position_id": False, + "payment_reference": "", + "invoice_payment_term_id": self.pay_terms_a.id, + "amount_untaxed": 1000.0, + "amount_tax": 50.0, + "amount_total": 1013.5, + } + + self.assertInvoiceValues( + self.move_in_compra_para_revenda, + [ + product_line_vals_1, + tax_line_vals_cofins_wh, + tax_line_vals_icms, + tax_line_vals_icms_comp, + tax_line_vals_ipi, + tax_line_vals_ipi_comp, + tax_line_vals_pis_wh, + term_line_vals_1, + ], + move_vals, + ) + + self.move_in_compra_para_revenda.action_post() + self.move_in_compra_para_revenda._compute_wh_invoice_ids() + self.assertEqual( + self.move_in_compra_para_revenda.wh_invoice_count, + 2, + "The invoice should have 2 withholding invoices (PIS and COFINS).", + ) diff --git a/l10n_br_account_withholding/views/account_move.xml b/l10n_br_account_withholding/views/account_move.xml new file mode 100644 index 000000000000..267d7db24a4d --- /dev/null +++ b/l10n_br_account_withholding/views/account_move.xml @@ -0,0 +1,48 @@ + + + + + + account.move + + + + + + + + + + + + + diff --git a/l10n_br_account_withholding/views/account_move_line.xml b/l10n_br_account_withholding/views/account_move_line.xml new file mode 100644 index 000000000000..e1f52bfca8bc --- /dev/null +++ b/l10n_br_account_withholding/views/account_move_line.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/l10n_br_account_withholding/views/l10n_br_fiscal_tax_group.xml b/l10n_br_account_withholding/views/l10n_br_fiscal_tax_group.xml new file mode 100644 index 000000000000..6139e0136849 --- /dev/null +++ b/l10n_br_account_withholding/views/l10n_br_fiscal_tax_group.xml @@ -0,0 +1,41 @@ + + + + + + l10n_br_fiscal.tax.group + + + + + + + + + + + l10n_br_fiscal.tax.group + + + + + + + + + + l10n_br_fiscal.tax.group + + + + + + + + + diff --git a/setup/l10n_br_account_withholding/odoo/addons/l10n_br_account_withholding b/setup/l10n_br_account_withholding/odoo/addons/l10n_br_account_withholding new file mode 120000 index 000000000000..981a4bb605fa --- /dev/null +++ b/setup/l10n_br_account_withholding/odoo/addons/l10n_br_account_withholding @@ -0,0 +1 @@ +../../../../l10n_br_account_withholding \ No newline at end of file diff --git a/setup/l10n_br_account_withholding/setup.py b/setup/l10n_br_account_withholding/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/l10n_br_account_withholding/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)