From 0770300a3ddfcd8795cb3873fd7f5e611cfd62d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B4nio=20Neto?= Date: Thu, 18 Jan 2024 20:00:53 -0300 Subject: [PATCH 1/8] [FIX] l10n_br_account: post by action --- l10n_br_account/models/account_move.py | 28 ++------------------------ 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/l10n_br_account/models/account_move.py b/l10n_br_account/models/account_move.py index 13e700a43b01..6659f62d5bbc 100644 --- a/l10n_br_account/models/account_move.py +++ b/l10n_br_account/models/account_move.py @@ -596,35 +596,11 @@ def action_document_back2draft(self): move.button_cancel() move.button_draft() - def action_post(self): - result = super().action_post() - + def _post(self, soft=True): self.mapped("fiscal_document_id").filtered( lambda d: d.document_type_id ).action_document_confirm() - - # TODO FIXME - # Deixar a migração das funcionalidades do refund por último. - # Verificar se ainda haverá necessidade desse código. - - # for record in self.filtered(lambda i: i.refund_move_id): - # if record.state == "open": - # # Ao confirmar uma fatura/documento fiscal se é uma devolução - # # é feito conciliado com o documento de origem para abater - # # o valor devolvido pelo documento de refund - # to_reconcile_lines = self.env["account.move.line"] - # for line in record.move_id.line_ids: - # if line.account_id.id == record.account_id.id: - # to_reconcile_lines += line - # if line.reconciled: - # line.remove_move_reconcile() - # for line in record.refund_move_id.move_id.line_ids: - # if line.account_id.id == record.refund_move_id.account_id.id: - # to_reconcile_lines += line - - # to_reconcile_lines.filtered(lambda l: l.reconciled).reconcile() - - return result + return super()._post(soft=soft) def view_xml(self): self.ensure_one_doc() From fa66a736b900a1231c6613c03473281bb65b1822 Mon Sep 17 00:00:00 2001 From: Felipe Motter Pereira Date: Wed, 5 Jun 2024 09:38:01 -0300 Subject: [PATCH 2/8] [FIX] l10n_br_account: validate move on document --- l10n_br_account/models/account_move.py | 2 +- l10n_br_account/models/fiscal_document.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/l10n_br_account/models/account_move.py b/l10n_br_account/models/account_move.py index 6659f62d5bbc..a3f92b53d983 100644 --- a/l10n_br_account/models/account_move.py +++ b/l10n_br_account/models/account_move.py @@ -599,7 +599,7 @@ def action_document_back2draft(self): def _post(self, soft=True): self.mapped("fiscal_document_id").filtered( lambda d: d.document_type_id - ).action_document_confirm() + )._document_confirm_to_send() return super()._post(soft=soft) def view_xml(self): diff --git a/l10n_br_account/models/fiscal_document.py b/l10n_br_account/models/fiscal_document.py index 8134e2868fe4..47ed0b8c14f7 100644 --- a/l10n_br_account/models/fiscal_document.py +++ b/l10n_br_account/models/fiscal_document.py @@ -178,3 +178,9 @@ def _document_correction(self, justificative): msg = "Carta de correção: {}".format(justificative) self.message_post(body=msg) return result + + def action_document_confirm(self): + result = super().action_document_confirm() + move_ids = self.move_ids.filtered(lambda move: move.state == "draft") + move_ids._post() + return result From ff218406809b99959798e93875dcb5789625c90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Wed, 10 Apr 2024 01:12:33 -0300 Subject: [PATCH 3/8] [REF] extract l10n_br_fiscal_edi <- l10n_br_fiscal [REF] extract l10n_br_fiscal_edi from l10n_br_fiscal 2/2 [TMP] leave invalidate.number in l10n_br_fiscal [REF] l10n_br_fiscal: mv doc wrkflw ->fisc edi [REF] l10n_br_fiscal_edi: mv doc wrkflw ->fisc edi [WIP] mv edoc import to fiscal_edi [REF] add fiscal document hook [FIX] l10n_br_fiscal_edi: wrong status_description [FIX] l10n_br_fiscal: edi migration --- l10n_br_fiscal/__manifest__.py | 14 +- .../migrations/16.0.2.0.0/pre-migration.py | 25 + l10n_br_fiscal/models/__init__.py | 3 - l10n_br_fiscal/models/document.py | 82 +- l10n_br_fiscal/models/invalidate_number.py | 44 - l10n_br_fiscal/security/ir.model.access.csv | 5 - l10n_br_fiscal/tests/__init__.py | 1 - .../tests/test_fiscal_document_generic.py | 48 - .../tests/test_fiscal_document_nfse.py | 2 - .../tests/test_subsequent_operation.py | 3 +- l10n_br_fiscal/tests/test_tax_benefit.py | 21 - l10n_br_fiscal/views/document_view.xml | 70 +- .../views/invalidate_number_view.xml | 9 +- .../views/l10n_br_fiscal_action.xml | 34 - l10n_br_fiscal/views/l10n_br_fiscal_menu.xml | 19 - l10n_br_fiscal/wizards/__init__.py | 6 - l10n_br_fiscal/wizards/base_wizard_mixin.py | 5 - .../wizards/document_status_wizard.py | 2 + l10n_br_fiscal_edi/README.rst | 124 ++ l10n_br_fiscal_edi/__init__.py | 2 + l10n_br_fiscal_edi/__manifest__.py | 41 + l10n_br_fiscal_edi/models/__init__.py | 4 + .../models/document.py | 70 +- .../models/document_event.py | 4 +- .../models/document_workflow.py | 53 +- .../models/invalidate_number.py | 53 + l10n_br_fiscal_edi/pyproject.toml | 3 + l10n_br_fiscal_edi/readme/CONTRIBUTORS.md | 5 + l10n_br_fiscal_edi/readme/DESCRIPTION.md | 6 + l10n_br_fiscal_edi/readme/ROADMAP.md | 6 + l10n_br_fiscal_edi/readme/USAGE.md | 7 + .../security/ir.model.access.csv | 7 + .../static/description/index.html | 460 +++++++ l10n_br_fiscal_edi/tests/__init__.py | 1 + .../tests/test_fiscal_document_generic.py | 1191 +++++++++++++++++ l10n_br_fiscal_edi/tests/test_tax_benefit.py | 74 + .../tests/test_workflow.py | 2 +- .../views/document_event_report.xml | 0 .../views/document_event_template.xml | 0 .../views/document_event_view.xml | 0 l10n_br_fiscal_edi/views/document_view.xml | 77 ++ .../views/invalidate_number_view.xml | 19 + .../views/l10n_br_fiscal_action.xml | 38 + .../views/l10n_br_fiscal_menu.xml | 23 + l10n_br_fiscal_edi/wizards/__init__.py | 7 + .../wizards/base_wizard_mixin.py | 13 + .../wizards/document_cancel_wizard.py | 0 .../wizards/document_cancel_wizard.xml | 0 .../wizards/document_correction_wizard.py | 0 .../wizards/document_correction_wizard.xml | 0 .../wizards/document_import_wizard_mixin.py | 2 +- .../wizards/document_import_wizard_mixin.xml | 0 .../wizards/document_status_wizard.xml | 0 .../wizards/invalidate_number_wizard.py | 4 + .../wizards/invalidate_number_wizard.xml | 0 l10n_br_nfe/wizards/import_document.xml | 2 +- .../odoo/addons/l10n_br_fiscal_edi | 1 + setup/l10n_br_fiscal_edi/setup.py | 6 + 58 files changed, 2362 insertions(+), 336 deletions(-) create mode 100644 l10n_br_fiscal/migrations/16.0.2.0.0/pre-migration.py create mode 100644 l10n_br_fiscal_edi/README.rst create mode 100644 l10n_br_fiscal_edi/__init__.py create mode 100644 l10n_br_fiscal_edi/__manifest__.py create mode 100644 l10n_br_fiscal_edi/models/__init__.py rename l10n_br_fiscal/models/document_eletronic.py => l10n_br_fiscal_edi/models/document.py (75%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/models/document_event.py (98%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/models/document_workflow.py (92%) create mode 100644 l10n_br_fiscal_edi/models/invalidate_number.py create mode 100644 l10n_br_fiscal_edi/pyproject.toml create mode 100644 l10n_br_fiscal_edi/readme/CONTRIBUTORS.md create mode 100644 l10n_br_fiscal_edi/readme/DESCRIPTION.md create mode 100644 l10n_br_fiscal_edi/readme/ROADMAP.md create mode 100644 l10n_br_fiscal_edi/readme/USAGE.md create mode 100644 l10n_br_fiscal_edi/security/ir.model.access.csv create mode 100644 l10n_br_fiscal_edi/static/description/index.html create mode 100644 l10n_br_fiscal_edi/tests/__init__.py create mode 100644 l10n_br_fiscal_edi/tests/test_fiscal_document_generic.py create mode 100644 l10n_br_fiscal_edi/tests/test_tax_benefit.py rename {l10n_br_fiscal => l10n_br_fiscal_edi}/tests/test_workflow.py (98%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/views/document_event_report.xml (100%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/views/document_event_template.xml (100%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/views/document_event_view.xml (100%) create mode 100644 l10n_br_fiscal_edi/views/document_view.xml create mode 100644 l10n_br_fiscal_edi/views/invalidate_number_view.xml create mode 100644 l10n_br_fiscal_edi/views/l10n_br_fiscal_action.xml create mode 100644 l10n_br_fiscal_edi/views/l10n_br_fiscal_menu.xml create mode 100644 l10n_br_fiscal_edi/wizards/__init__.py create mode 100644 l10n_br_fiscal_edi/wizards/base_wizard_mixin.py rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/document_cancel_wizard.py (100%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/document_cancel_wizard.xml (100%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/document_correction_wizard.py (100%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/document_correction_wizard.xml (100%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/document_import_wizard_mixin.py (98%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/document_import_wizard_mixin.xml (100%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/document_status_wizard.xml (100%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/invalidate_number_wizard.py (81%) rename {l10n_br_fiscal => l10n_br_fiscal_edi}/wizards/invalidate_number_wizard.xml (100%) create mode 120000 setup/l10n_br_fiscal_edi/odoo/addons/l10n_br_fiscal_edi create mode 100644 setup/l10n_br_fiscal_edi/setup.py diff --git a/l10n_br_fiscal/__manifest__.py b/l10n_br_fiscal/__manifest__.py index a5ae40475f66..f373ce26f017 100644 --- a/l10n_br_fiscal/__manifest__.py +++ b/l10n_br_fiscal/__manifest__.py @@ -3,14 +3,14 @@ { "name": "Módulo fiscal brasileiro", - "summary": "Brazilian fiscal core module.", + "summary": "Fiscal module/tax engine for Brazil", "category": "Localisation", "license": "AGPL-3", "author": "Akretion, Odoo Community Association (OCA)", "maintainers": ["renatonlima"], "website": "https://github.com/OCA/l10n-brazil", "development_status": "Production/Stable", - "version": "15.0.1.20.4", + "version": "15.0.2.0.0", "depends": [ "product", "l10n_br_base", @@ -84,16 +84,6 @@ "views/invalidate_number_view.xml", "views/city_taxation_code.xml", "views/operation_dashboard_view.xml", - "views/document_event_view.xml", - # Reports - "views/document_event_report.xml", - "views/document_event_template.xml", - # Wizards - "wizards/document_cancel_wizard.xml", - "wizards/document_correction_wizard.xml", - "wizards/document_status_wizard.xml", - "wizards/invalidate_number_wizard.xml", - "wizards/document_import_wizard_mixin.xml", # Actions "views/l10n_br_fiscal_action.xml", # Menus diff --git a/l10n_br_fiscal/migrations/16.0.2.0.0/pre-migration.py b/l10n_br_fiscal/migrations/16.0.2.0.0/pre-migration.py new file mode 100644 index 000000000000..8a9b668b64c9 --- /dev/null +++ b/l10n_br_fiscal/migrations/16.0.2.0.0/pre-migration.py @@ -0,0 +1,25 @@ +# Copyright (C) 2024 - TODAY - Raphaël Valyi - Akretion +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + +to_install = "l10n_br_fiscal_edi" + + +def install_new_modules(cr): + sql = f""" + UPDATE ir_module_module + SET state='to install' + WHERE name = '{to_install}' AND state='uninstalled' + """ + openupgrade.logged_query(cr, sql) + + +@openupgrade.migrate() +def migrate(env, version): + install_new_modules(env.cr) + query = """ + DELETE FROM ir_model_fields + WHERE model = 'l10n_br_fiscal.document.electronic' + """ + openupgrade.logged_query(env.cr, query) diff --git a/l10n_br_fiscal/models/__init__.py b/l10n_br_fiscal/models/__init__.py index 26b4a953b2f4..b2c926450c67 100644 --- a/l10n_br_fiscal/models/__init__.py +++ b/l10n_br_fiscal/models/__init__.py @@ -3,15 +3,12 @@ from . import data_abstract from . import data_product_abstract from . import data_ncm_nbs_abstract -from . import document_workflow from . import document_fiscal_mixin_methods from . import document_fiscal_mixin_fields from . import document_fiscal_mixin from . import document_move_mixin from . import document_fiscal_line_mixin_methods from . import document_fiscal_line_mixin -from . import document_event -from . import document_eletronic from . import invalidate_number from . import comment from . import ibpt diff --git a/l10n_br_fiscal/models/document.py b/l10n_br_fiscal/models/document.py index 58e72ef08323..dfe625f30ca3 100644 --- a/l10n_br_fiscal/models/document.py +++ b/l10n_br_fiscal/models/document.py @@ -10,6 +10,7 @@ from odoo.exceptions import ValidationError from ..constants.fiscal import ( + DOCUMENT_ISSUER, DOCUMENT_ISSUER_COMPANY, DOCUMENT_ISSUER_DICT, DOCUMENT_ISSUER_PARTNER, @@ -20,10 +21,13 @@ MODELO_FISCAL_NFCE, MODELO_FISCAL_NFE, MODELO_FISCAL_NFSE, + SITUACAO_EDOC, SITUACAO_EDOC_AUTORIZADA, SITUACAO_EDOC_CANCELADA, SITUACAO_EDOC_DENEGADA, + SITUACAO_EDOC_EM_DIGITACAO, SITUACAO_EDOC_INUTILIZADA, + SITUACAO_FISCAL, ) @@ -49,7 +53,6 @@ class Document(models.Model): _name = "l10n_br_fiscal.document" _inherit = [ "l10n_br_fiscal.document.mixin.fields", - "l10n_br_fiscal.document.electronic", "l10n_br_fiscal.document.move.mixin", "mail.thread", ] @@ -62,6 +65,25 @@ class Document(models.Model): index=True, ) + state_edoc = fields.Selection( + selection=SITUACAO_EDOC, + string="Situação e-doc", + default=SITUACAO_EDOC_EM_DIGITACAO, + copy=False, + required=True, + readonly=True, + # tracking=True, + index=True, + ) + + state_fiscal = fields.Selection( + selection=SITUACAO_FISCAL, + string="Situação Fiscal", + copy=False, + # tracking=True, + index=True, + ) + fiscal_operation_id = fields.Many2one( domain="[('state', '=', 'approved'), " "'|', ('fiscal_operation_type', '=', fiscal_operation_type)," @@ -140,23 +162,6 @@ class Document(models.Model): default=EDOC_PURPOSE_NORMAL, ) - event_ids = fields.One2many( - comodel_name="l10n_br_fiscal.event", - inverse_name="document_id", - string="Events", - copy=False, - readonly=True, - ) - - correction_event_ids = fields.One2many( - comodel_name="l10n_br_fiscal.event", - inverse_name="document_id", - domain=[("type", "=", "14")], - string="Correction Events", - copy=False, - readonly=True, - ) - document_type = fields.Char( string="Document Type Code", related="document_type_id.code", @@ -171,10 +176,16 @@ class Document(models.Model): copy=False, ) - # Você não vai poder fazer isso em modelos que já tem state - # TODO Porque não usar o campo state do fiscal.document??? + # this related "state" field is required for the status bar widget + # while state_edoc avoids colliding with the state field + # of objects where the fiscal mixin might be injected. state = fields.Selection(related="state_edoc", string="State") + issuer = fields.Selection( + selection=DOCUMENT_ISSUER, + default=DOCUMENT_ISSUER_COMPANY, + ) + document_subsequent_ids = fields.One2many( comodel_name="l10n_br_fiscal.subsequent.document", inverse_name="source_document_id", @@ -404,6 +415,37 @@ def action_create_return(self): return action + # the following actions are meant to be implemented in other modules such as + # l10n_br_fiscal_edi. They are defined here so they can be overriden in modules + # that don't depend on l10n_br_fiscal_edi (such as l10n_br_account). + def view_pdf(self): + pass + + def view_xml(self): + pass + + def action_document_confirm(self): + pass + + def action_document_send(self): + pass + + def action_document_back2draft(self): + pass + + def action_document_cancel(self): + pass + + def action_document_invalidate(self): + pass + + def action_document_correction(self): + pass + + def exec_after_SITUACAO_EDOC_DENEGADA(self, old_state, new_state): + # see https://github.com/OCA/l10n-brazil/pull/3272 + pass + def _get_email_template(self, state): self.ensure_one() return self.document_type_id.document_email_ids.search( diff --git a/l10n_br_fiscal/models/invalidate_number.py b/l10n_br_fiscal/models/invalidate_number.py index 572de6b921a5..d16dbac8d3bd 100644 --- a/l10n_br_fiscal/models/invalidate_number.py +++ b/l10n_br_fiscal/models/invalidate_number.py @@ -82,50 +82,6 @@ class InvalidateNumber(models.Model): default="draft", ) - event_ids = fields.One2many( - comodel_name="l10n_br_fiscal.event", - inverse_name="invalidate_number_id", - string="Events", - readonly=True, - states={"done": [("readonly", True)]}, - ) - - # Authorization Event Related Fields - authorization_event_id = fields.Many2one( - comodel_name="l10n_br_fiscal.event", - string="Authorization Event", - readonly=True, - copy=False, - ) - - authorization_date = fields.Datetime( - string="Authorization Date", - readonly=True, - related="authorization_event_id.protocol_date", - ) - - authorization_protocol = fields.Char( - string="Authorization Protocol", - related="authorization_event_id.protocol_number", - readonly=True, - ) - - send_file_id = fields.Many2one( - comodel_name="ir.attachment", - related="authorization_event_id.file_request_id", - string="Send Document File XML", - ondelete="restrict", - readonly=True, - ) - - authorization_file_id = fields.Many2one( - comodel_name="ir.attachment", - related="authorization_event_id.file_response_id", - string="Authorization File XML", - ondelete="restrict", - readonly=True, - ) - @api.constrains("number_start", "number_end") def _check_range(self): for record in self: diff --git a/l10n_br_fiscal/security/ir.model.access.csv b/l10n_br_fiscal/security/ir.model.access.csv index 16644e4eb897..a7f680f0b2b5 100644 --- a/l10n_br_fiscal/security/ir.model.access.csv +++ b/l10n_br_fiscal/security/ir.model.access.csv @@ -93,14 +93,9 @@ "uom_alternative_user","UOM alternative for User","model_uom_uom_alternative","l10n_br_fiscal.group_user",1,0,0,0 "uom_alternative_manager","UOM alternative for Manager","model_uom_uom_alternative","l10n_br_fiscal.group_manager",1,0,0,0 "uom_alternative_maintenance","UOM alternative for Maintenance","model_uom_uom_alternative","l10n_br_fiscal.group_data_maintenance",1,1,1,1 -"l10n_br_fiscal_event_user","Fiscal Document Event for User","model_l10n_br_fiscal_event","l10n_br_fiscal.group_user",1,1,1,0 "l10n_br_fiscal_invalidate_number_user","user_l10n_br_fiscal_invalidate_number","model_l10n_br_fiscal_invalidate_number","l10n_br_fiscal.group_user",1,0,0,0 "l10n_br_fiscal_invalidate_number_manager","manager_l10n_br_fiscal_invalidate_number","model_l10n_br_fiscal_invalidate_number","l10n_br_fiscal.group_manager",1,1,1,1 "l10n_br_fiscal_city_taxation_code_user","Fiscal City Taxation Code for User","model_l10n_br_fiscal_city_taxation_code","l10n_br_fiscal.group_user",1,1,1,0 "l10n_br_fiscal_city_taxation_code_manager","Fiscal City Taxation Code for Manager","model_l10n_br_fiscal_city_taxation_code","l10n_br_fiscal.group_user",1,1,1,1 "l10n_br_fiscal_base_wizard_mixin_user",l10n_br_fiscal_base_wizard_mixin,model_l10n_br_fiscal_base_wizard_mixin,base.group_user,1,1,1,1 -"l10n_br_fiscal_document_cancel_wizard_user",l10n_br_fiscal_document_cancel_wizard,model_l10n_br_fiscal_document_cancel_wizard,base.group_user,1,1,1,1 -"l10n_br_fiscal_document_correction_wizard_user",l10n_br_fiscal_document_correction_wizard,model_l10n_br_fiscal_document_correction_wizard,base.group_user,1,1,1,1 "l10n_br_fiscal_document_status_wizard_user",l10n_br_fiscal_document_status_wizard,model_l10n_br_fiscal_document_status_wizard,base.group_user,1,1,1,1 -"l10n_br_fiscal_invalidate_number_wizard_user",l10n_br_fiscal_invalidate_number_wizard,model_l10n_br_fiscal_invalidate_number_wizard,base.group_user,1,1,1,1 -"l10n_br_fiscal_document_import_wizard_mixin_user",l10n_br_fiscal_document_import_wizard_mixin_user,model_l10n_br_fiscal_document_import_wizard_mixin,base.group_user,1,1,1,1 diff --git a/l10n_br_fiscal/tests/__init__.py b/l10n_br_fiscal/tests/__init__.py index 87f201269014..dae7f5062928 100644 --- a/l10n_br_fiscal/tests/__init__.py +++ b/l10n_br_fiscal/tests/__init__.py @@ -14,5 +14,4 @@ test_service_type, test_subsequent_operation, test_uom_uom, - test_workflow, ) diff --git a/l10n_br_fiscal/tests/test_fiscal_document_generic.py b/l10n_br_fiscal/tests/test_fiscal_document_generic.py index 385cbef63037..f2f6eb360f14 100644 --- a/l10n_br_fiscal/tests/test_fiscal_document_generic.py +++ b/l10n_br_fiscal/tests/test_fiscal_document_generic.py @@ -5,12 +5,6 @@ from odoo.tests import TransactionCase -from ..constants.fiscal import ( - SITUACAO_EDOC_A_ENVIAR, - SITUACAO_EDOC_AUTORIZADA, - SITUACAO_EDOC_CANCELADA, - SITUACAO_EDOC_EM_DIGITACAO, -) from ..constants.icms import ICMS_ORIGIN_TAX_IMPORTED @@ -172,26 +166,9 @@ def test_nfe_same_state(self): self.nfe_same_state.action_document_confirm() - self.assertEqual( - self.nfe_same_state.state_edoc, - SITUACAO_EDOC_A_ENVIAR, - "Document is not in To Sent state", - ) - - self.nfe_same_state.action_document_send() - - self.assertEqual( - self.nfe_same_state.state_edoc, - SITUACAO_EDOC_AUTORIZADA, - "Document is not in Authorized state", - ) - # Total value of the products self.assertEqual(self.nfe_same_state.amount_price_gross, 200) - result = self.nfe_same_state.action_document_cancel() - self.assertTrue(result) - def test_nfe_other_state(self): """Test NFe other state.""" @@ -1164,28 +1141,3 @@ def test_fields_freight_insurance_other_costs(self): "Unexpected value for the field" " Other Values in Fiscal Document line", ) - - def test_nfe_purchase_same_state(self): - self.nfe_purchase_same_state.action_document_confirm() - - self.assertEqual( - self.nfe_purchase_same_state.state_edoc, - SITUACAO_EDOC_AUTORIZADA, - "Document is not in Authorized state", - ) - - self.nfe_purchase_same_state.action_document_back2draft() - - self.assertEqual( - self.nfe_purchase_same_state.state_edoc, - SITUACAO_EDOC_EM_DIGITACAO, - "Document is not in Draft state", - ) - - self.nfe_purchase_same_state.action_document_cancel() - - self.assertEqual( - self.nfe_purchase_same_state.state_edoc, - SITUACAO_EDOC_CANCELADA, - "Document is not in Canceled state", - ) diff --git a/l10n_br_fiscal/tests/test_fiscal_document_nfse.py b/l10n_br_fiscal/tests/test_fiscal_document_nfse.py index 18f8b6d2683d..ca7f14493d4f 100644 --- a/l10n_br_fiscal/tests/test_fiscal_document_nfse.py +++ b/l10n_br_fiscal/tests/test_fiscal_document_nfse.py @@ -46,5 +46,3 @@ def test_nfse_same_state(self): "Error to mapping ICMS CST Tributada com permissão de crédito" " for Venda de Serviço de Contribuinte Dentro do Estado.", ) - - self.nfse_same_state.action_document_confirm() diff --git a/l10n_br_fiscal/tests/test_subsequent_operation.py b/l10n_br_fiscal/tests/test_subsequent_operation.py index 2f7ade24378c..ef8d6d69fe6d 100644 --- a/l10n_br_fiscal/tests/test_subsequent_operation.py +++ b/l10n_br_fiscal/tests/test_subsequent_operation.py @@ -33,7 +33,8 @@ def test_subsequent_operation_simple_faturamento(self): line._onchange_product_id_fiscal() line._onchange_fiscal_taxes() - self.nfe_simples_faturamento.action_document_confirm() + self.nfe_simples_faturamento.state_edoc = "a_enviar" + self.nfe_simples_faturamento._generates_subsequent_operations() subsequent_documents = self.nfe_simples_faturamento.document_subsequent_ids diff --git a/l10n_br_fiscal/tests/test_tax_benefit.py b/l10n_br_fiscal/tests/test_tax_benefit.py index a354f0c9a106..5a7e78ca688f 100644 --- a/l10n_br_fiscal/tests/test_tax_benefit.py +++ b/l10n_br_fiscal/tests/test_tax_benefit.py @@ -3,8 +3,6 @@ from odoo.tests import TransactionCase -from ..constants.fiscal import SITUACAO_EDOC_A_ENVIAR, SITUACAO_EDOC_AUTORIZADA - class TestTaxBenefit(TransactionCase): def setUp(self): @@ -53,22 +51,3 @@ def test_nfe_tax_benefit(self): self.tax_benefit, "Document line must have tax benefit", ) - - self.nfe_tax_benefit.action_document_confirm() - - self.assertEqual( - self.nfe_tax_benefit.state_edoc, - SITUACAO_EDOC_A_ENVIAR, - "Document is not in To Send state", - ) - - self.nfe_tax_benefit.action_document_send() - - self.assertEqual( - self.nfe_tax_benefit.state_edoc, - SITUACAO_EDOC_AUTORIZADA, - "Document is not in Authorized state", - ) - - result = self.nfe_tax_benefit.action_document_cancel() - self.assertTrue(result) diff --git a/l10n_br_fiscal/views/document_view.xml b/l10n_br_fiscal/views/document_view.xml index c90641fa15ea..2319770ff128 100644 --- a/l10n_br_fiscal/views/document_view.xml +++ b/l10n_br_fiscal/views/document_view.xml @@ -94,7 +94,7 @@
- +
- + - + - - - - -
diff --git a/l10n_br_fiscal/views/l10n_br_fiscal_action.xml b/l10n_br_fiscal/views/l10n_br_fiscal_action.xml index fb45ee42d652..94f398b5a926 100644 --- a/l10n_br_fiscal/views/l10n_br_fiscal_action.xml +++ b/l10n_br_fiscal/views/l10n_br_fiscal_action.xml @@ -647,40 +647,6 @@
- - - Fiscal Event - ir.actions.act_window - l10n_br_fiscal.event - kanban,tree,form - - - -

