diff --git a/l10n_br_fiscal/__manifest__.py b/l10n_br_fiscal/__manifest__.py index 92ea5bc77893..30a2e5ff9b6d 100644 --- a/l10n_br_fiscal/__manifest__.py +++ b/l10n_br_fiscal/__manifest__.py @@ -30,6 +30,7 @@ "data/res_partner_data.xml", "data/l10n_br_fiscal.tax.group.csv", "data/l10n_br_fiscal.icms.relief.csv", + "data/l10n_br_fiscal_icms_difal_definition_data.xml", "data/l10n_br_fiscal.document.type.csv", "data/l10n_br_fiscal.product.genre.csv", "data/l10n_br_fiscal.cst.csv", diff --git a/l10n_br_fiscal/constants/icms.py b/l10n_br_fiscal/constants/icms.py index d2435faed3db..ef5526789dd0 100644 --- a/l10n_br_fiscal/constants/icms.py +++ b/l10n_br_fiscal/constants/icms.py @@ -105,39 +105,4 @@ } -ICMS_DIFAL_UNIQUE_BASE = [ - "DF", - "ES", - "MA", - "MS", - "PE", - "RJ", - "RN", - "RR", -] - - -ICMS_DIFAL_DOUBLE_BASE = [ - "AC", - "AL", - "AP", - "AM", - "BA", - "CE", - "GO", - "MG", - "MT", - "PA", - "PB", - "PI", - "PR", - "RO", - "RS", - "SC", - "SE", - "SP", - "TO", -] - - ICSM_CST_CSOSN_ST_BASE = ["10", "30", "70", "90", "201", "202", "203", "900"] diff --git a/l10n_br_fiscal/data/l10n_br_fiscal_icms_difal_definition_data.xml b/l10n_br_fiscal/data/l10n_br_fiscal_icms_difal_definition_data.xml new file mode 100644 index 000000000000..5e48085466a5 --- /dev/null +++ b/l10n_br_fiscal/data/l10n_br_fiscal_icms_difal_definition_data.xml @@ -0,0 +1,39 @@ + + + + Regulamento do ICMS Difal + + + + diff --git a/l10n_br_fiscal/demo/company_demo.xml b/l10n_br_fiscal/demo/company_demo.xml index 407e1cdc7053..57dc2b9f68fe 100644 --- a/l10n_br_fiscal/demo/company_demo.xml +++ b/l10n_br_fiscal/demo/company_demo.xml @@ -108,6 +108,7 @@ + diff --git a/l10n_br_fiscal/models/__init__.py b/l10n_br_fiscal/models/__init__.py index bf686ec7cec0..d01e2b74239f 100644 --- a/l10n_br_fiscal/models/__init__.py +++ b/l10n_br_fiscal/models/__init__.py @@ -43,6 +43,7 @@ from . import tax_definition_partner_profile from . import icms_regulation from . import icms_relief +from . import icms_difal_regulation from . import document_type from . import document_serie from . import product_genre diff --git a/l10n_br_fiscal/models/icms_difal_regulation.py b/l10n_br_fiscal/models/icms_difal_regulation.py new file mode 100644 index 000000000000..62eb3483b7c2 --- /dev/null +++ b/l10n_br_fiscal/models/icms_difal_regulation.py @@ -0,0 +1,37 @@ +# Copyright (C) 2023 Felipe Motter Pereira - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class ICMSDifalRegulation(models.Model): + _name = "l10n_br_fiscal.icms.difal.regulation" + _description = "ICMS Difal Regulation" + + name = fields.Text(required=True, index=True) + + unique_base_state_ids = fields.Many2many( + comodel_name="res.country.state", + relation="icms_difal_regulation_unique_base_state_rel", + column1="icms_difal_regulation", + column2="state_id", + string="States with Unique Base", + domain=[("country_id.code", "=", "BR")], + ) + + double_base_state_ids = fields.Many2many( + comodel_name="res.country.state", + relation="icms_difal_regulation_double_base_state_rel", + column1="icms_difal_regulation", + column2="state_id", + string="States with Double Base", + domain=[("country_id.code", "=", "BR")], + ) + + @api.constrains("unique_base_state_ids", "double_base_state_ids") + def _check_duplicity(self): + for state in self.unique_base_state_ids: + if state in self.double_base_state_ids: + raise UserError(_("You cannot have two bases for same state.")) + return True diff --git a/l10n_br_fiscal/models/res_company.py b/l10n_br_fiscal/models/res_company.py index 2939073aa747..9acd2546dec2 100644 --- a/l10n_br_fiscal/models/res_company.py +++ b/l10n_br_fiscal/models/res_company.py @@ -253,6 +253,11 @@ def _compute_simplified_tax(self): comodel_name="l10n_br_fiscal.icms.regulation", string="ICMS Regulation" ) + icms_difal_regulation_id = fields.Many2one( + comodel_name="l10n_br_fiscal.icms.difal.regulation", + string="ICMS Difal Regulation", + ) + tax_issqn_id = fields.Many2one( comodel_name="l10n_br_fiscal.tax", string="Default ISSQN", diff --git a/l10n_br_fiscal/models/tax.py b/l10n_br_fiscal/models/tax.py index d00196f6a89a..11c136a58cbb 100644 --- a/l10n_br_fiscal/models/tax.py +++ b/l10n_br_fiscal/models/tax.py @@ -1,7 +1,8 @@ # Copyright (C) 2013 Renato Lima - Akretion # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import UserError from odoo.tools import float_is_zero from ..constants.fiscal import ( @@ -21,9 +22,7 @@ from ..constants.icms import ( ICMS_BASE_TYPE, ICMS_BASE_TYPE_DEFAULT, - ICMS_DIFAL_DOUBLE_BASE, ICMS_DIFAL_PARTITION, - ICMS_DIFAL_UNIQUE_BASE, ICMS_ORIGIN_TAX_IMPORTED, ICMS_SN_CST_WITH_CREDIT, ICMS_ST_BASE_TYPE, @@ -388,7 +387,10 @@ def _compute_icms(self, tax, taxes_dict, **kwargs): and partner.ind_ie_dest == NFE_IND_IE_DEST_9 and tax_dict.get("tax_value") ): - icms_tax_difal, _ = company.icms_regulation_id.map_tax_def_icms_difal( + ( + icms_tax_difal, + tax_definitions, + ) = company.icms_regulation_id.map_tax_def_icms_difal( company, partner, product, ncm, nbm, cest, operation_line, ind_final ) icmsfcp_tax_difal = taxes_dict.get("icmsfcp", {}) @@ -416,10 +418,16 @@ def _compute_icms(self, tax, taxes_dict, **kwargs): # Difal - ICMS Dest Value icms_dest_value = currency.round(icms_base * (icms_dest_perc / 100)) - if partner.state_id.code in ICMS_DIFAL_UNIQUE_BASE: + icms_difal_regulation = company.icms_difal_regulation_id + if not icms_difal_regulation: + raise UserError( + _("The company '%s' don't have a ICMS Difal Regulation defined.") + % (company.name) + ) + if partner.state_id in icms_difal_regulation.unique_base_state_ids: difal_icms_base = icms_base - if partner.state_id.code in ICMS_DIFAL_DOUBLE_BASE: + elif partner.state_id in icms_difal_regulation.double_base_state_ids: difal_icms_base = currency.round( (icms_base - icms_origin_value) / (1 - ((icms_dest_perc + icmsfcp_perc) / 100)) @@ -428,6 +436,14 @@ def _compute_icms(self, tax, taxes_dict, **kwargs): icms_dest_value = currency.round( difal_icms_base * (icms_dest_perc / 100) ) + else: + raise UserError( + _( + "The state of partner '%s' does not have a defined " + "base in the icms difal regulation." + ) + % (partner.state_id.code) + ) difal_value = icms_dest_value - icms_origin_value diff --git a/l10n_br_fiscal/security/ir.model.access.csv b/l10n_br_fiscal/security/ir.model.access.csv index 43a893a1a1af..235a7588c8c9 100644 --- a/l10n_br_fiscal/security/ir.model.access.csv +++ b/l10n_br_fiscal/security/ir.model.access.csv @@ -10,6 +10,8 @@ "l10n_br_fiscal_cst_maintenance","Fiscal CST for Maintenance","model_l10n_br_fiscal_cst","l10n_br_fiscal.group_data_maintenance",1,1,1,1 "l10n_br_fiscal_tax_group_user","Fiscal Tax Group for User","model_l10n_br_fiscal_tax_group","l10n_br_fiscal.group_user",1,0,0,0 "l10n_br_fiscal_tax_group_manager","Fiscal Tax Group for Manager","model_l10n_br_fiscal_tax_group","l10n_br_fiscal.group_manager",1,1,1,1 +"l10n_br_fiscal_icms_difal_regulation_user","Fiscal Tax ICMS Difal Regulation for User","model_l10n_br_fiscal_icms_difal_regulation","l10n_br_fiscal.group_user",1,0,0,0 +"l10n_br_fiscal_icms_difal_regulation_manager","Fiscal Tax ICMS Difal Regulation for Manager","model_l10n_br_fiscal_icms_difal_regulation","l10n_br_fiscal.group_manager",1,1,1,1 "l10n_br_fiscal_icms_regulation_user","Fiscal Tax ICMS Regulation for User","model_l10n_br_fiscal_icms_regulation","l10n_br_fiscal.group_user",1,0,0,0 "l10n_br_fiscal_icms_regulation_manager","Fiscal Tax ICMS Regulation for Manager","model_l10n_br_fiscal_icms_regulation","l10n_br_fiscal.group_manager",1,1,1,1 "l10n_br_fiscal_icms_relief_user","Fiscal Tax ICMS Relief for User","model_l10n_br_fiscal_icms_relief","l10n_br_fiscal.group_user",1,0,0,0 diff --git a/l10n_br_fiscal/tests/test_fiscal_tax.py b/l10n_br_fiscal/tests/test_fiscal_tax.py index 063f7d9ac2fa..5236605473c8 100644 --- a/l10n_br_fiscal/tests/test_fiscal_tax.py +++ b/l10n_br_fiscal/tests/test_fiscal_tax.py @@ -1,6 +1,7 @@ # Copyright 2020 Akretion - Renato Lima # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import exceptions from odoo.tests import SavepointCase from odoo.tools import float_compare @@ -303,3 +304,45 @@ def test_compute_taxes_03(self): } self._check_compute_taxes_result(test_result, compute_result, currency) + + def test_difal(self): + """Testa o calculo dos impostos de compra - entrada de importação""" + + kwargs = self._create_compute_taxes_kwargs() + currency = kwargs["company"].currency_id + + kwargs["partner"] = self.env.ref("l10n_br_base.res_partner_cliente10_mg") + + fiscal_taxes = self.env["l10n_br_fiscal.tax"] + fiscal_taxes |= self.env.ref("l10n_br_fiscal.tax_icms_4") + + difal_regulation = self.env.ref("l10n_br_fiscal.tax_icms_difal_regulation") + kwargs["company"].icms_difal_regulation_id = False + + with self.assertRaises(exceptions.UserError): + fiscal_taxes.compute_taxes(**kwargs) + + kwargs["company"].icms_difal_regulation_id = difal_regulation + + compute_result = fiscal_taxes.compute_taxes(**kwargs) + + test_result = { + "amount_included": 1.38, + "amount_not_included": 0.0, + "amount_withholding": 0.0, + "estimate_tax": 0.0, + "taxes": { + "icms": { + "icms_dest_base": 34.58, + "icms_dest_value": 4.84, + }, + }, + } + + self._check_compute_taxes_result(test_result, compute_result, currency) + + difal_regulation.unique_base_state_ids = [(5, 0, 0)] + difal_regulation.double_base_state_ids = [(5, 0, 0)] + + with self.assertRaises(exceptions.UserError): + fiscal_taxes.compute_taxes(**kwargs) diff --git a/l10n_br_fiscal/tests/test_icms_regulation.py b/l10n_br_fiscal/tests/test_icms_regulation.py index f1d187370dff..fc396079804e 100644 --- a/l10n_br_fiscal/tests/test_icms_regulation.py +++ b/l10n_br_fiscal/tests/test_icms_regulation.py @@ -1,3 +1,4 @@ +from odoo.exceptions import UserError from odoo.tests import SavepointCase, tagged from ..constants.fiscal import FINAL_CUSTOMER_NO, FINAL_CUSTOMER_YES, TAX_DOMAIN_ICMS @@ -71,3 +72,16 @@ def find_icms_tax(self, in_state_id, out_state_id, ncm_id, ind_final): ind_final=ind_final, ) return tax_icms.filtered(lambda t: t.tax_domain == TAX_DOMAIN_ICMS) + + def test_state_difal_base_duplicity(self): + + demo_state = self.env.ref("base.state_br_sc") + + with self.assertRaises(UserError): + self.env["l10n_br_fiscal.icms.difal.regulation"].create( + { + "name": "Difal Test", + "unique_base_state_ids": [(4, demo_state.id, 0)], + "double_base_state_ids": [(4, demo_state.id, 0)], + } + ) diff --git a/l10n_br_fiscal/views/l10n_br_fiscal_action.xml b/l10n_br_fiscal/views/l10n_br_fiscal_action.xml index fb45ee42d652..2f771cbf2f72 100644 --- a/l10n_br_fiscal/views/l10n_br_fiscal_action.xml +++ b/l10n_br_fiscal/views/l10n_br_fiscal_action.xml @@ -100,6 +100,22 @@ + + + ICMS Difal Regulation + ir.actions.act_window + l10n_br_fiscal.icms.difal.regulation + tree,form + + + Add a new ICMS Difal Regulation + + A ICMS Difal Regulation is necessary to calc + ICMS Difal values on document lines. + + + + ICMS Regulation diff --git a/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml b/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml index aaf90d6845f8..9307dddf8874 100644 --- a/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml +++ b/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml @@ -408,6 +408,15 @@ sequence="20" /> + + + +
+ Add a new ICMS Difal Regulation +
+ A ICMS Difal Regulation is necessary to calc + ICMS Difal values on document lines. +