- Create a new Document -

- Odoo helps you easily track all activities - related to a fiscal operation. -

-
-
- - - - Invalidate Number - ir.actions.act_window - l10n_br_fiscal.invalidate.number - tree,form - -

- Create a new Document -

- Odoo helps you easily track all activities - related to a fiscal operation. -

-
-
- Settings diff --git a/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml b/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml index e38a9f9e4b58..ac59172b5f2d 100644 --- a/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml +++ b/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml @@ -86,25 +86,6 @@ sequence="30" /> - - - - - - `_. +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 +------- + +* Akretion +* KMEE + +Contributors +------------ + +- `Akretion `__: + + - Renato Lima + - Raphaël Valyi + +- `KMEE `__: + + - Luis Felipe Mileo + +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. + +.. |maintainer-renatonlima| image:: https://github.com/renatonlima.png?size=40px + :target: https://github.com/renatonlima + :alt: renatonlima +.. |maintainer-rvalyi| image:: https://github.com/rvalyi.png?size=40px + :target: https://github.com/rvalyi + :alt: rvalyi +.. |maintainer-mileo| image:: https://github.com/mileo.png?size=40px + :target: https://github.com/mileo + :alt: mileo + +Current `maintainers `__: + +|maintainer-renatonlima| |maintainer-rvalyi| |maintainer-mileo| + +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_fiscal_edi/__init__.py b/l10n_br_fiscal_edi/__init__.py new file mode 100644 index 000000000000..aee8895e7a31 --- /dev/null +++ b/l10n_br_fiscal_edi/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/l10n_br_fiscal_edi/__manifest__.py b/l10n_br_fiscal_edi/__manifest__.py new file mode 100644 index 000000000000..5c785112ecf0 --- /dev/null +++ b/l10n_br_fiscal_edi/__manifest__.py @@ -0,0 +1,41 @@ +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +{ + "name": "Common EDI fiscal features", + "summary": "Common EDI fiscal features", + "category": "Localisation", + "license": "AGPL-3", + "author": "Akretion, KMEE, Odoo Community Association (OCA)", + "maintainers": ["renatonlima", "rvalyi", "mileo"], + "website": "https://github.com/OCA/l10n-brazil", + "development_status": "Beta", + "version": "15.0.1.0.0", + "depends": [ + "l10n_br_fiscal", + ], + "data": [ + # security + "security/ir.model.access.csv", + # Views + "views/document_view.xml", + "views/invalidate_number_view.xml", + "views/document_event_view.xml", + "views/document_event_report.xml", + "views/document_event_template.xml", + # Reports + "views/document_event_report.xml", + "views/document_event_template.xml", + # Wizards + "wizards/document_cancel_wizard.xml", + "wizards/document_correction_wizard.xml", + "wizards/document_status_wizard.xml", + "wizards/invalidate_number_wizard.xml", + # Actions + "views/l10n_br_fiscal_action.xml", + # Menus + "views/l10n_br_fiscal_menu.xml", + # Wizards + "wizards/document_import_wizard_mixin.xml", + ], + "installable": True, +} diff --git a/l10n_br_fiscal_edi/models/__init__.py b/l10n_br_fiscal_edi/models/__init__.py new file mode 100644 index 000000000000..3a6af24fad34 --- /dev/null +++ b/l10n_br_fiscal_edi/models/__init__.py @@ -0,0 +1,4 @@ +from . import invalidate_number +from . import document_event +from . import document_workflow +from . import document diff --git a/l10n_br_fiscal/models/document_eletronic.py b/l10n_br_fiscal_edi/models/document.py similarity index 75% rename from l10n_br_fiscal/models/document_eletronic.py rename to l10n_br_fiscal_edi/models/document.py index 6024222b944b..fd0329dab1e0 100644 --- a/l10n_br_fiscal/models/document_eletronic.py +++ b/l10n_br_fiscal_edi/models/document.py @@ -5,7 +5,7 @@ from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError -from ..constants.fiscal import ( +from odoo.addons.l10n_br_fiscal.constants.fiscal import ( DOCUMENT_ISSUER, DOCUMENT_ISSUER_COMPANY, PROCESSADOR_NENHUM, @@ -19,10 +19,37 @@ def filter_processador(record): return False -class DocumentEletronic(models.AbstractModel): - _name = "l10n_br_fiscal.document.electronic" - _description = "Fiscal Eletronic Document" - _inherit = "l10n_br_fiscal.document.workflow" +class Document(models.Model): + """ + As of August 2024, this is the extraction of the legacy + l10n_br_fiscal.document.electronic mixin that was part of l10n_br_fiscal + from version 12 to 14. This code was made before the OCA/edi and OCA/edi-framework + and might easily be improved... + """ + + _name = "l10n_br_fiscal.document" + + _inherit = [ + "l10n_br_fiscal.document", + "l10n_br_fiscal.document.workflow", + ] + + event_ids = fields.One2many( + comodel_name="l10n_br_fiscal.event", + inverse_name="document_id", + string="Events", + copy=False, + readonly=True, + ) + + correction_event_ids = fields.One2many( + comodel_name="l10n_br_fiscal.event", + inverse_name="document_id", + domain=[("type", "=", "14")], + string="Correction Events", + copy=False, + readonly=True, + ) issuer = fields.Selection( selection=DOCUMENT_ISSUER, @@ -144,6 +171,37 @@ class DocumentEletronic(models.AbstractModel): copy=False, ) + # these workflow methods are plugged here so their interface defined in + # l10n_br_fiscal can easily be overriden in other modules. + def action_document_confirm(self): + super().action_document_confirm() + return self._document_confirm_to_send() + + def action_document_send(self): + super().action_document_send() + return self._action_document_send() + + def action_document_back2draft(self): + super().action_document_back2draft() + return self._action_document_back2draft() + + def action_document_cancel(self): + super().action_document_confirm() + return self._action_document_cancel() + + def action_document_invalidate(self): + super().action_document_invalidate() + return self._action_document_invalidate() + + def action_document_correction(self): + super().action_document_correction() + return self._action_document_correction() + + def exec_after_SITUACAO_EDOC_DENEGADA(self, old_state, new_state): + # see https://github.com/OCA/l10n-brazil/pull/3272 + super().exec_after_SITUACAO_EDOC_DENEGADA(old_state, new_state) + return self._exec_after_SITUACAO_EDOC_DENEGADA(old_state, new_state) + @api.depends("status_code", "status_name") def _compute_status_description(self): for record in self: @@ -199,6 +257,7 @@ def _target_new_tab(self, attachment_id): def view_xml(self): self.ensure_one() + super().view_xml() xml_file = self.authorization_file_id or self.send_file_id if not xml_file: self._document_export() @@ -212,6 +271,7 @@ def make_pdf(self): def view_pdf(self): self.ensure_one() + super().view_pdf() if not self.file_report_id or not self.authorization_file_id: self.make_pdf() if not self.file_report_id: diff --git a/l10n_br_fiscal/models/document_event.py b/l10n_br_fiscal_edi/models/document_event.py similarity index 98% rename from l10n_br_fiscal/models/document_event.py rename to l10n_br_fiscal_edi/models/document_event.py index f6bd5231d20d..9d5b907ae04f 100644 --- a/l10n_br_fiscal/models/document_event.py +++ b/l10n_br_fiscal_edi/models/document_event.py @@ -9,8 +9,8 @@ from odoo import _, api, fields, models from odoo.exceptions import UserError -from ..constants.fiscal import EVENT_ENVIRONMENT -from ..tools import build_edoc_path +from odoo.addons.l10n_br_fiscal.constants.fiscal import EVENT_ENVIRONMENT +from odoo.addons.l10n_br_fiscal.tools import build_edoc_path _logger = logging.getLogger(__name__) diff --git a/l10n_br_fiscal/models/document_workflow.py b/l10n_br_fiscal_edi/models/document_workflow.py similarity index 92% rename from l10n_br_fiscal/models/document_workflow.py rename to l10n_br_fiscal_edi/models/document_workflow.py index 8ee3f9d966e7..b703e227c1d1 100644 --- a/l10n_br_fiscal/models/document_workflow.py +++ b/l10n_br_fiscal_edi/models/document_workflow.py @@ -6,13 +6,12 @@ from odoo import _, api, fields, models from odoo.exceptions import UserError -from ..constants.fiscal import ( +from odoo.addons.l10n_br_fiscal.constants.fiscal import ( DOCUMENT_ISSUER_COMPANY, MODELO_FISCAL_CTE, MODELO_FISCAL_NFCE, MODELO_FISCAL_NFE, MODELO_FISCAL_NFSE, - SITUACAO_EDOC, SITUACAO_EDOC_A_ENVIAR, SITUACAO_EDOC_AUTORIZADA, SITUACAO_EDOC_CANCELADA, @@ -21,7 +20,6 @@ SITUACAO_EDOC_ENVIADA, SITUACAO_EDOC_INUTILIZADA, SITUACAO_EDOC_REJEITADA, - SITUACAO_FISCAL, SITUACAO_FISCAL_SPED_CONSIDERA_CANCELADO, WORKFLOW_DOCUMENTO_NAO_ELETRONICO, WORKFLOW_EDOC, @@ -29,28 +27,18 @@ class DocumentWorkflow(models.AbstractModel): + """ + This mixin encapsulates the fiscal_state and state_edoc transitions logic. + This is legacy code that was made by the KMEE company in version 10 + and included in the l10n_br_fiscal in version 12. The strange method names + with _exec_after_*/_exec_before_* reflect the old Odoo "low-code" + "state machine" for invoices that was customized in version 8 by Akretion. + So this legacy code can probably be improved a lot... + """ + _name = "l10n_br_fiscal.document.workflow" _description = "Fiscal Document Workflow" - state_edoc = fields.Selection( - selection=SITUACAO_EDOC, - string="Situação e-doc", - default=SITUACAO_EDOC_EM_DIGITACAO, - copy=False, - required=True, - readonly=True, - # tracking=True, - index=True, - ) - - state_fiscal = fields.Selection( - selection=SITUACAO_FISCAL, - string="Situação Fiscal", - copy=False, - # tracking=True, - index=True, - ) - cancel_reason = fields.Char() correction_reason = fields.Char() @@ -307,7 +295,7 @@ def _document_confirm(self): else: self._change_state(SITUACAO_EDOC_AUTORIZADA) - def action_document_confirm(self): + def _document_confirm_to_send(self): to_confirm = self.filtered(lambda inv: inv.state_edoc != SITUACAO_EDOC_A_ENVIAR) if to_confirm: to_confirm._document_confirm() @@ -318,7 +306,7 @@ def _no_eletronic_document_send(self): def _document_export(self): pass - def action_document_send(self): + def _action_document_send(self): to_send = self.filtered( lambda d: d.state_edoc in ( @@ -330,7 +318,7 @@ def action_document_send(self): if to_send: to_send._document_send() - def action_document_back2draft(self): + def document_back2draft(self): self.xml_error_message = False self.file_report_id = False if self.issuer == DOCUMENT_ISSUER_COMPANY: @@ -338,24 +326,27 @@ def action_document_back2draft(self): else: self.state_edoc = SITUACAO_EDOC_EM_DIGITACAO + def _action_document_back2draft(self): + self.document_back2draft() + def _document_cancel(self, justificative): self.ensure_one() self.cancel_reason = justificative if self._change_state(SITUACAO_EDOC_CANCELADA): self.cancel_reason = justificative - def action_document_cancel(self): + def _action_document_cancel(self): self.ensure_one() if self.issuer == DOCUMENT_ISSUER_COMPANY: if self.state_edoc == SITUACAO_EDOC_AUTORIZADA: result = self.env["ir.actions.act_window"]._for_xml_id( - "l10n_br_fiscal.document_cancel_wizard_action" + "l10n_br_fiscal_edi.document_cancel_wizard_action" ) return result else: self.state_edoc = SITUACAO_EDOC_CANCELADA - def action_document_invalidate(self): + def _action_document_invalidate(self): self.ensure_one() if ( self.document_number @@ -369,7 +360,7 @@ def action_document_invalidate(self): and self.issuer == DOCUMENT_ISSUER_COMPANY ): return self.env["ir.actions.act_window"]._for_xml_id( - "l10n_br_fiscal.invalidate_number_wizard_action" + "l10n_br_fiscal_edi.invalidate_number_wizard_action" ) else: raise UserError(_("You cannot invalidate this document")) @@ -378,14 +369,14 @@ def _document_correction(self, justificative): self.ensure_one() self.correction_reason = justificative - def action_document_correction(self): + def _action_document_correction(self): self.ensure_one() if ( self.state_edoc in SITUACAO_EDOC_AUTORIZADA and self.issuer == DOCUMENT_ISSUER_COMPANY ): return self.env["ir.actions.act_window"]._for_xml_id( - "l10n_br_fiscal.document_correction_wizard_action" + "l10n_br_fiscal_edi.document_correction_wizard_action" ) else: raise UserError( diff --git a/l10n_br_fiscal_edi/models/invalidate_number.py b/l10n_br_fiscal_edi/models/invalidate_number.py new file mode 100644 index 000000000000..93df4d422218 --- /dev/null +++ b/l10n_br_fiscal_edi/models/invalidate_number.py @@ -0,0 +1,53 @@ +# Copyright (C) 2009 - TODAY Renato Lima - Akretion +# Copyright (C) 2014 KMEE - www.kmee.com.br +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class InvalidateNumber(models.Model): + _inherit = "l10n_br_fiscal.invalidate.number" + + event_ids = fields.One2many( + comodel_name="l10n_br_fiscal.event", + inverse_name="invalidate_number_id", + string="Events", + readonly=True, + states={"done": [("readonly", True)]}, + ) + + # Authorization Event Related Fields + authorization_event_id = fields.Many2one( + comodel_name="l10n_br_fiscal.event", + string="Authorization Event", + readonly=True, + copy=False, + ) + + authorization_date = fields.Datetime( + string="Authorization Date", + readonly=True, + related="authorization_event_id.protocol_date", + ) + + authorization_protocol = fields.Char( + string="Authorization Protocol", + related="authorization_event_id.protocol_number", + readonly=True, + ) + + send_file_id = fields.Many2one( + comodel_name="ir.attachment", + related="authorization_event_id.file_request_id", + string="Send Document File XML", + ondelete="restrict", + readonly=True, + ) + + authorization_file_id = fields.Many2one( + comodel_name="ir.attachment", + related="authorization_event_id.file_response_id", + string="Authorization File XML", + ondelete="restrict", + readonly=True, + ) diff --git a/l10n_br_fiscal_edi/pyproject.toml b/l10n_br_fiscal_edi/pyproject.toml new file mode 100644 index 000000000000..4231d0cccb3d --- /dev/null +++ b/l10n_br_fiscal_edi/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/l10n_br_fiscal_edi/readme/CONTRIBUTORS.md b/l10n_br_fiscal_edi/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..5a5cb61fe19b --- /dev/null +++ b/l10n_br_fiscal_edi/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- [Akretion](https://www.akretion.com/pt-BR): + - Renato Lima \<\> + - Raphaël Valyi \<\> +- [KMEE](https://www.kmee.com.br): + - Luis Felipe Mileo \<\> diff --git a/l10n_br_fiscal_edi/readme/DESCRIPTION.md b/l10n_br_fiscal_edi/readme/DESCRIPTION.md new file mode 100644 index 000000000000..b0831a0d91f6 --- /dev/null +++ b/l10n_br_fiscal_edi/readme/DESCRIPTION.md @@ -0,0 +1,6 @@ +Este módulo estende o módulo `l10n_br_fiscal` e cuida da parte de EDI +(Electronic Data Interchange) que é comum entre os vários documentos +fiscais no Brasil. Ele introduz os modelos de eventos de transmissão, de +correções... Alem disso ele cuida das possíveis transições de estado do +documento fiscal em função dos retornos dos webservices (campo +`state_edoc`). diff --git a/l10n_br_fiscal_edi/readme/ROADMAP.md b/l10n_br_fiscal_edi/readme/ROADMAP.md new file mode 100644 index 000000000000..84a7536604e0 --- /dev/null +++ b/l10n_br_fiscal_edi/readme/ROADMAP.md @@ -0,0 +1,6 @@ +O código deste módulo foi feito antes dos repos `OCA/edi` e +`OCA/edi-framework`. O código do `document_workflow.py` por exemplo foi +uma espécie de tradução em código do "workflow de state machine" que +tinha sido customizado para a NFe na versão 8.0 mas que teve que ser +re-escrito quando o engine de workflow foi removido na versão 10.0. +Nisso o código deste módulo tem bastante possibilidades de melhorias... diff --git a/l10n_br_fiscal_edi/readme/USAGE.md b/l10n_br_fiscal_edi/readme/USAGE.md new file mode 100644 index 000000000000..12822ea2fce8 --- /dev/null +++ b/l10n_br_fiscal_edi/readme/USAGE.md @@ -0,0 +1,7 @@ +Use os botões na barra de header do documento fiscal para alterar o +estado do documento fiscal, para abrir os wizards e para interagir com a +fazenda... Quando o módulo `l10n_br_account` ou alguns módulos de +documentos fiscais específicos como `l10n_br_nfe` ou `l10n_br_nfse` são +instalados, alguns métodos de transição de estado do módulo +`l10n_br_fiscal_edi` são chamados automaticamente, por exemplo ao +confirmar ou cancelar uma nota. diff --git a/l10n_br_fiscal_edi/security/ir.model.access.csv b/l10n_br_fiscal_edi/security/ir.model.access.csv new file mode 100644 index 000000000000..5cd21fd08f2e --- /dev/null +++ b/l10n_br_fiscal_edi/security/ir.model.access.csv @@ -0,0 +1,7 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"l10n_br_fiscal_event_user","Fiscal Document Event for User","model_l10n_br_fiscal_event","l10n_br_fiscal.group_user",1,1,1,0 +"l10n_br_fiscal_invalidate_number_user","user_l10n_br_fiscal_invalidate_number","model_l10n_br_fiscal_invalidate_number","l10n_br_fiscal.group_user",1,0,0,0 +"l10n_br_fiscal_invalidate_number_manager","manager_l10n_br_fiscal_invalidate_number","model_l10n_br_fiscal_invalidate_number","l10n_br_fiscal.group_manager",1,1,1,1 +"l10n_br_fiscal_document_cancel_wizard_user",l10n_br_fiscal_document_cancel_wizard,model_l10n_br_fiscal_document_cancel_wizard,base.group_user,1,1,1,1 +"l10n_br_fiscal_document_correction_wizard_user",l10n_br_fiscal_document_correction_wizard,model_l10n_br_fiscal_document_correction_wizard,base.group_user,1,1,1,1 +"l10n_br_fiscal_document_import_wizard_mixin_user",l10n_br_fiscal_document_import_wizard_mixin_user,model_l10n_br_fiscal_document_import_wizard_mixin,base.group_user,1,1,1,1 diff --git a/l10n_br_fiscal_edi/static/description/index.html b/l10n_br_fiscal_edi/static/description/index.html new file mode 100644 index 000000000000..8131c3de1aa0 --- /dev/null +++ b/l10n_br_fiscal_edi/static/description/index.html @@ -0,0 +1,460 @@ + + + + + +common EDI fiscal features + + + +
+

common EDI fiscal features

+ + +

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

+

Este módulo estende o módulo l10n_br_fiscal e cuida da parte de EDI +(Electronic Data Interchange) que é comum entre os vários documentos +fiscais no Brasil. Ele introduz os modelos de eventos de transmissão, de +correções… Alem disso ele cuida das possíveis transições de estado do +documento fiscal em função dos retornos dos webservices (campo +state_edoc).

+

Table of contents

+ +
+

Usage

+

Use os botões na barra de header do documento fiscal para alterar o +estado do documento fiscal, para abrir os wizards e para interagir com a +fazenda… Quando o módulo l10n_br_account ou alguns módulos de +documentos fiscais específicos como l10n_br_nfe ou l10n_br_nfse +são instalados, alguns métodos de transição de estado do módulo +l10n_br_fiscal_edi são chamados automaticamente, por exemplo ao +confirmar ou cancelar uma nota.

+
+
+

Known issues / Roadmap

+

O código deste módulo foi feito antes dos repos OCA/edi e +OCA/edi-framework. O código do document_workflow.py por exemplo +foi uma espécie de tradução em código do “workflow de state machine” que +tinha sido customizado para a NFe na versão 8.0 mas que teve que ser +re-escrito quando o engine de workflow foi removido na versão 10.0. +Nisso o código deste módulo tem bastante possibilidades de melhorias…

+
+
+

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

+
    +
  • Akretion
  • +
  • KMEE
  • +
+
+
+

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.

+

Current maintainers:

+

renatonlima rvalyi mileo

+

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_fiscal_edi/tests/__init__.py b/l10n_br_fiscal_edi/tests/__init__.py new file mode 100644 index 000000000000..35e4b63aa62a --- /dev/null +++ b/l10n_br_fiscal_edi/tests/__init__.py @@ -0,0 +1 @@ +from . import test_workflow diff --git a/l10n_br_fiscal_edi/tests/test_fiscal_document_generic.py b/l10n_br_fiscal_edi/tests/test_fiscal_document_generic.py new file mode 100644 index 000000000000..9f2c6bbc9b38 --- /dev/null +++ b/l10n_br_fiscal_edi/tests/test_fiscal_document_generic.py @@ -0,0 +1,1191 @@ +# @ 2020 Akretion - www.akretion.com.br - +# Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +from odoo.tests import SavepointCase + +from ..constants.fiscal import ( + SITUACAO_EDOC_A_ENVIAR, + SITUACAO_EDOC_AUTORIZADA, + SITUACAO_EDOC_CANCELADA, + SITUACAO_EDOC_EM_DIGITACAO, +) +from ..constants.icms import ICMS_ORIGIN_TAX_IMPORTED + + +class TestFiscalDocumentGeneric(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Contribuinte + cls.nfe_same_state = cls.env.ref("l10n_br_fiscal.demo_nfe_same_state") + cls.nfe_other_state = cls.env.ref("l10n_br_fiscal.demo_nfe_other_state") + cls.nfe_not_taxpayer = cls.env.ref("l10n_br_fiscal.demo_nfe_nao_contribuinte") + + cls.nfe_not_taxpayer_pf = cls.env.ref( + "l10n_br_fiscal.demo_nfe_nao_contribuinte_pf" + ) + + cls.nfe_export = cls.env.ref("l10n_br_fiscal.demo_nfe_export") + + # Simples Nacional + cls.nfe_sn_same_state = cls.env.ref("l10n_br_fiscal.demo_nfe_sn_same_state") + cls.nfe_sn_other_state = cls.env.ref("l10n_br_fiscal.demo_nfe_sn_other_state") + cls.nfe_sn_not_taxpayer = cls.env.ref( + "l10n_br_fiscal.demo_nfe_sn_nao_contribuinte" + ) + cls.nfe_sn_export = cls.env.ref("l10n_br_fiscal.demo_nfe_sn_export") + + # Compra + cls.nfe_purchase_same_state = cls.env.ref( + "l10n_br_fiscal.demo_nfe_purchase_same_state" + ) + + def test_nfe_same_state(self): + """Test NFe same state.""" + + self.nfe_same_state._onchange_document_serie_id() + self.nfe_same_state._onchange_fiscal_operation_id() + + for line in self.nfe_same_state.fiscal_line_ids: + # Save the original price_unit value of the line as defined in + # the NFe demo data. + original_price_unit = line.price_unit + + line._onchange_product_id_fiscal() + + # Restore the original price_unit value, + # as the product change might have altered it. + line.price_unit = original_price_unit + + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "5102", + "Error to mappping CFOP 5102" + " for Revenda de Contribuinte Dentro do Estado.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "5101", + "Error to mapping CFOP 5101" + " for Venda de Contribuinte Dentro do Estado.", + ) + + icms_internal_sp = [ + self.env.ref("l10n_br_fiscal.tax_icms_4"), + self.env.ref("l10n_br_fiscal.tax_icms_7"), + self.env.ref("l10n_br_fiscal.tax_icms_12"), + self.env.ref("l10n_br_fiscal.tax_icms_18"), + self.env.ref("l10n_br_fiscal.tax_icms_25"), + ] + + is_icms_internal = line.icms_tax_id in icms_internal_sp + + # ICMS + self.assertTrue( + is_icms_internal, + "Error to mapping ICMS Inernal for {}" + " for Venda de Contribuinte Dentro do " + "Estado.".format(self.nfe_same_state.partner_id.state_id.name), + ) + self.assertEqual( + line.icms_cst_id.code, + "00", + "Error to mapping CST 00 from ICMS 12%" + " for Venda de Contribuinte Dentro do Estado.", + ) + + # ICMS FCP + self.assertFalse( + line.icmsfcp_tax_id, + "Error to mapping ICMS FCP 2%" + " for Venda de Contribuinte Dentro do Estado.", + ) + + # IPI + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.ipi_tax_id.name, + "IPI NT", + "Error to mapping IPI NT" + " for Revenda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "53", + "Error to mapping CST 53 from IPI NT" + " to Revenda de Contribuinte Dentro do Estado.", + ) + else: + self.assertEqual( + line.ipi_tax_id.name, + "IPI 5%", + "Error to mapping IPI 5%" + " for Venda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "50", + "Error to mapping CST 50 from IPI 5%" + " to Venda de Contribuinte Dentro do Estado.", + ) + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS 0,65%", + "Error to mapping PIS 0,65%" + " for Venda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.pis_cst_id.code, + "01", + "Error to mapping CST 01 - Operação Tributável com Alíquota" + " Básica from PIS 0,65% to Venda de Contribuinte Dentro do Estado.", + ) + + # PIS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS 3%", + "Error to mapping COFINS 3%" + " for Venda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "01", + "Error to mapping CST 01 - Operação Tributável com Alíquota Básica" + " Básica to COFINS 3% de Venda de Contribuinte Dentro do Estado.", + ) + + product_total = line.price_unit * line.quantity + self.assertEqual(line.price_gross, product_total) + + # self.nfe_same_state.action_document_confirm() + + self.assertEqual( + self.nfe_same_state.state_edoc, + SITUACAO_EDOC_A_ENVIAR, + "Document is not in To Sent state", + ) + + self.nfe_same_state.action_document_send() + + # self.assertEqual( + # self.nfe_same_state.state_edoc, + # SITUACAO_EDOC_AUTORIZADA, + # "Document is not in Authorized state", + # ) + + # Total value of the products + # self.assertEqual(self.nfe_same_state.amount_price_gross, 200) + + # result = self.nfe_same_state.action_document_cancel() + # self.assertTrue(result) + + def test_nfe_other_state(self): + """Test NFe other state.""" + + self.nfe_other_state._onchange_document_serie_id() + self.nfe_other_state._onchange_fiscal_operation_id() + + for line in self.nfe_other_state.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "6102", + "Error to mapping CFOP 6102" + " for Revenda de Contribuinte p/ Fora do Estado.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "6101", + "Error to mapping CFOP 6101" + " for Venda de Contribuinte p/ Fora do Estado.", + ) + + # ICMS + if line.product_id.icms_origin in ICMS_ORIGIN_TAX_IMPORTED: + self.assertEqual( + line.icms_tax_id.name, + "ICMS 4%", + "Error to mapping ICMS 4%" + " for Venda de Contribuinte p/ Fora do Estado.", + ) + self.assertEqual( + line.icms_cst_id.code, + "00", + "Error to mapping CST 00 from ICMS 4%" + " for Venda de Contribuinte p/ Fora do Estado.", + ) + else: + self.assertEqual( + line.icms_tax_id.name, + "ICMS 7%", + "Error to mapping ICMS 7%" + " for Venda de Contribuinte p/ Fora do Estado.", + ) + self.assertEqual( + line.icms_cst_id.code, + "00", + "Error to mapping CST 00 from ICMS 7%" + " for Venda de Contribuinte p/ Fora do Estado.", + ) + + # ICMS FCP + self.assertFalse( + line.icmsfcp_tax_id, + "Error to mapping ICMS FCP 2%" + " for Venda de Contribuinte Dentro do Estado.", + ) + + # IPI + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.ipi_tax_id.name, + "IPI NT", + "Error to mapping IPI NT" + " for Revenda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "53", + "Error to mapping CST 53 from IPI NT" + " to Revenda de Contribuinte Dentro do Estado.", + ) + else: + self.assertEqual( + line.ipi_tax_id.name, + "IPI 5%", + "Error to mapping IPI 5%" + " for Venda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "50", + "Error to mapping CST 50 from IPI 5%" + " to Venda de Contribuinte Dentro do Estado.", + ) + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS 0,65%", + "Error to mapping PIS 0,65%" + " for Venda de Contribuinte p/ Fora do Estado.", + ) + self.assertEqual( + line.pis_cst_id.code, + "01", + "Error to mapping CST 01 - Operação Tributável com Alíquota" + " Básica from PIS 0,65% for" + " Venda de Contribuinte p/ Fora do Estado.", + ) + + # PIS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS 3%", + "Error to mapping COFINS 3%" + " for Venda de Contribuinte p/ Fora do Estado.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "01", + "Error to mapping CST 01 -" + " Operação Tributável com Alíquota Básica" + "from COFINS 3% for Venda de Contribuinte p/ Fora do Estado.", + ) + + def test_nfe_not_taxpayer(self): + """Test NFe not taxpayer.""" + + self.nfe_not_taxpayer._onchange_document_serie_id() + self.nfe_not_taxpayer._onchange_fiscal_operation_id() + + for line in self.nfe_not_taxpayer.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "6108", + "Error to mapping CFOP 6108" + " for Revenda de Contribuinte p/ Não Contribuinte.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "6107", + "Error to mapping CFOP 6107" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + + # ICMS + self.assertEqual( + line.icms_tax_id.name, + "ICMS 12%", + "Error to mapping ICMS 12%" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + self.assertEqual( + line.icms_cst_id.code, + "00", + "Error to mapping CST 00 from ICMS 12%" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + + # ICMS FCP + self.assertEqual( + line.icmsfcp_tax_id.name, + "FCP 2%", + "Erro ao mapear ICMS FCP 2%" + " para Venda de Contribuinte p/ Não Contribuinte.", + ) + + # IPI + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.ipi_tax_id.name, + "IPI NT", + "Error to mapping IPI NT" + " for Revenda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "53", + "Error to mapping CST 53 from IPI NT" + " to Revenda de Contribuinte Dentro do Estado.", + ) + else: + self.assertEqual( + line.ipi_tax_id.name, + "IPI 5%", + "Error to mapping IPI 5%" + " for Venda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "50", + "Error to mapping CST 50 from IPI 5%" + " to Venda de Contribuinte Dentro do Estado.", + ) + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS 0,65%", + "Error to mapping PIS 0,65%" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + self.assertEqual( + line.pis_cst_id.code, + "01", + "Error to mapping CST 01 - Operação Tributável com Alíquota" + " Básica from PIS 0,65% for" + " Venda de Contribuinte p/ Não Contribuinte.", + ) + + # PIS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS 3%", + "Error to mapping COFINS 3%" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "01", + "Error to mapping CST 01 -" + " Operação Tributável com Alíquota Básica" + "from COFINS 3% for Venda de Contribuinte p/ Não Contribuinte.", + ) + + def test_nfe_not_taxpayer_not_company(self): + """Test NFe not taxpayer not Company.""" + + self.nfe_not_taxpayer_pf._onchange_document_serie_id() + self.nfe_not_taxpayer_pf._onchange_fiscal_operation_id() + + for line in self.nfe_not_taxpayer_pf.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "6108", + "Error to mapping CFOP 6108" + " for Revenda de Contribuinte p/ Não Contribuinte.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "6107", + "Error to mapping CFOP 6107" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + + # ICMS + self.assertEqual( + line.icms_tax_id.name, + "ICMS 12%", + "Error to mapping ICMS 12%" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + self.assertEqual( + line.icms_cst_id.code, + "00", + "Error to mapping CST 00 from ICMS 12%" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + + # ICMS FCP + self.assertEqual( + line.icmsfcp_tax_id.name, + "FCP 2%", + "Erro ao mapear ICMS FCP 2%" + " para Venda de Contribuinte p/ Não Contribuinte.", + ) + + # IPI + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.ipi_tax_id.name, + "IPI NT", + "Error to mapping IPI NT" + " for Revenda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "53", + "Error to mapping CST 53 from IPI NT" + " to Revenda de Contribuinte Dentro do Estado.", + ) + else: + self.assertEqual( + line.ipi_tax_id.name, + "IPI 5%", + "Error to mapping IPI 5%" + " for Venda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "50", + "Error to mapping CST 50 from IPI 5%" + " to Venda de Contribuinte Dentro do Estado.", + ) + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS 0,65%", + "Error to mapping PIS 0,65%" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + self.assertEqual( + line.pis_cst_id.code, + "01", + "Error to mapping CST 01 - Operação Tributável com Alíquota" + " Básica from PIS 0,65% for" + " Venda de Contribuinte p/ Não Contribuinte.", + ) + + # PIS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS 3%", + "Error to mapping COFINS 3%" + " for Venda de Contribuinte p/ Não Contribuinte.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "01", + "Error to mapping CST 01 -" + " Operação Tributável com Alíquota Básica" + "from COFINS 3% for Venda de Contribuinte p/ Não Contribuinte.", + ) + + def test_nfe_export(self): + """Test NFe export.""" + + self.nfe_export._onchange_document_serie_id() + self.nfe_export._onchange_fiscal_operation_id() + + for line in self.nfe_export.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "7102", + "Error to mapping CFOP 7102" + " for Revenda de Contribuinte p/ o Exterior.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "7101", + "Error to mapping CFOP 7101" + " for Venda de Contribuinte p/ o Exterior.", + ) + + # ICMS - TODO field missing + # self.assertEqual( + # line.icms_tax_id.name, 'ICMS 7%', + # "Error to mapping ICMS 7%" + # " for Venda de Contribuinte p/ o Exterior.") + # self.assertEqual( + # line.icms_cst_id.code, '00', + # "Error to mapping CST 00 from ICMS 7%" + # " for Venda de Contribuinte p/ o Exterior.") + + # ICMS FCP + # self.assertEqual( + # line.icmsfcp_tax_id.name, 'FCP 2%', + # "Erro ao mapear ICMS FCP 2%" + # " para Venda de Contribuinte p/ o Exterior.") + + # IPI + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.ipi_tax_id.name, + "IPI NT", + "Error to mapping IPI NT" + " for Revenda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "53", + "Error to mapping CST 53 from IPI NT" + " to Revenda de Contribuinte Dentro do Estado.", + ) + else: + self.assertEqual( + line.ipi_tax_id.name, + "IPI 5%", + "Error to mapping IPI 5%" + " for Venda de Contribuinte Dentro do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "50", + "Error to mapping CST 50 from IPI 5%" + " to Venda de Contribuinte Dentro do Estado.", + ) + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS 0,65%", + "Error to mapping PIS 0,65%" + " for Venda de Contribuinte p/ o Exterior.", + ) + self.assertEqual( + line.pis_cst_id.code, + "01", + "Error to mapping CST 01 - Operação Tributável com Alíquota" + " Básica from PIS 0,65% for" + " Venda de Contribuinte p/ o Exterior.", + ) + + # PIS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS 3%", + "Error to mapping COFINS 3%" + " for Venda de Contribuinte p/ o Exterior.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "01", + "Error to mapping CST 01 -" + " Operação Tributável com Alíquota Básica" + "from COFINS 3% for Venda de Contribuinte p/ o Exterior.", + ) + + def test_nfe_sn_same_state(self): + """Test NFe Simples Nacional same state.""" + + self.nfe_sn_same_state._onchange_document_serie_id() + self.nfe_sn_same_state._onchange_fiscal_operation_id() + + for line in self.nfe_sn_same_state.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + + # set fake estimate tax + line.ncm_id.tax_estimate_ids.create( + { + "ncm_id": line.ncm_id.id, + "state_id": line.company_id.state_id.id, + "key": "fake estimate tax", + "origin": "fake estimate tax", + "federal_taxes_national": 33.00, + } + ) + + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "5102", + "Error to mappping CFOP 5102" + " for Revenda de Simples Nacional Dentro do Estado.", + ) + # IPI + self.assertEqual( + line.ipi_tax_id.name, + "IPI NT", + "Error to mapping IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "53", + "Error to mapping CST 53 from IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "5101", + "Error to mapping CFOP 5101" + " for Venda de Simples Nacional Dentro do Estado.", + ) + # IPI + self.assertEqual( + line.ipi_tax_id.name, + "IPI Outros", + "Error to mapping IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "99", + "Error to mapping CST 99 from IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + + # ICMS + self.assertEqual( + line.icmssn_tax_id.name, + "ICMS SN Com Permissão de Crédito", + "Error to mapping ICMS SN Com Permissão de Crédito" + " for Venda de Simples Nacional Dentro do Estado.", + ) + self.assertEqual( + line.icms_cst_id.code, + "101", + "Error to mapping CST 101 do ICMS SN Com Permissão de Crédito" + " for Venda de Simples Nacional Dentro do Estado.", + ) + + # ICMS FCP - TODO mapping failed + # self.assertEqual( + # line.icmsfcp_tax_id.name, 'FCP 2%', + # "Erro ao mapear ICMS FCP 2%" + # " para Venda de Simples Nacional Dentro do Estado.") + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS Outros", + "Error to mapping PIS Simples Nacional" + " for Venda de Simples Nacional Dentro do Estado.", + ) + self.assertEqual( + line.pis_cst_id.code, + "49", + "Error to mapping CST 49 Outras Operações de Saída" + " from PIS Simples Nacional from Venda de" + " Simples Nacional Dentro do Estado.", + ) + + # COFINS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS Outros", + "Error to mapping COFINS Simples Nacional" + " for Venda de Simples Nacional Dentro do Estado.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "49", + "Error to mapping CST 49 Outras Operações de Saída" + " from COFINS Simples Nacional for Venda de" + " Simples Nacional Fora do Estado.", + ) + + # ESTIMATE TAXES + self.assertEqual(self.nfe_sn_same_state.amount_estimate_tax, 1308.45) + + def test_nfe_sn_other_state(self): + """Test NFe SN other state.""" + + self.nfe_sn_other_state._onchange_document_serie_id() + self.nfe_sn_other_state._onchange_fiscal_operation_id() + + for line in self.nfe_sn_other_state.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "6102", + "Error to mappping CFOP 6102" + " for Revenda de Simples Nacional Fora do Estado.", + ) + # IPI + self.assertEqual( + line.ipi_tax_id.name, + "IPI NT", + "Error to mapping IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "53", + "Error to mapping CST 53 from IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "6101", + "Error to mapping CFOP 6101" + " for Venda de Simples Nacional Fora do Estado.", + ) + # IPI + self.assertEqual( + line.ipi_tax_id.name, + "IPI Outros", + "Error to mapping IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "99", + "Error to mapping CST 99 from IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + + # ICMS + self.assertEqual( + line.icmssn_tax_id.name, + "ICMS SN Com Permissão de Crédito", + "Error to mapping ICMS SN Com Permissão de Crédito" + " for Venda de Simples Nacional Dentro do Estado.", + ) + self.assertEqual( + line.icms_cst_id.code, + "101", + "Erro ao mapear a CST 101 do ICMS SN Com Permissão de Crédito" + " para Venda de Simples Nacional Dentro do Estado.", + ) + + # ICMS FCP - TODO mapping failed + # self.assertEqual( + # line.icmsfcp_tax_id.name, 'FCP 2%', + # "Erro ao mapear ICMS FCP 2%" + # " para Venda de Simples Nacional Fora do Estado.") + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS Outros", + "Erro ao mapear PIS Simples Nacional" + " para Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.pis_cst_id.code, + "49", + "Erro ao mapear a CST 49 Outras Operações de Saída" + " com Alíquota Básica do PIS Simples Nacional de Venda de" + " Simples Nacional Dentro do Estado.", + ) + + # COFINS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS Outros", + "Error to mapping COFINS Simples Nacional" + " for Venda de Simples Nacional Dentro do Estado.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "49", + "Error to mapping CST 49 Outras Operações de Saída" + " from COFINS Simples Nacional for Venda de" + " Simples Nacional Fora do Estado.", + ) + + def test_nfe_sn_not_taxpayer(self): + """Test NFe SN not taxpayer.""" + + self.nfe_sn_not_taxpayer._onchange_document_serie_id() + self.nfe_sn_not_taxpayer._onchange_fiscal_operation_id() + + for line in self.nfe_sn_not_taxpayer.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "5102", + "Error to mappping CFOP 5102" + " for Revenda de Simples Nacional Fora do Estado.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "5101", + "Error to mapping CFOP 5101" + " for Venda de Simples Nacional Fora do Estado.", + ) + + # ICMS + self.assertEqual( + line.icms_tax_id.name, + "ICMS 18%", + "Error to mapping ICMS 18%" + " for Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.icms_cst_id.code, + "00", + "Erro ao mapear a CST 00 do ICMS 18%" + " para Venda de Simples Nacional Fora do Estado.", + ) + + # ICMS FCP + # self.assertEqual( + # line.icmsfcp_tax_id.name, 'FCP 2%', + # "Erro ao mapear ICMS FCP 2%" + # " para Venda de Simples Nacional Fora do Estado.") + + # IPI + self.assertEqual( + line.ipi_tax_id.name, + "IPI 5%", + "Erro ao mapear IPI 5%" + " para Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "50", + "Erro ao mapear a CST 50 do IPI 5%" + " de Venda de Simples Nacional Fora do Estado.", + ) + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS 0,65%", + "Erro ao mapear PIS 0,65%" + " para Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.pis_cst_id.code, + "01", + "Erro ao mapear a CST 01 - Operação Tributável" + " com Alíquota Básica do PIS 0,65% de Venda de" + " Simples Nacional Fora do Estado.", + ) + + # PIS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS 3%", + "Erro ao mapear COFINS 3%" + " para Venda de Simples Nacional Dentro do Estado.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "01", + "Erro ao mapear a CST 01 - Operação Tributável" + " com Alíquota Básica do COFINS 3% de Venda de" + " Simples Nacional Fora do Estado.", + ) + + def test_nfe_sn_export(self): + """Test NFe SN export.""" + + self.nfe_sn_export._onchange_document_serie_id() + self.nfe_sn_export._onchange_fiscal_operation_id() + + for line in self.nfe_sn_export.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + if "Revenda" in line.fiscal_operation_line_id.name: + self.assertEqual( + line.cfop_id.code, + "7102", + "Error to mapping CFOP 7102" + " for Revenda de Contribuinte p/ o Exterior.", + ) + # IPI + self.assertEqual( + line.ipi_tax_id.name, + "IPI NT", + "Error to mapping IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "53", + "Error to mapping CST 53 from IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + else: + self.assertEqual( + line.cfop_id.code, + "7101", + "Error to mapping CFOP 7101" + " for Venda de Contribuinte p/ o Exterior.", + ) + # IPI + self.assertEqual( + line.ipi_tax_id.name, + "IPI Outros", + "Error to mapping IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.ipi_cst_id.code, + "99", + "Error to mapping CST 99 from IPI Simples Nacional" + " for Venda de Simples Nacional Fora do Estado.", + ) + + # ICMS + self.assertEqual( + line.icmssn_tax_id.name, + "ICMS SN Com Permissão de Crédito", + "Error to mapping ICMS SN Com Permissão de Crédito" + " for Venda de Simples Nacional Dentro do Estado.", + ) + self.assertEqual( + line.icms_cst_id.code, + "101", + "Erro ao mapear a CST 101 do ICMS SN Com Permissão de Crédito" + " para Venda de Simples Nacional Dentro do Estado.", + ) + + # ICMS FCP + # self.assertEqual( + # line.icmsfcp_tax_id.name, 'FCP 2%', + # "Erro ao mapear ICMS FCP 2%" + # " para Venda de Contribuinte p/ o Exterior.") + + # PIS + self.assertEqual( + line.pis_tax_id.name, + "PIS Outros", + "Erro ao mapear PIS Simples Nacional" + " para Venda de Simples Nacional Fora do Estado.", + ) + self.assertEqual( + line.pis_cst_id.code, + "49", + "Erro ao mapear a CST 49 Outras Operações de Saída" + " com Alíquota Básica do PIS Simples Nacional de Venda de" + " Simples Nacional Dentro do Estado.", + ) + + # COFINS + self.assertEqual( + line.cofins_tax_id.name, + "COFINS Outros", + "Error to mapping COFINS Simples Nacional" + " for Venda de Simples Nacional Dentro do Estado.", + ) + self.assertEqual( + line.cofins_cst_id.code, + "49", + "Error to mapping CST 49 Outras Operações de Saída" + " from COFINS Simples Nacional for Venda de" + " Simples Nacional Fora do Estado.", + ) + + def test_nfe_return(self): + """Test Fiscal Document Return""" + action = self.nfe_same_state.action_create_return() + return_id = self.nfe_same_state.browse( + [i[2][0] for i in action["domain"] if i[0] == "id"] + ) + + self.assertEqual( + return_id.fiscal_operation_id.id, + self.nfe_same_state.fiscal_operation_id.return_fiscal_operation_id.id, + "Error on creation return", + ) + + def test_nfe_comments(self): + self.nfe_not_taxpayer._document_comment() + additional_data = self.nfe_not_taxpayer.fiscal_line_ids[0].additional_data + self.assertEqual( + additional_data, + "manual comment test - Valor Aprox. dos Tributos: R$ 0,00", + # TODO FIXME changed 0.00 to 0,00 to get tests pass on v13, but not + # correct + ) + + def test_fields_freight_insurance_other_costs(self): + """Test fields Freight, Insurance and Other Costs when + defined or By Line or By Total. + """ + + # Teste definindo os valores Por Linha + for line in self.nfe_same_state.fiscal_line_ids: + line.freight_value = 10.0 + line.insurance_value = 10.0 + line.other_value = 10.0 + + self.assertEqual( + self.nfe_same_state.amount_freight_value, + 20.0, + "Unexpected value for the field" " Amount Freight in Fiscal Document line", + ) + self.assertEqual( + self.nfe_same_state.amount_insurance_value, + 20.0, + "Unexpected value for the field" + " Amount Insurance in Fiscal Document line", + ) + self.assertEqual( + self.nfe_same_state.amount_other_value, + 20.0, + "Unexpected value for the field" + " Amount Other Value in Fiscal Document line", + ) + + # Teste definindo os valores Por Total + # Por padrão a definição dos campos está por Linha + self.nfe_same_state.company_id.delivery_costs = "total" + + # Caso que os Campos na Linha tem valor + self.nfe_same_state.amount_freight_value = 10.0 + self.nfe_same_state.amount_insurance_value = 10.0 + self.nfe_same_state.amount_other_value = 10.0 + + for line in self.nfe_same_state.fiscal_line_ids: + self.assertEqual( + line.freight_value, + 5.0, + "Unexpected value for the field" " Freight in Fiscal Document line", + ) + self.assertEqual( + line.insurance_value, + 5.0, + "Unexpected value for the field" " Insurance in Fiscal Document line", + ) + self.assertEqual( + line.other_value, + 5.0, + "Unexpected value for the field" + " Other Values in Fiscal Document line", + ) + + # Caso que os Campos na Linha não tem valor + for line in self.nfe_same_state.fiscal_line_ids: + line.freight_value = 0.0 + line.insurance_value = 0.0 + line.other_value = 0.0 + + self.nfe_same_state.amount_freight_value = 20.0 + self.nfe_same_state.amount_insurance_value = 20.0 + self.nfe_same_state.amount_other_value = 20.0 + + for line in self.nfe_same_state.fiscal_line_ids: + self.assertEqual( + line.freight_value, + 10.0, + "Unexpected value for the field" " Freight in Fiscal Document line", + ) + self.assertEqual( + line.insurance_value, + 10.0, + "Unexpected value for the field" " Insurance in Fiscal Document line", + ) + self.assertEqual( + line.other_value, + 10.0, + "Unexpected value for the field" + " Other Values in Fiscal Document line", + ) + + def test_nfe_purchase_same_state(self): + # self.nfe_purchase_same_state.action_document_confirm() + + self.assertEqual( + self.nfe_purchase_same_state.state_edoc, + SITUACAO_EDOC_AUTORIZADA, + "Document is not in Authorized state", + ) + + self.nfe_purchase_same_state.action_document_back2draft() + + self.assertEqual( + self.nfe_purchase_same_state.state_edoc, + SITUACAO_EDOC_EM_DIGITACAO, + "Document is not in Draft state", + ) + + self.nfe_purchase_same_state.action_document_cancel() + + self.assertEqual( + self.nfe_purchase_same_state.state_edoc, + SITUACAO_EDOC_CANCELADA, + "Document is not in Canceled state", + ) diff --git a/l10n_br_fiscal_edi/tests/test_tax_benefit.py b/l10n_br_fiscal_edi/tests/test_tax_benefit.py new file mode 100644 index 000000000000..2fa4f36ee585 --- /dev/null +++ b/l10n_br_fiscal_edi/tests/test_tax_benefit.py @@ -0,0 +1,74 @@ +# Copyright 2023 Akretion - Renato Lima +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import SavepointCase + +from ..constants.fiscal import SITUACAO_EDOC_A_ENVIAR + + +class TestTaxBenefit(SavepointCase): + def setUp(self): + super().setUp() + self.nfe_tax_benefit = self.env.ref("l10n_br_fiscal.demo_nfe_tax_benefit") + self.tax_benefit = self.env["l10n_br_fiscal.tax.definition"].create( + { + "icms_regulation_id": self.env.ref( + "l10n_br_fiscal.tax_icms_regulation" + ).id, + "tax_group_id": self.env.ref("l10n_br_fiscal.tax_group_icms").id, + "code": "SP810001", + "name": "TAX BENEFIT DEMO", + "description": "TAX BENEFIT DEMO", + "benefit_type": "1", + "is_benefit": True, + "is_taxed": True, + "is_debit_credit": True, + "custom_tax": True, + "tax_id": self.env.ref("l10n_br_fiscal.tax_icms_12_red_26_57").id, + "cst_id": self.env.ref("l10n_br_fiscal.cst_icms_20").id, + "state_from_id": self.env.ref("base.state_br_sp").id, + "state_to_ids": [(6, 0, self.env.ref("base.state_br_mg").ids)], + "ncms": "73269090", + "ncm_ids": [(6, 0, self.env.ref("l10n_br_fiscal.ncm_73269090").ids)], + "state": "approved", + } + ) + + def test_nfe_tax_benefit(self): + """Test NFe with tax benefit.""" + + self.nfe_tax_benefit._onchange_document_serie_id() + self.nfe_tax_benefit._onchange_fiscal_operation_id() + + for line in self.nfe_tax_benefit.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + self.assertEqual( + line.icms_tax_benefit_id, + self.tax_benefit, + "Document line must have tax benefit", + ) + + # self.nfe_tax_benefit.action_document_confirm() + + self.assertEqual( + self.nfe_tax_benefit.state_edoc, + SITUACAO_EDOC_A_ENVIAR, + "Document is not in To Send state", + ) + + self.nfe_tax_benefit.action_document_send() + + # self.assertEqual( + # self.nfe_tax_benefit.state_edoc, + # SITUACAO_EDOC_AUTORIZADA, + # "Document is not in Authorized state", + # ) + + # result = self.nfe_tax_benefit.action_document_cancel() + # self.assertTrue(result) diff --git a/l10n_br_fiscal/tests/test_workflow.py b/l10n_br_fiscal_edi/tests/test_workflow.py similarity index 98% rename from l10n_br_fiscal/tests/test_workflow.py rename to l10n_br_fiscal_edi/tests/test_workflow.py index 5a54a0464583..bd408c786968 100644 --- a/l10n_br_fiscal/tests/test_workflow.py +++ b/l10n_br_fiscal_edi/tests/test_workflow.py @@ -3,7 +3,7 @@ from odoo.tests import TransactionCase -from ..constants.fiscal import ( +from odoo.addons.l10n_br_fiscal.constants.fiscal import ( SITUACAO_EDOC_A_ENVIAR, SITUACAO_EDOC_AUTORIZADA, SITUACAO_EDOC_CANCELADA, diff --git a/l10n_br_fiscal/views/document_event_report.xml b/l10n_br_fiscal_edi/views/document_event_report.xml similarity index 100% rename from l10n_br_fiscal/views/document_event_report.xml rename to l10n_br_fiscal_edi/views/document_event_report.xml diff --git a/l10n_br_fiscal/views/document_event_template.xml b/l10n_br_fiscal_edi/views/document_event_template.xml similarity index 100% rename from l10n_br_fiscal/views/document_event_template.xml rename to l10n_br_fiscal_edi/views/document_event_template.xml diff --git a/l10n_br_fiscal/views/document_event_view.xml b/l10n_br_fiscal_edi/views/document_event_view.xml similarity index 100% rename from l10n_br_fiscal/views/document_event_view.xml rename to l10n_br_fiscal_edi/views/document_event_view.xml diff --git a/l10n_br_fiscal_edi/views/document_view.xml b/l10n_br_fiscal_edi/views/document_view.xml new file mode 100644 index 000000000000..671bf10a9a58 --- /dev/null +++ b/l10n_br_fiscal_edi/views/document_view.xml @@ -0,0 +1,77 @@ + + + + + l10n_br_fiscal_edi.document.form + l10n_br_fiscal.document + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +