diff --git a/l10n_br_cte/README.rst b/l10n_br_cte/README.rst new file mode 100644 index 000000000000..c7c3458a6ea3 --- /dev/null +++ b/l10n_br_cte/README.rst @@ -0,0 +1,139 @@ +==== +CT-e +==== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c91616235e33e68d0115aa3807f25142a45f5013a23492f240cf507a41d41340 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--brazil-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-brazil/tree/14.0/l10n_br_cte + :alt: OCA/l10n-brazil +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-brazil-14-0/l10n-brazil-14-0-l10n_br_cte + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-brazil&target_branch=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Este módulo permite a emissão de CT-e (Conhecimento de Transporte). + +Mais especificamente ele: + * mapea os campos de CT-e do módulo ``l10n_br_cte_spec`` com os campos Odoo. + * usa a logica do módulo ``spec_driven_model`` para realizar esse mapeamento de forma dinâmica, em especial ele usa o sistema de modelos com várias camadas, ou ``StackedModel``, com os modelos ``l10n_br_fiscal.document`` e ``l10n_br_fiscal.document.related`` que tem varios niveis hierarquicos de elementos XML que estão sendo denormalizados dentro desses modelos Odoo  + * tem wizards para implementar a comunicação SOAP de CT-e com a SEFAZ (Autorização, Cancelamento, Encerramento...) + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Para configurar este módulo, você precisa definir um certificado digital na empresa e também definir o processador edoc da empresa. + +Usage +===== + +**Passo a Passo:** + +1. **Criar uma Fatura:** + - Defina o tipo de documento como **57 (CTe - Conhecimento de Transporte)**. + +2. **Configurar o Parceiro da Fatura:** + - Configure o parceiro responsável pelo pagamento do CTe e os parceiros como Rementente, Expedidor, Destinatário e Recebedor. + +3. **Adicionar uma Linha na Aba Produtos:** + - Adicione uma linha de fatura e selecione o produto Frete ou outro que esteja previamente configurado. + +4. **Acesse os detalhes fiscais da fatura e informe os demais dados necessário para emissão do CT-e:** + - Preencha os campos obrigatórios para emissão do CT-e. + +5. **Valide o CT-e, verifique os dados do XML e envie para a SEFAZ:** + - Após preencher todos os dados necessários, valide o CT-e e envie para a SEFAZ. + +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 +~~~~~~~ + +* KMEE +* Escodoo + +Contributors +~~~~~~~~~~~~ + + +* `KMEE `_: + + * Luis Felipe Mileo + * Ygor Carvalho + +* `ESCODOO `_: + + * Marcel Savegnago + +* `AKRETION `_: + + * Raphaël Valyi + +* `Engenere `_: + + * Antônio S. Pereira Neto + +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-mileo| image:: https://github.com/mileo.png?size=40px + :target: https://github.com/mileo + :alt: mileo +.. |maintainer-marcelsavegnago| image:: https://github.com/marcelsavegnago.png?size=40px + :target: https://github.com/marcelsavegnago + :alt: marcelsavegnago + +Current `maintainers `__: + +|maintainer-mileo| |maintainer-marcelsavegnago| + +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_cte/__init__.py b/l10n_br_cte/__init__.py new file mode 100644 index 000000000000..ee3b8ee6dfd6 --- /dev/null +++ b/l10n_br_cte/__init__.py @@ -0,0 +1,3 @@ +from .hooks import post_init_hook +from . import models +from . import report diff --git a/l10n_br_cte/__manifest__.py b/l10n_br_cte/__manifest__.py new file mode 100644 index 000000000000..6372e3e8dd6a --- /dev/null +++ b/l10n_br_cte/__manifest__.py @@ -0,0 +1,56 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "CT-e", + "summary": """Brazilian Electronic Invoice CT-e""", + "version": "14.0.1.0.0", + "category": "Localisation", + "license": "AGPL-3", + "author": "KMEE,Escodoo,Odoo Community Association (OCA)", + "maintainers": ["mileo", "marcelsavegnago"], + "website": "https://github.com/OCA/l10n-brazil", + "development_status": "Alpha", + "depends": [ + "l10n_br_fiscal_edi", + "l10n_br_cte_spec", + "l10n_br_fiscal_certificate", + "spec_driven_model", + ], + "data": [ + # Data + "data/ir_config_parameter.xml", + # Security + "security/ir.model.access.csv", + # Views + "views/document.xml", + "views/res_company.xml", + "views/modal/modal_rodoviario.xml", + "views/modal/modal_aquaviario.xml", + "views/modal/modal_ferroviario.xml", + "views/modal/modal_aereo.xml", + # Report + "report/dacte_report.xml", + # Wizards + "wizards/document_correction_wizard.xml", + # Actions + "views/cte_action.xml", + # Menus + "views/cte_menu.xml", + ], + "demo": [ + "demo/company_demo.xml", + "demo/fiscal_document_demo.xml", + ], + "post_init_hook": "post_init_hook", + "installable": True, + "auto_install": False, + "external_dependencies": { + "python": [ + "nfelib<=2.0.7", + "erpbrasil.assinatura>=1.7.0", + "erpbrasil.transmissao>=1.1.0", + "erpbrasil.edoc>=2.5.2", + ], + }, +} diff --git a/l10n_br_cte/constants/cte.py b/l10n_br_cte/constants/cte.py new file mode 100644 index 000000000000..bb8a4ed5cb87 --- /dev/null +++ b/l10n_br_cte/constants/cte.py @@ -0,0 +1,105 @@ +# Copyright (C) 2024 - TODAY, Marcel Savegnago +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +CTE_VERSIONS = [("4.00", "4.00")] + +CTE_VERSION_DEFAULT = "4.00" + +CTE_ENVIRONMENTS = [("1", "Produção"), ("2", "Homologação")] + +CTE_ENVIRONMENT_DEFAULT = "2" + +CTE_EMIT_TYPES = [ + ("1", "1 - Prestador de serviço de transporte"), + ("2", "2 - Transportador de Carga Própria"), + ("3", "3 - Prestador de serviço de transporte que emitirá CT-e Globalizado"), +] + +CTE_EMIT_TYPE_DEFAULT = "2" + +CTE_TRANSP_TYPE = [ + ("1", "Empresa de Transporte de Cargas – ETC"), + ("2", "Transportador Autônomo de Cargas – TAC"), + ("3", "Cooperativa de Transporte de Cargas – CTC"), +] + +CTE_TRANSP_TYPE_DEFAULT = "1" + +CTE_TRANSMISSIONS = [ + ("1", "Emissão Normal"), + ("2", "Contingência Off-Line"), + ("3", "Regime Especial NFF"), +] + +CTE_TRANSMISSION_DEFAULT = "1" + +CTE_EMISSION_PROCESSES = [("0", "Emissão de CTe com aplicativo do contribuinte")] + +CTE_EMISSION_PROCESS_DEFAULT = "0" + +CTE_TYPE = [ + ("0", "CT-e Normal"), + ("1", "CT-e de Complemento de Valores"), + ("3", "CT-e de Substituição"), +] + +CTE_TYPE_DEFAULT = "0" + +CTE_INDIETOMA = [ + ("1", "Contribuinte ICMS"), + ("2", "Contribuinte isento de inscrição"), + ("9", "Não Contribuinte"), +] + +CTE_INDIETOMA_DEFAULT = "1" + +CTE_TPSERV = [ + ("0", "Normal"), + ("1", "Subcontratação"), + ("2", "Redespacho"), + ("3", "Redespacho Intermediário"), + ("4", "Serviço Vinculado a Multimodal"), +] + +CTE_TPSERV_DEFAULT = "0" + +CTE_TPEMIS = [ + ("1", "Normal"), + ("3", "Regime Especial NFF"), + ("4", "EPEC pela SVC"), + ("5", "Contingência FSDA"), + ("7", "Autorização pela SVC-RS"), + ("8", "Autorização pela SVC-SP"), +] + +CTE_TPEMIS_DEFAULT = "1" + +CTE_TPIMP = [ + ("1", "Retrato"), + ("2", "Paisagem."), +] + +CTE_TPIMP_DEFAULT = "1" + + +CTE_ICMS_SUB_TAGS = [ + "ICMS00", + "ICMS20", + "ICMS45", + "ICMS60", + "ICMS90", + "ICMSOutraUF", + "ICMSSN", +] + +CTE_ICMS_SELECTION = list(map(lambda tag: (f"cte40_{tag}", tag), CTE_ICMS_SUB_TAGS)) + +CTE_CST = [ + ("00", "00 - Tributação normal ICMS"), + ("20", "20 - Tributação com BC reduzida do ICMS"), + ("45", "45 - ICMS Isento, não Tributado ou diferido"), + ("60", "60 - ICMS cobrado por substituição tributária"), + ("90", "90 - ICMS outros"), + ("90", "90 - ICMS Outra UF"), + ("01", "01 - Simples Nacional"), +] diff --git a/l10n_br_cte/constants/modal.py b/l10n_br_cte/constants/modal.py new file mode 100644 index 000000000000..6b1c1625ac51 --- /dev/null +++ b/l10n_br_cte/constants/modal.py @@ -0,0 +1,43 @@ +CTE_MODALS = [ + ("01", "Rodoviário"), + ("02", "Aéreo"), + ("03", "Aquaviário"), + ("04", "Ferroviário"), + ("05", "Dutoviário"), + ("06", "Multimodal"), +] + +CTE_MODAL_DEFAULT = "01" + +CTE_MODAL_VERSION_DEFAULT = "4.00" + +TUF = [ + ("AC", "AC"), + ("AL", "AL"), + ("AM", "AM"), + ("AP", "AP"), + ("BA", "BA"), + ("CE", "CE"), + ("DF", "DF"), + ("ES", "ES"), + ("GO", "GO"), + ("MA", "MA"), + ("MG", "MG"), + ("MS", "MS"), + ("MT", "MT"), + ("PA", "PA"), + ("PB", "PB"), + ("PE", "PE"), + ("PI", "PI"), + ("PR", "PR"), + ("RJ", "RJ"), + ("RN", "RN"), + ("RO", "RO"), + ("RR", "RR"), + ("RS", "RS"), + ("SC", "SC"), + ("SE", "SE"), + ("SP", "SP"), + ("TO", "TO"), + ("EX", "EX"), +] diff --git a/l10n_br_cte/data/ir_config_parameter.xml b/l10n_br_cte/data/ir_config_parameter.xml new file mode 100644 index 000000000000..a0a0745d0918 --- /dev/null +++ b/l10n_br_cte/data/ir_config_parameter.xml @@ -0,0 +1,9 @@ + + + + + l10n_br_cte.version.name + Odoo Brasil OCA v14 + + + diff --git a/l10n_br_cte/demo/company_demo.xml b/l10n_br_cte/demo/company_demo.xml new file mode 100644 index 000000000000..ea8dc04edd59 --- /dev/null +++ b/l10n_br_cte/demo/company_demo.xml @@ -0,0 +1,24 @@ + + + + + + oca + + + + 07946021 + + + + + + oca + + + + 07946021 + + + + diff --git a/l10n_br_cte/demo/fiscal_document_demo.xml b/l10n_br_cte/demo/fiscal_document_demo.xml new file mode 100644 index 000000000000..6cc9849c0050 --- /dev/null +++ b/l10n_br_cte/demo/fiscal_document_demo.xml @@ -0,0 +1,693 @@ + + + + + + + + + + + + 573 + 1 + 35240781583054000129570010000057311040645894 + 2 + oca + + 3 + 01 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + + + + + + 574 + 1 + 35240781583054000129570010000057411040645890 + 2 + oca + + 3 + 02 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + + + + + + 575 + 1 + 35240781583054000129570010000057511040645897 + 2 + oca + + 3 + 03 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + + + + + + 576 + 1 + 35240781583054000129570010000057611040645893 + 2 + oca + + 3 + 04 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + + + + + + 577 + 1 + 35240781583054000129570010000057711040645890 + 2 + oca + + 3 + 05 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + + + + + + + + 573 + 1 + 35240759594315000157570010000057311040445890 + 2 + oca + + 3 + 01 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + diff --git a/l10n_br_cte/hooks.py b/l10n_br_cte/hooks.py new file mode 100644 index 000000000000..972c6786362f --- /dev/null +++ b/l10n_br_cte/hooks.py @@ -0,0 +1,48 @@ +# Copyright (C) 2019-2020 - Raphael Valyi Akretion +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +import logging + +import pkg_resources + +# from nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00 import Tcte +from nfelib.cte.bindings.v4_0.cte_v4_00 import Tcte + +from odoo import SUPERUSER_ID, api +from odoo.exceptions import ValidationError + +from odoo.addons import l10n_br_cte + +_logger = logging.getLogger(__name__) + + +def post_init_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + cr.execute("select demo from ir_module_module where name='l10n_br_cte';") + is_demo = cr.fetchone()[0] + if is_demo: + res_items = ( + "tests", + "cte", + "v4_00", + "leiauteCTe", + "CTe51160724686092000173570010000000031000000024.xml", + ) + + resource_path = "/".join(res_items) + doc_stream = pkg_resources.resource_stream(l10n_br_cte.__name__, resource_path) + binding = Tcte.from_xml(doc_stream.read().decode()) + document_number = binding.infCte.ide.nCT + existing_docs = env["l10n_br_fiscal.document"].search( + [("document_number", "=", document_number)] + ) + try: + existing_docs.unlink() + cte = ( + env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type="in") + .build_from_binding("cte", "40", binding.infCte) + ) + _logger.info(cte.cte40_emit.cte40_CNPJ) + except ValidationError: + _logger.info(f"CTE-e already {document_number} imported by hooks") diff --git a/l10n_br_cte/models/__init__.py b/l10n_br_cte/models/__init__.py new file mode 100644 index 000000000000..0d674aa9eed1 --- /dev/null +++ b/l10n_br_cte/models/__init__.py @@ -0,0 +1,22 @@ +from . import res_company +from . import res_partner +from . import document +from . import document_related +from . import document_line +from . import res_config_settings +from . import ferroviario +from . import rodoviario +from . import aereo +from . import dutoviario +from . import aquaviario +from . import document_cargo_quantity_infos +from . import document_supplement +from . import document_transported_vehicles +from . import document_comment +from . import res_country +from . import document_type +from . import res_country_state +from . import res_city + +spec_schema = "cte" +spec_version = "40" diff --git a/l10n_br_cte/models/aereo.py b/l10n_br_cte/models/aereo.py new file mode 100644 index 000000000000..e6c6e17cfd67 --- /dev/null +++ b/l10n_br_cte/models/aereo.py @@ -0,0 +1,65 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class Aereo(spec_models.StackedModel): + _name = "l10n_br_cte.modal.aereo" + _inherit = "cte.40.aereo" + _description = "Modal Aereo CTe" + + _cte40_stacking_mixin = "cte.40.aereo" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aereo_v4_00" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_nMinu = fields.Char(related="document_id.cte40_nMinu") + + cte40_nOCA = fields.Char(related="document_id.cte40_nOCA") + + cte40_dPrevAereo = fields.Date(related="document_id.cte40_dPrevAereo") + + cte40_CL = fields.Char(related="document_id.cte40_CL") + + cte40_cTar = fields.Char(related="document_id.cte40_cTar") + + cte40_vTar = fields.Monetary(related="document_id.cte40_aereo_vTar") + + cte40_xDime = fields.Char(related="document_id.cte40_xDime") + + cte40_peri = fields.One2many(related="document_id.cte40_peri") + + def _prepare_dacte_values(self): + if not self: + return {} + + +class Peri(spec_models.StackedModel): + _name = "l10n_br_cte.modal.aereo.peri" + _inherit = "cte.40.peri" + _description = """Preenchido quando for transporte de produtos classificados pela + ONU como perigosos. O preenchimento desses campos não desobriga a empresa aérea de + emitir os demais documentos que constam na legislação vigente.""" + + _cte40_stacking_mixin = "cte.40.peri" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aereo_v4_00" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_nONU = fields.Char(required=True) + + cte40_qTotEmb = fields.Char(required=True) + + cte40_qTotProd = fields.Float(required=True) + + cte40_uniAP = fields.Selection(required=True) diff --git a/l10n_br_cte/models/aquaviario.py b/l10n_br_cte/models/aquaviario.py new file mode 100644 index 000000000000..8b30ce02cd2e --- /dev/null +++ b/l10n_br_cte/models/aquaviario.py @@ -0,0 +1,58 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class Aquav(spec_models.StackedModel): + _name = "l10n_br_cte.modal.aquav" + _inherit = "cte.40.aquav" + _description = "Modal Aquaviário CTe" + + _cte40_stacking_mixin = "cte.40.aquav" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aquaviario_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aquaviario_v4_00" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_vAFRMM = fields.Monetary(related="document_id.cte40_vAFRMM") + + cte40_vPrest = fields.Monetary( + related="document_id.cte40_vTPrest" + ) # TODO: avaliar melhor + + cte40_xNavio = fields.Char(related="document_id.cte40_xNavio") + + cte40_nViag = fields.Char(related="document_id.cte40_nViag") + + cte40_direc = fields.Selection(related="document_id.cte40_direc") + + cte40_irin = fields.Char(related="document_id.cte40_irin") + + cte40_tpNav = fields.Selection(related="document_id.cte40_tpNav") + + cte40_balsa = fields.One2many(related="document_id.cte40_balsa") + + def _prepare_dacte_values(self): + if not self: + return {} + + +class Balsa(spec_models.SpecModel): + _name = "l10n_br_cte.modal.aquav.balsa" + _inherit = "cte.40.balsa" + _description = "Grupo de informações das balsas" + + _cte40_stacking_mixin = "cte.40.balsa" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aquaviario_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aquaviario_v4_00" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_xBalsa = fields.Char(string="Identificador da Balsa") diff --git a/l10n_br_cte/models/document.py b/l10n_br_cte/models/document.py new file mode 100644 index 000000000000..7e42b291cb1f --- /dev/null +++ b/l10n_br_cte/models/document.py @@ -0,0 +1,1769 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import base64 +import logging +import re +import string +import sys +from datetime import datetime +from enum import Enum + +from erpbrasil.base.fiscal import cnpj_cpf + +# TODO: precisa tratar +# from erpbrasil.edoc.cte import TransmissaoCTE +from erpbrasil.base.fiscal.edoc import ChaveEdoc +from lxml import etree +from nfelib.cte.bindings.v4_0.cte_v4_00 import Cte +from nfelib.cte.bindings.v4_0.proc_cte_v4_00 import CteProc + +# TODO: precisa tratar nfelib +# from nfelib.nfe.ws.edoc_legacy import CTeAdapter as edoc_cte +from xsdata.formats.dataclass.parsers import XmlParser + +from odoo import _, api, fields +from odoo.exceptions import UserError, ValidationError + +from odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_ferroviario_v4_00 import ( + FERROV_TPTRAF, + TRAFMUT_FERREMI, + TRAFMUT_RESPFAT, +) +from odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00 import ( + COMDATA_TPPER, + SEMHORA_TPHOR, +) +from odoo.addons.l10n_br_fiscal.constants.fiscal import ( + AUTORIZADO, + CANCELADO, + CANCELADO_DENTRO_PRAZO, + CANCELADO_FORA_PRAZO, + DENEGADO, + DOCUMENT_ISSUER_COMPANY, + EVENT_ENV_HML, + EVENT_ENV_PROD, + EVENTO_RECEBIDO, + LOTE_PROCESSADO, + PROCESSADOR_OCA, + SITUACAO_EDOC_A_ENVIAR, + SITUACAO_EDOC_AUTORIZADA, + SITUACAO_EDOC_CANCELADA, + SITUACAO_EDOC_DENEGADA, + SITUACAO_EDOC_EM_DIGITACAO, + SITUACAO_EDOC_REJEITADA, + SITUACAO_FISCAL_CANCELADO, + SITUACAO_FISCAL_CANCELADO_EXTEMPORANEO, +) +from odoo.addons.l10n_br_fiscal.constants.icms import ICMS_CST, ICMS_SN_CST +from odoo.addons.spec_driven_model.models import spec_models + +from ..constants.cte import ( + CTE_CST, + CTE_ENVIRONMENTS, + CTE_ICMS_SELECTION, + CTE_INDIETOMA, + CTE_INDIETOMA_DEFAULT, + CTE_TPEMIS, + CTE_TPEMIS_DEFAULT, + CTE_TPIMP, + CTE_TPIMP_DEFAULT, + CTE_TPSERV, + CTE_TPSERV_DEFAULT, + CTE_TRANSMISSIONS, + CTE_TYPE, + CTE_TYPE_DEFAULT, +) +from ..constants.modal import ( + CTE_MODAL_VERSION_DEFAULT, +) + +CTE_XML_NAMESPACE = {"cte": "http://www.portalfiscal.inf.br/cte"} + + +_logger = logging.getLogger(__name__) + + +def filter_processador_edoc_cte(record): + if record.processador_edoc == "oca" and record.document_type_id.code in [ + "57", + "67", + ]: + return True + return False + + +class CTe(spec_models.StackedModel): + _name = "l10n_br_fiscal.document" + _inherit = [ + "l10n_br_fiscal.document", + "cte.40.tcte_infcte", + ] + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_infcte" + _cte40_stacking_skip_paths = ( + "cte40_semData", + "cte40_noPeriodo", + "cte40_comHora", + "cte40_noInter", + # "cte40_NFref_ide_id", + ) + + # all m2o at this level will be stacked even if not required: + _cte40_stacking_force_paths = ( + "infcte.compl", + # "infcte.compl.entrega", + ) + _cte_search_keys = ["cte40_Id"] + + INFCTE_TREE = """ + > infCte + > + > res.partner + > + > + - + ≡ + ≡ + + > res.company + > res.company + > res.partner + > res.partner + > + ≡ + > + > + > + > + > + ≡ + > + ≡ + ≡ + ≡ + - > + > + > + > """ + + cte40_versao = fields.Char(related="document_version") + + cte_version = fields.Selection( + string="CT-e Version", + related="company_id.cte_version", + readonly=False, + ) + + cte_environment = fields.Selection( + selection=CTE_ENVIRONMENTS, + string="CTe Environment", + copy=False, + default=lambda self: self.env.company.cte_environment, + ) + + cte_transmission = fields.Selection( + selection=CTE_TRANSMISSIONS, + string="CTE Transmission", + copy=False, + default=lambda self: self.env.company.cte_transmission, + ) + + ########################## + # CT-e spec related fields + ########################## + + ########################## + # CT-e tag: infCte + ########################## + + cte40_Id = fields.Char( + compute="_compute_cte40_id", + inverse="_inverse_cte40_id", + ) + + ########################## + # CT-e tag: Id + # Compute Methods + ########################## + + @api.depends("document_type_id", "document_key") + def _compute_cte40_id(self): + """Set schema data which are not just related fields""" + + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_Id = False + + if ( + record.document_type_id + and record.document_type_id.prefix + and record.document_key + ): + record.cte40_Id = "{}{}".format( + record.document_type_id.prefix, record.document_key + ) + + ########################## + # CT-e tag: id + # Inverse Methods + ########################## + + def _inverse_cte40_id(self): + for record in self: + if record.cte40_Id: + record.document_key = re.findall(r"\d+", str(record.cte40_Id))[0] + + ########################## + # CT-e tag: ide + ########################## + + cte40_cUF = fields.Char( + related="company_id.partner_id.state_id.ibge_code", + string="cte40_cUF", + ) + + cte40_cCT = fields.Char(compute="_compute_cte40_cct") + + cte40_CFOP = fields.Char(compute="_compute_cte40_CFOP", store=True) + + cte40_natOp = fields.Char(related="operation_name") + + cte40_mod = fields.Char(related="document_type_id.code", string="cte40_mod") + + cte40_serie = fields.Char(related="document_serie") + + cte40_nCT = fields.Char(related="document_number") + + cte40_dhEmi = fields.Datetime(related="document_date") + + # TODO: Tratar/Avaliar + # cte40_cDV = fields.Char(compute="_compute_cte40_cDV", store=True) + # cte40_cDV = fields.Char(related="key_check_digit") + + cte40_procEmi = fields.Selection(default="0") + + cte40_verProc = fields.Char( + copy=False, + default=lambda s: s.env["ir.config_parameter"] + .sudo() + .get_param("l10n_br_cte.version.name", default="Odoo Brasil OCA v14"), + ) + + cte40_cMunEnv = fields.Char( + compute="_compute_cte40_data", + store=True, + compute_sudo=True, + ) + + cte40_xMunEnv = fields.Char( + compute="_compute_cte40_data", + store=True, + compute_sudo=True, + ) + + cte40_UFEnv = fields.Char( + compute="_compute_cte40_data", + store=True, + compute_sudo=True, + ) + + cte40_indIEToma = fields.Selection( + selection=CTE_INDIETOMA, + default=CTE_INDIETOMA_DEFAULT, + ) + + cte40_cMunIni = fields.Char( + compute="_compute_cte40_data", + compute_sudo=True, + ) + + cte40_xMunIni = fields.Char( + compute="_compute_cte40_data", + compute_sudo=True, + ) + + cte40_UFIni = fields.Char() + + cte40_cMunFim = fields.Char( + compute="_compute_cte40_data", + compute_sudo=True, + ) + + cte40_xMunFim = fields.Char( + compute="_compute_cte40_data", + compute_sudo=True, + ) + + cte40_UFFim = fields.Char() + + cte40_retira = fields.Selection(selection=[("0", "Sim"), ("1", "Não")], default="1") + + cte40_tpServ = fields.Selection( + selection=CTE_TPSERV, + default=CTE_TPSERV_DEFAULT, + ) + + cte40_tpCTe = fields.Selection( + selection=CTE_TYPE, + default=CTE_TYPE_DEFAULT, + ) + + cte40_tpAmb = fields.Selection(related="cte_environment") + + cte40_tpEmis = fields.Selection( + selection=CTE_TPEMIS, + default=CTE_TPEMIS_DEFAULT, + ) + + cte40_tpImp = fields.Selection(selection=CTE_TPIMP, default=CTE_TPIMP_DEFAULT) + + # toma + cte40_toma = fields.Selection(related="service_provider") + + cte40_choice_toma = fields.Selection( + selection=[ + ("cte40_toma3", "toma3"), + ("cte40_toma4", "toma4"), + ], + compute="_compute_cte40_choice_toma", + ) + + cte40_enderToma = fields.Many2one(comodel_name="res.partner", related="partner_id") + + cte40_enderReme = fields.Many2one(comodel_name="res.partner") + + ########################## + # CT-e tag: ide + # Compute Methods + ########################## + + @api.depends("company_id", "partner_id", "service_provider") + def _compute_cte40_choice_toma(self): + for rec in self.filtered(filter_processador_edoc_cte): + if rec.service_provider in ["0", "1", "2", "3"]: + rec.cte40_choice_toma = "cte40_toma3" + else: + rec.cte40_choice_toma = "cte40_toma4" + + def _export_fields_cte_40_toma3(self, xsd_fields, class_obj, export_dict): + if self.cte40_choice_toma == "cte40_toma4": + xsd_fields.remove("cte40_toma") + + def _export_fields_cte_40_tcte_toma4(self, xsd_fields, class_obj, export_dict): + if self.cte40_choice_toma == "cte40_toma3": + xsd_fields.remove("cte40_toma") + xsd_fields.remove("cte40_CNPJ") + xsd_fields.remove("cte40_CPF") + xsd_fields.remove("cte40_IE") + xsd_fields.remove("cte40_xNome") + xsd_fields.remove("cte40_xFant") + xsd_fields.remove("cte40_enderToma") + + @api.depends("fiscal_line_ids", "fiscal_line_ids.cfop_id") + def _compute_cte40_CFOP(self): + for rec in self.filtered(filter_processador_edoc_cte): + if rec.fiscal_line_ids: + rec.cte40_CFOP = rec.fiscal_line_ids[0].cfop_id.code + + # TODO: Tratar + # @api.depends("document_key") + # def _compute_cte40_cDV(self): + # for rec in self.filtered(filter_processador_edoc_cte): + # if rec.document_key: + # rec.cte40_cDV = rec.document_key[-1] + + def _compute_cte40_cct(self): + for rec in self.filtered(filter_processador_edoc_cte): + if rec.document_key: + rec.cte40_cCT = rec.document_key[35:43] + + @api.depends( + "partner_id", + "company_id", + "partner_sendering_id", + "partner_shippering_id", + "partner_shipping_id", + "partner_receivering_id", + ) + def _compute_cte40_data(self): + for doc in self.filtered(filter_processador_edoc_cte): + if doc.company_id.partner_id.country_id == doc.partner_id.country_id: + if doc.issuer == DOCUMENT_ISSUER_COMPANY: + doc.cte40_xMunEnv = ( + doc.company_id.partner_id.city_id.name + ) # TODO: provavelmente vai depender de quem é o emissor + else: + doc.cte40_xMunEnv = ( + doc.partner_id.city_id.name + ) # TODO: provavelmente vai depender de quem é o emissor + + doc.cte40_cMunEnv = doc.company_id.partner_id.city_id.ibge_code + doc.cte40_UFEnv = doc.company_id.partner_id.state_id.code + doc.cte40_xMunIni = ( + doc.cte40_exped.city_id.name or doc.cte40_rem.city_id.name + ) + doc.cte40_cMunIni = ( + doc.cte40_exped.city_id.ibge_code or doc.cte40_rem.city_id.ibge_code + ) + doc.cte40_UFIni = ( + doc.cte40_exped.state_id.code or doc.cte40_rem.state_id.code + ) + doc.cte40_xMunFim = ( + doc.cte40_receb.city_id.name or doc.cte40_dest.city_id.name + ) + doc.cte40_cMunFim = ( + doc.cte40_receb.city_id.ibge_code + or doc.cte40_dest.city_id.ibge_code + ) + doc.cte40_UFFim = ( + doc.cte40_receb.state_id.code or doc.cte40_dest.state_id.code + ) + else: + doc.cte40_UFIni = "EX" + doc.cte40_UFEnv = "EX" + doc.cte40_xMunIni = "EXTERIOR" + doc.cte40_cMunIni = "9999999" + doc.cte40_xMunEnv = ( + doc.company_id.partner_id.country_id.name + + "/" + + doc.company_id.partner_id.city_id.name + ) + doc.cte40_cMunEnv = "9999999" + doc.cte40_cMunFim = "9999999" + doc.cte40_xMunFim = "EXTERIOR" + doc.cte40_UFFim = "EX" + + # TODO: nao esta rodando direto.. corrigir + def _compute_cte40_infQ(self): + for record in self.filtered(filter_processador_edoc_cte): + cargo_info_vals = [ + {"cte40_cUnid": "01", "cte40_tpMed": "Peso Bruto", "cte40_qCarga": 0}, + { + "cte40_cUnid": "01", + "cte40_tpMed": "Peso Base Calculado", + "cte40_qCarga": 0, + }, + {"cte40_cUnid": "01", "cte40_tpMed": "Peso Aferido", "cte40_qCarga": 0}, + {"cte40_cUnid": "00", "cte40_tpMed": "Cubagem", "cte40_qCarga": 0}, + {"cte40_cUnid": "03", "cte40_tpMed": "Unidade", "cte40_qCarga": 0}, + ] + + record.cte40_infQ = self.env["l10n_br_cte.cargo.quantity.infos"].create( + cargo_info_vals + ) + + ########################## + # CT-e tag: compl + ########################## + + cte40_xObs = fields.Text(compute="_compute_cte40_compl") + cte40_obsCont = fields.One2many( + "l10n_br_fiscal.comment", compute="_compute_cte40_obsCont" + ) + + cte40_obsFisco = fields.One2many( + "l10n_br_fiscal.comment", compute="_compute_cte40_obsCont" + ) + + ########################## + # CT-e tag: compl + # Methods + ########################## + + @api.depends("comment_ids") + def _compute_cte40_obsCont(self): + for doc in self.filtered(filter_processador_edoc_cte): + doc.cte40_obsCont = doc.comment_ids.filtered( + lambda c: c.comment_type == "commercial" + ) + doc.cte40_obsFisco = doc.comment_ids.filtered( + lambda c: c.comment_type == "fiscal" + ) + + def _compute_cte40_compl(self): + for doc in self.filtered(filter_processador_edoc_cte): + fiscal_data = ( + doc.fiscal_additional_data if doc.fiscal_additional_data else "" + ) + customer_data = ( + doc.customer_additional_data if doc.customer_additional_data else "" + ) + doc.cte40_xObs = (fiscal_data + " " + customer_data)[:256].strip() + + ########################## + # CT-e tag: entrega + ########################## + + # TODO: pensar em algo genericoom base nisso decidir quais tags + # puxar (comData,semData,noPeriodo...) + cte40_tpPer = fields.Selection( + selection=COMDATA_TPPER, string="Tipo de data/período programado", default="2" + ) + cte40_dProg = fields.Date("Data Programada", default=fields.Date.today) + + cte40_tpHor = fields.Selection(SEMHORA_TPHOR, string="Tipo de hora", default="0") + + ########################## + # CT-e tag: emit + ########################## + + cte40_emit = fields.Many2one( + comodel_name="res.company", + compute="_compute_cte_emit_data", + string="Emit", + ) + + cte40_CRT = fields.Selection( + related="company_tax_framework", + string="Código de Regime Tributário (CTe)", + ) + + ########################## + # CT-e tag: emit + # Compute Methods + ########################## + + @api.depends("partner_id", "company_id") + def _compute_cte_emit_data(self): + for doc in self: # TODO if out + doc.cte40_emit = doc.company_id + + def _set_cte40_IEST(self): + self.ensure_one() + iest = "" + if self.partner_id: + dest_state_id = self.partner_id.state_id + if dest_state_id in self.company_id.state_tax_number_ids.mapped("state_id"): + stn_id = self.company_id.state_tax_number_ids.filtered( + lambda stn: stn.state_id == dest_state_id + ) + iest = stn_id.inscr_est + iest = re.sub("[^0-9]+", "", iest) + self.company_inscr_est_st = iest + + ########################## + # CT-e tag: rem + ########################## + + cte40_rem = fields.Many2one( + comodel_name="res.partner", + string="Remetente", + related="partner_sendering_id", + ) + + ########################## + # CT-e tag: exped + ########################## + + cte40_exped = fields.Many2one( + comodel_name="res.partner", + string="Expedidor", + related="partner_shippering_id", + ) + + ########################## + # CT-e tag: dest + ########################## + + cte40_dest = fields.Many2one( + comodel_name="res.partner", + string="Destinatário", + related="partner_shipping_id", + ) + + ########################## + # CT-e tag: receb + ########################## + + cte40_receb = fields.Many2one( + comodel_name="res.partner", + string="Recebedor", + related="partner_receivering_id", + ) + + ########################## + # CT-e tag: vPrest + ########################## + + cte40_vTPrest = fields.Monetary(related="amount_total") + + cte40_vRec = fields.Monetary(related="amount_price_gross") + + cte40_comp = fields.One2many( + comodel_name="l10n_br_fiscal.document.line", + inverse_name="document_id", + related="fiscal_line_ids", + ) + + ################################################## + # CT-e tag: ICMS + # Grupo N01. Grupo Tributação do ICMS= 00 + # Grupo N02. Grupo Tributação do ICMS= 20 + # Grupo N03. Grupo Tributação do ICMS= 45 (40, 41 e 51) + # Grupo N04. Grupo Tributação do ICMS= 60 + # Grupo N05. Grupo Tributação do ICMS= 90 - ICMS outros + # Grupo N06. Grupo Tributação do ICMS= 90 - ICMS Outra UF + # Grupo N06. Grupo Tributação do ICMS= 01 - ISSN + ################################################# + + cte40_vTotTrib = fields.Monetary(related="amount_estimate_tax") + + # TODO: Tratar + # cte40_infAdFisco = fields.Text(related="additional_data") + + ################################################## + # CT-e tag: ICMS + # Methods + ################################################## + + cte40_choice_icms = fields.Selection( + selection=CTE_ICMS_SELECTION, + string="Tipo de ICMS", + compute="_compute_cte40_choice_icms", + store=True, + ) + + cte40_CST = fields.Selection( + selection=CTE_CST, + string="Classificação Tributária do Serviço", + compute="_compute_cte40_choice_icms", + store=True, + ) + + # ICMSSN + cte40_indSN = fields.Float(default=1) + + # # ICMSOutraUF + # # TODO + + ########################## + # CT-e tag: ICMS + # Compute Methods + ########################## + + @api.depends("fiscal_line_ids", "fiscal_line_ids.icms_cst_id") + def _compute_cte40_choice_icms(self): + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_choice_icms = None + record.cte40_CST = None + if not record.fiscal_line_ids: + continue + if record.fiscal_line_ids[0].icms_cst_id.code in ICMS_CST: + if record.fiscal_line_ids[0].icms_cst_id.code in ["40", "41", "50"]: + record.cte40_choice_icms = "cte40_ICMS45" + record.cte40_CST = "45" + elif ( + record.fiscal_line_ids[0].icms_cst_id.code == "90" + and record.partner_id.state_id != record.company_id.state_id + ): + record.cte40_choice_icms = "cte40_ICMSOutraUF" + else: + record.cte40_choice_icms = "{}{}".format( + "cte40_ICMS", record.fiscal_line_ids[0].icms_cst_id.code + ) + record.cte40_CST = record.fiscal_line_ids[0].icms_cst_id.code + elif record.fiscal_line_ids[0].icms_cst_id.code in ICMS_SN_CST: + record.cte40_choice_icms = "cte40_ICMSSN" + record.cte40_CST = "90" + + def _export_fields_cte40_icms(self): + # Verifica se fiscal_line_ids está vazio para evitar erros + if not self.fiscal_line_ids: + return {} + + # TODO:aprimorar. talvez criar os campos relacionados com os campos e totais + # do documento fiscal e buscar apenas os percentuais da primeira linha + first_line = self.fiscal_line_ids[0] + + icms = { + "CST": self.cte40_CST, + "vBC": 0.0, + "pRedBC": first_line.icms_reduction, + "pICMS": first_line.icms_percent, + "vICMS": 0.0, + "vICMSSubstituto": 0.0, + "indSN": int(self.cte40_indSN), + "vBCSTRet": 0.0, + "vICMSSTRet": 0.0, + "pICMSSTRet": first_line.icmsst_wh_percent, + } + + for line in self.fiscal_line_ids: + icms["vBC"] += line.icms_base + icms["vICMS"] += line.icms_value + icms["vICMSSubstituto"] += line.icms_substitute + icms["vBCSTRet"] += line.icmsst_wh_base + icms["vICMSSTRet"] += line.icmsst_wh_value + + # Formatar os valores acumulados + icms["vBC"] = str("%.02f" % icms["vBC"]) + icms["vICMS"] = str("%.02f" % icms["vICMS"]) + icms["vICMSSubstituto"] = str("%.02f" % icms["vICMSSubstituto"]) + icms["vBCSTRet"] = str("%.02f" % icms["vBCSTRet"]) + icms["vICMSSTRet"] = str("%.02f" % icms["vICMSSTRet"]) + icms["pRedBC"] = str("%.04f" % icms["pRedBC"]) + icms["pICMS"] = str("%.02f" % icms["pICMS"]) + icms["pICMSSTRet"] = str("%.02f" % icms["pICMSSTRet"]) + + return icms + + def _export_fields_cte_40_timp(self, xsd_fields, class_obj, export_dict): + # TODO Not Implemented + for record in self.filtered(filter_processador_edoc_cte): + if "cte40_ICMSOutraUF" in xsd_fields: + xsd_fields.remove("cte40_ICMSOutraUF") + + xsd_fields = [record.cte40_choice_icms] + icms_tag = ( + record.cte40_choice_icms.replace("cte40_", "") + .replace("ICMSSN", "Icmssn") + .replace("ICMS", "Icms") + ) + binding_module = sys.modules[record._get_spec_property("binding_module")] + icms = binding_module.Timp + icms_binding = getattr(icms, icms_tag) + icms_dict = record._export_fields_cte40_icms() + sliced_icms_dict = { + key: icms_dict.get(key) + for key in icms_binding.__dataclass_fields__.keys() + if icms_dict.get(key) + } + export_dict[icms_tag.upper()] = icms_binding(**sliced_icms_dict) + + # ########################## + # # CT-e tag: ICMSUFFim + # ########################## + + # cte40_vBCUFFim = fields.Monetary(related="icms_destination_base") + # cte40_pFCPUFFim = fields.Monetary(compute="_compute_cte40_ICMSUFFim", store=True) + # cte40_pICMSUFFim = fields.Monetary(compute="_compute_cte40_ICMSUFFim", store=True) + # # TODO + # # cte40_pICMSInter = fields.Selection( + # # selection=[("0", "Teste")], + # # compute="_compute_cte40_ICMSUFFim") + + # def _compute_cte40_ICMSUFFim(self): + # for record in self: + # # if record.icms_origin_percent: + # # record.cte40_pICMSInter = + # str("%.02f" % record.icms_origin_percent) + # # else: + # # record.cte40_pICMSInter = False + + # record.cte40_pFCPUFFim = record.icmsfcp_percent + # record.cte40_pICMSUFFim = record.icms_destination_percent + + # cte40_vFCPUFfim = fields.Monetary(related="icmsfcp_value") + # cte40_vICMSUFFim = fields.Monetary(related="icms_destination_value") + # cte40_vICMSUFIni = fields.Monetary(related="icms_origin_value") + + ##################################### + # CT-e tag: infCTeNorm and infCteComp + ##################################### + + cte40_choice_infcteNorm_infcteComp = fields.Selection( + selection=[ + ("cte40_infCTeComp", "infCTeComp"), + ("cte40_infCTeNorm", "infCTeNorm"), + ], + default="cte40_infCTeNorm", + ) + + # def _compute_cte40_infDoc(self): + # for doc in self: + # doc.cte40_infDoc = doc + + cte40_infCTeComp = fields.One2many( + comodel_name="l10n_br_fiscal.document.related", + inverse_name="document_id", + ) + + ########################## + # CT-e tag: infCarga + ########################## + + cte40_vCarga = fields.Monetary( + string="Valor total da carga", + ) + + cte40_proPred = fields.Char( + string="Produto predominante", + ) + + cte40_xOutCat = fields.Char( + string="Outras características da carga", + ) + + cte40_infQ = fields.One2many( + comodel_name="l10n_br_cte.cargo.quantity.infos", + inverse_name="document_id", + compute="_compute_cte40_infQ", + readonly=False, + store=True, + ) + + cte40_vCargaAverb = fields.Monetary( + string="Valor da Carga para efeito de averbação", + ) + + ########################## + # CT-e tag: infDoc + ########################## + + cte40_infDoc = fields.Many2one( + comodel_name="l10n_br_fiscal.document", + compute="_compute_cte40_infDoc", + string="Informações dos documentos transportados", + ) + + def _compute_cte40_infDoc(self): + for doc in self.filtered(filter_processador_edoc_cte): + doc.cte40_infDoc = doc + + def _compute_cte40_infNFe(self): + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_infNFe = record.document_related_ids.filtered( + lambda r: r.cte40_infDoc == "cte40_infNFe" + ) + + def _compute_cte40_infOutros(self): + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_infOutros = record.document_related_ids.filtered( + lambda r: r.cte40_infDoc == "cte40_infOutros" + ) + + cte40_infNFe = fields.One2many( + comodel_name="l10n_br_fiscal.document.related", + inverse_name="document_id", + string="Informações das NF-e DOCS (Cte)", + compute="_compute_cte40_infNFe", + ) + + cte40_infOutros = fields.One2many( + comodel_name="l10n_br_fiscal.document.related", + inverse_name="document_id", + string="Informações dos Outros DOCS (Cte)", + compute="_compute_cte40_infOutros", + ) + + ########################## + # CT-e tag: veicNovos + ########################## + + cte40_veicNovos = fields.One2many( + comodel_name="l10n_br_cte.transported.vehicles", + inverse_name="document_id", + ) + + ########################## + # CT-e tag: autXML + # Compute Methods + ########################## + + def _default_cte40_autxml(self): + company = self.env.company + authorized_partners = [] + if company.accountant_id: + authorized_partners.append(company.accountant_id.id) + if company.technical_support_id: + authorized_partners.append(company.technical_support_id.id) + return authorized_partners + + ########################## + # CT-e tag: autXML + ########################## + + cte40_autXML = fields.One2many(default=_default_cte40_autxml) + + ########################## + # CT-e tag: infCTeSupl + ########################## + + cte40_infCTeSupl = fields.Many2one( + comodel_name="l10n_br_fiscal.document.supplement", + ) + + ########################## + # CT-e tag: infRespTec + ########################## + + cte40_infRespTec = fields.Many2one( + comodel_name="res.partner", + related="company_id.technical_support_id", + ) + + ########################## + # CT-e tag: infmodal + ########################## + + cte40_modal = fields.Selection(related="transport_modal") + + cte_modal = fields.Selection(related="transport_modal") + + cte40_versaoModal = fields.Char(default=CTE_MODAL_VERSION_DEFAULT) + + # Campos do Modal Aereo + cte_modal_aereo_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aereo") + + cte40_nMinu = fields.Char( + string="Número da Minuta", + help=( + "Número da Minuta\nDocumento que precede o CT-e, assinado pelo " + "expedidor, espécie de pedido de serviço" + ), + ) + + cte40_nOCA = fields.Char( + string="Número Operacional do Conhecimento Aéreo", + help=( + "Número Operacional do Conhecimento Aéreo\nRepresenta o número de " + "controle comumente utilizado pelo conhecimento aéreo composto por" + " uma sequência numérica de onze dígitos. Os três primeiros " + "dígitos representam um código que os operadores de transporte " + "aéreo associados à IATA possuem. Em seguida um número de série de" + " sete dígitos determinados pelo operador de transporte aéreo. " + "Para finalizar, um dígito verificador, que é um sistema de módulo" + " sete imponderado o qual divide o número de série do conhecimento" + " aéreo por sete e usa o resto como dígito de verificação." + ), + ) + + cte40_dPrevAereo = fields.Date( + string="Data prevista da entrega", + help="Data prevista da entrega\nFormato AAAA-MM-DD", + ) + + cte40_xDime = fields.Char( + string="Dimensão", + help=( + "Dimensão\nFormato:1234X1234X1234 (cm). Esse campo deve sempre que" + " possível ser preenchido. Entretanto, quando for impossível o " + "preenchimento das dimensões, fica obrigatório o preenchimento da " + "cubagem em metro cúbico do leiaute do CT-e da estrutura genérica " + "(infQ)." + ), + ) + + # TODO: Tratar + # def _compute_cte40_dime(self): + # for record in self: + # for package in record.product_id.packaging_ids: + # record.cte40_xDime = ( + # package.width + "X" + package.packaging_length + + # "X" + package.width + # ) + + cte40_CL = fields.Char( + string="Classe", + help=( + "Classe\nPreencher com:\n\t\t\t\t\t\t\t\t\tM - Tarifa " + "Mínima;\n\t\t\t\t\t\t\t\t\tG - Tarifa Geral;\n\t\t\t\t\t\t\t\t\tE" + " - Tarifa Específica" + ), + ) + + cte40_cTar = fields.Char( + string="Código da Tarifa", + help=( + "Código da Tarifa\nDeverão ser incluídos os códigos de três " + "dígitos, correspondentes à tarifa." + ), + ) + # Existem dois vTar no spec, um float e um monetary, por isso a mudança de nome + cte40_aereo_vTar = fields.Monetary( + string="Valor da Tarifa", + currency_field="brl_currency_id", + help="Valor da Tarifa\nValor da tarifa por kg quando for o caso.", + ) + + cte40_peri = fields.One2many( + comodel_name="l10n_br_cte.modal.aereo.peri", + inverse_name="document_id", + string="Dados de carga perigosa", + ) + + # Campos do Modal Aquaviario + cte_modal_aquaviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aquav") + + # TODO: Tratar + # cte40_vPrest = fields.Monetary( + # compute="_compute_cte40_vPrest", # FIX + # store=True, + # string="Valor da Prestação Base de Cálculo", + # ) + + cte40_vAFRMM = fields.Monetary( + string="AFRMM", + currency_field="brl_currency_id", + help=("AFRMM (Adicional de Frete para Renovação da Marinha Mercante)"), + ) + + cte40_xNavio = fields.Char(string="Identificação do Navio") + + cte40_nViag = fields.Char(string="Número da Viagem") + + cte40_direc = fields.Selection( + selection=[ + ("N", "Norte, L-Leste, S-Sul, O-Oeste"), + ("S", "Sul, O-Oeste"), + ("L", "Leste, S-Sul, O-Oeste"), + ("O", "Oeste"), + ], + string="Direção", + help="Direção\nPreencher com: N-Norte, L-Leste, S-Sul, O-Oeste", + ) + + cte40_irin = fields.Char( + string="Irin do navio", + help="Irin do navio sempre deverá ser informado", + ) + + cte40_tpNav = fields.Selection( + selection=[ + ("0", "Interior"), + ("1", "Cabotagem"), + ], + string="Tipo de Navegação", + help=( + "Tipo de Navegação\nPreencher com: \n\t\t\t\t\t\t0 - " + "Interior;\n\t\t\t\t\t\t1 - Cabotagem" + ), + ) + + cte40_balsa = fields.One2many( + comodel_name="l10n_br_cte.modal.aquav.balsa", + inverse_name="document_id", + string="Grupo de informações das balsas", + ) + + # Campos do Modal Dutoviario + cte_modal_dutoviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.duto") + + cte40_dIni = fields.Date(string="Data de Início da prestação do serviço") + + cte40_dFim = fields.Date(string="Data de Fim da prestação do serviço") + + cte40_vTar = fields.Float(string="Valor da tarifa") + + # Campos do Modal Ferroviario + cte_modal_ferroviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.ferrov") + + cte40_tpTraf = fields.Selection( + selection=FERROV_TPTRAF, + default="0", + string="Tipo de Tráfego", + ) + + cte40_fluxo = fields.Char( + string="Fluxo Ferroviário", + help=( + "Fluxo Ferroviário\nTrata-se de um número identificador do " + "contrato firmado com o cliente" + ), + ) + + cte40_pass = fields.Char( + string="Fluxo Pass", + ) + + cte40_vFrete = fields.Monetary( + related="amount_freight_value", + string="Valor do Frete do Tráfego Mútuo", + currency_field="brl_currency_id", + ) + + cte40_respFat = fields.Selection( + TRAFMUT_RESPFAT, + string="Responsável pelo Faturamento", + ) + + cte40_ferrEmi = fields.Selection( + TRAFMUT_FERREMI, + string="Ferrovia Emitente do CTe", + help=( + "Ferrovia Emitente do CTe\nPreencher com: " + "\n\t\t\t\t\t\t\t\t\t1-Ferrovia de origem; " + "\n\t\t\t\t\t\t\t\t\t2-Ferrovia de destino" + ), + ) + + cte40_chCTeFerroOrigem = fields.Char( + string="Chave de acesso do CT-e emitido", + help="Chave de acesso do CT-e emitido pelo ferrovia de origem", + ) + + cte40_ferroEnv = fields.Many2many( + comodel_name="res.partner", + string="Informações das Ferrovias Envolvidas", + ) + + # Campos do Modal rodoviario + cte_modal_rodoviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.rodo") + + cte40_RNTRC = fields.Char( + string="RNTRC", + help="Registro Nacional de Transportadores Rodoviários de Carga", + compute="_compute_cte40_RNTRC", + store=True, + ) + + @api.depends( + "issuer", + "company_id", + "company_id.partner_id.rntrc_code", + "partner_id", + "partner_id.rntrc_code", + ) + def _compute_cte40_RNTRC(self): + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_RNTRC = None + if record.issuer == DOCUMENT_ISSUER_COMPANY and record.company_id: + record.cte40_RNTRC = record.company_id.partner_id.rntrc_code + elif record.partner_id: + record.cte40_RNTRC = record.partner_id.rntrc_code + + cte40_occ = fields.One2many( + comodel_name="l10n_br_cte.modal.rodo.occ", + inverse_name="document_id", + string="Ordens de Coleta associados", + copy=False, + ) + + ########################## + # CT-e tag: infmodal + # Compute Methods + ########################## + + def _export_fields_cte_40_tcte_infmodal(self, xsd_fields, class_obj, export_dict): + if self.cte40_modal == "01": + export_dict["any_element"] = self._export_cte_modal_rodoviario() + elif self.cte40_modal == "02": + export_dict["any_element"] = self._export_cte_modal_aereo() + elif self.cte40_modal == "03": + export_dict["any_element"] = self._export_cte_modal_aquaviario() + elif self.cte40_modal == "04": + export_dict["any_element"] = self._export_cte_modal_ferroviario() + elif self.cte40_modal == "05": + export_dict["any_element"] = self._export_cte_modal_dutoviario() + + def _export_cte_modal_aereo(self): + if not self.cte_modal_aereo_id: + self.cte_modal_aereo_id = self.cte_modal_aereo_id.create( + {"document_id": self.id} + ) + + return self.cte_modal_aereo_id._build_binding("cte", "40") + + def _export_cte_modal_ferroviario(self): + if not self.cte_modal_ferroviario_id: + self.cte_modal_ferroviario_id = self.cte_modal_ferroviario_id.create( + {"document_id": self.id} + ) + + return self.cte_modal_ferroviario_id._build_binding("cte", "40") + + def _export_cte_modal_aquaviario(self): + if not self.cte_modal_aquaviario_id: + self.cte_modal_aquaviario_id = self.cte_modal_aquaviario_id.create( + {"document_id": self.id} + ) + + return self.cte_modal_aquaviario_id._build_binding("cte", "40") + + def _export_cte_modal_rodoviario(self): + if not self.cte_modal_rodoviario_id: + self.cte_modal_rodoviario_id = self.cte_modal_rodoviario_id.create( + {"document_id": self.id} + ) + + return self.cte_modal_rodoviario_id._build_binding("cte", "40") + + def _export_cte_modal_dutoviario(self): + if not self.cte_modal_dutoviario_id: + self.cte_modal_dutoviario_id = self.cte_modal_dutoviario_id.create( + {"document_id": self.id} + ) + + return self.cte_modal_dutoviario_id._build_binding("cte", "40") + + ################################ + # Framework Spec model's methods + ################################ + + def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + if xsd_field == "cte40_tpAmb": + self.env.context = dict(self.env.context) + self.env.context.update({"tpAmb": self[xsd_field]}) + self.env.context.update({"doc": self.id}) + + # TODO: Força a remoção da tag infGlobalizado já que o + # campo xObs está no l10n_br_fiscal.document + if xsd_field == "cte40_infGlobalizado": + return False + if xsd_field == "cte40_toma4" and self.cte40_choice_toma == "cte40_toma3": + return False + elif xsd_field == "cte40_toma3" and self.cte40_choice_toma == "cte40_toma4": + return False + return super()._export_field(xsd_field, class_obj, member_spec, export_value) + + def _export_many2one(self, field_name, xsd_required, class_obj=None): + """ + Overriden to avoid creating inner tag for m2o if none of the + denormalized inner attribute has been set. + """ + self.ensure_one() + if field_name in self._get_stacking_points().keys(): + if field_name == "cte40_ISSQNtot" and not any( + t == "issqn" + for t in self.cte40_det.mapped("product_id.tax_icms_or_issqn") + ): + return False + + elif (not xsd_required) and field_name not in ["cte40_enderDest"]: + comodel = self.env[ + self._get_stacking_points().get(field_name).comodel_name + ] + fields = [ + f + for f in comodel._fields + if f.startswith(self._spec_prefix()) + and f in self._fields.keys() + and f + # don't try to cte40_fat id when reading cte40_cobr for instance + not in self._get_stacking_points().keys() + ] + sub_tag_read = self.read(fields)[0] + if not any( + v + for k, v in sub_tag_read.items() + if k.startswith(self._spec_prefix()) + ): + return False + + if ( + field_name == "cte40_emit" + and self.fiscal_operation_type == "out" + and self.issuer == "company" + ): + self._set_cte40_IEST() + res = super()._export_many2one(field_name, xsd_required, class_obj) + if self.company_inscr_est_st: + res.IEST = self.company_inscr_est_st + return res + + return super()._export_many2one(field_name, xsd_required, class_obj) + + @api.model + def _prepare_import_dict( + self, values, model=None, parent_dict=None, defaults_model=None + ): + return { + **super()._prepare_import_dict(values, model, parent_dict, defaults_model), + "imported_document": True, + } + + def _build_attr(self, node, fields, vals, path, attr): + key = f"cte40_{attr[0]}" # TODO schema wise + value = getattr(node, attr[0]) + + # if attr[0] == "any_element": # build modal + # modal_id = self._get_modal_to_build(node.any_element.__module__) + # if modal_id is False: + # return + + # modal_attrs = modal_id.build_attrs(value, path=path) + # for chave, valor in modal_attrs.items(): + # vals[chave] = valor + # return + + if key == "cte40_mod": + if isinstance(value, Enum): + value = value.value + + vals["document_type_id"] = ( + self.env["l10n_br_fiscal.document.type"] + .search([("code", "=", value)], limit=1) + .id + ) + + return super()._build_attr(node, fields, vals, path, attr) + + def _build_many2one(self, comodel, vals, new_value, key, value, path): + if key == "cte40_emit" and self.env.context.get("edoc_type") == "in": + enderEmit_value = self.env["res.partner"].build_attrs( + value.enderEmit, path=path + ) + new_value.update(enderEmit_value) + company_cnpj = self.env.company.cnpj_cpf.translate( + str.maketrans("", "", string.punctuation) + ) + emit_cnpj = new_value.get("cte40_CNPJ").translate( + str.maketrans("", "", string.punctuation) + ) + if company_cnpj != emit_cnpj: + vals["issuer"] = "partner" + new_value["is_company"] = True + new_value["cnpj_cpf"] = emit_cnpj + super()._build_many2one( + self.env["res.partner"], vals, new_value, "partner_id", value, path + ) + elif key == "cte40_dest" and self.env.context.get("edoc_type") == "out": + enderDest_value = self.env["res.partner"].build_attrs( + value.enderDest, path=path + ) + new_value.update(enderDest_value) + company_cnpj = self.env.company.cnpj_cpf.translate( + str.maketrans("", "", string.punctuation) + ) + dest_cnpj = new_value.get("cte40_CNPJ").translate( + str.maketrans("", "", string.punctuation) + ) + if company_cnpj != dest_cnpj: + vals["issuer"] = "partner" + new_value["is_company"] = True + new_value["cnpj_cpf"] = dest_cnpj + super()._build_many2one( + self.env["res.partner"], vals, new_value, "partner_id", value, path + ) + elif ( + self.env.context.get("edoc_type") == "in" + and key + in [ + "cte40_dest", + "cte40_enderDest", + ] + ) or ( + self.env.context.get("edoc_type") == "out" + and key + in [ + "cte40_emit", + "cte40_enderEmit", + ] + ): + # this would be the emit/company data, but we won't update it on + # CTe import so just do nothing + return + elif ( + self._name == "account.invoice" + and comodel._name == "l10n_br_fiscal.document" + ): + # module l10n_br_account_nfe + # stacked m2o + vals.update(new_value) + else: + super()._build_many2one(comodel, vals, new_value, key, value, path) + + @api.model + def _get_concrete_model(self, model_name): + result = super()._get_concrete_model(model_name) + if self._module == "l10n_br_cte" and not result: + model_type = model_name.split(".")[-1] + model_name = model_name.rpartition(".")[0] + ".tcte_" + model_type + result = super()._get_concrete_model(model_name) + return result + + ################################ + # Business Model Methods + ################################ + + def _serialize(self, edocs): + edocs = super()._serialize(edocs) + for record in self.with_context(lang="pt_BR").filtered( + filter_processador_edoc_cte + ): + inf_cte = record._build_binding("cte", "40") + + inf_cte_supl = None + if record.cte40_infCTeSupl: + inf_cte_supl = record.cte40_infCTeSupl._build_binding("cte", "40") + + cte = Cte(infCte=inf_cte, infCTeSupl=inf_cte_supl, signature=None) + edocs.append(cte) + return edocs + + # TODO: precisa tratar a lib nfelib + # def _edoc_processor(self): + # if self.document_type != MODELO_FISCAL_CTE: + # return super()._edoc_processor() + + # if not self.company_id.certificate_nfe_id: + # raise UserError(_("Certificado não encontrado")) + + # certificado = self.env.company._get_br_ecertificate() + # session = Session() + # session.verify = False + # transmissao = TransmissaoCTE(certificado, session) + # return edoc_cte( + # transmissao, + # self.company_id.state_id.ibge_code, + # self.cte40_versao, + # self.cte40_tpAmb, + # ) + + def _edoc_processor(self): + super()._edoc_processor() + + def _document_export(self, pretty_print=True): + result = super()._document_export() + for record in self.filtered(filter_processador_edoc_cte): + edoc = record.serialize()[0] + # processador = record._edoc_processor() + xml_file = edoc.to_xml() + event_id = self.event_ids.create_event_save_xml( + company_id=self.company_id, + environment=( + EVENT_ENV_PROD if self.cte40_tpAmb == "1" else EVENT_ENV_HML + ), + event_type="0", + xml_file=xml_file, + document_id=self, + ) + record.authorization_event_id = event_id + + # TODO: precisa tratar + # xml_assinado = processador.assina_raiz(edoc, edoc.infCte.Id) + # self._validate_xml(xml_assinado) + return result + + def _validate_xml(self, xml_file): + self.ensure_one() + + if not self.filtered(filter_processador_edoc_cte): + return super()._validate_xml(xml_file) + + erros = Cte.schema_validation(xml_file) + erros = "\n".join(erros) + self.write({"xml_error_message": erros or False}) + + def update_status_cte(self, process): + self.ensure_one() + + if hasattr(process, "protocolo"): + infProt = process.protocolo.infProt + else: + infProt = process.resposta.protCTe.infProt + + if infProt.cStat in AUTORIZADO: + state = SITUACAO_EDOC_AUTORIZADA + self._cte_response_add_proc(process) + elif infProt.cStat in DENEGADO: + state = SITUACAO_EDOC_DENEGADA + else: + state = SITUACAO_EDOC_REJEITADA + if self.authorization_event_id and infProt.nProt: + if type(infProt.dhRecbto) == datetime: + protocol_date = fields.Datetime.to_string(infProt.dhRecbto) + else: + protocol_date = fields.Datetime.to_string( + datetime.fromisoformat(infProt.dhRecbto) + ) + + self.authorization_event_id.set_done( + status_code=infProt.cStat, + response=infProt.xMotivo, + protocol_date=protocol_date, + protocol_number=infProt.nProt, + file_response_xml=process.processo_xml.decode("utf-8"), + ) + self.write( + { + "status_code": infProt.cStat, + "status_name": infProt.xMotivo, + } + ) + self._change_state(state) + + def _eletronic_document_send(self): + super(CTe, self)._eletronic_document_send() + for record in self.filtered(filter_processador_edoc_cte): + if record.xml_error_message: + return + processador = record._edoc_processor() + for edoc in record.serialize(): + process = None + for p in processador.processar_documento(edoc): + process = p + if process.webservice == "cteRecepcaoLote": + record.authorization_event_id._save_event_file( + process.envio_xml, "xml" + ) + + if process.resposta.cStat in LOTE_PROCESSADO + ["100"]: + record.update_status_cte(process) + + elif process.resposta.cStat in DENEGADO: + record._change_state(SITUACAO_EDOC_DENEGADA) + record.write( + { + "status_code": process.resposta.cStat, + "status_name": process.resposta.xMotivo, + } + ) + + else: + record._change_state(SITUACAO_EDOC_REJEITADA) + record.write( + { + "status_code": process.resposta.cStat, + "status_name": process.resposta.xMotivo, + } + ) + + def _document_cancel(self, justificative): + result = super(CTe, self)._document_cancel(justificative) + online_event = self.filtered(filter_processador_edoc_cte) + if online_event: + online_event._cte_cancel() + return result + + def _cte_cancel(self): + self.ensure_one() + processador = self._edoc_processor() + + if not self.authorization_protocol: + raise UserError(_("Authorization Protocol Not Found!")) + + evento = processador.cancela_documento( + chave=self.document_key, + protocolo_autorizacao=self.authorization_protocol, + justificativa=self.cancel_reason.replace("\n", "\\n"), + ) + process = processador.enviar_lote_evento(lista_eventos=[evento]) + + self.cancel_event_id = self.event_ids.create_event_save_xml( + company_id=self.company_id, + environment=(EVENT_ENV_PROD if self.cte40_tpAmb == "1" else EVENT_ENV_HML), + event_type="2", + xml_file=process.envio_xml, + document_id=self, + ) + + resposta = process.resposta.infEvento + + if resposta.cStat not in CANCELADO: + mensagem = "Erro no cancelamento" + mensagem += "\nCódigo: " + resposta.cStat + mensagem += "\nMotivo: " + resposta.xMotivo + raise UserError(mensagem) + + if resposta.chCTe == self.document_key: + if resposta.cStat in CANCELADO_FORA_PRAZO: + self.state_fiscal = SITUACAO_FISCAL_CANCELADO_EXTEMPORANEO + elif resposta.cStat in CANCELADO_DENTRO_PRAZO: + self.state_fiscal = SITUACAO_FISCAL_CANCELADO + + self.state_edoc = SITUACAO_EDOC_CANCELADA + self.cancel_event_id.set_done( + status_code=resposta.cStat, + response=resposta.xMotivo, + protocol_date=fields.Datetime.to_string( + datetime.fromisoformat(resposta.dhRegEvento) + ), + protocol_number=resposta.nProt, + file_response_xml=process.retorno.content.decode("utf-8"), + ) + + def _document_correction(self, justificative): + result = super(CTe, self)._document_correction(justificative) + online_event = self.filtered(filter_processador_edoc_cte) + if online_event: + online_event._cte_correction(justificative) + return result + + def _cte_correction(self, justificative): + self.ensure_one() + processador = self._edoc_processor() + + numeros = self.event_ids.filtered( + lambda e: e.type == "14" and e.state == "done" + ).mapped("sequence") + + sequence = str(int(max(numeros)) + 1) if numeros else "1" + + evento = processador.carta_correcao( + chave=self.document_key, + protocolo_autorizacao=self.authorization_protocol, + justificativa=justificative.replace("\n", "\\n"), + sequencia=sequence, + ) + process = processador.enviar_lote_evento(lista_eventos=[evento]) + # Gravamos o arquivo no disco e no filestore ASAP. + event_id = self.event_ids.create_event_save_xml( + company_id=self.company_id, + environment=(EVENT_ENV_PROD if self.cte40_tpAmb == "1" else EVENT_ENV_HML), + event_type="14", + xml_file=process.envio_xml, + document_id=self, + sequence=sequence, + justification=justificative, + ) + + resposta = process.resposta.infEvento + + if resposta.cStat not in EVENTO_RECEBIDO and not ( + resposta.chCTe == self.document_key + ): + mensagem = "Erro na carta de correção" + mensagem += "\nCódigo: " + resposta.cStat + mensagem += "\nMotivo: " + resposta.xMotivo + raise UserError(mensagem) + + event_id.set_done( + status_code=resposta.cStat, + response=resposta.xMotivo, + protocol_date=fields.Datetime.to_string( + datetime.fromisoformat(resposta.dhRegEvento) + ), + protocol_number=resposta.nProt, + file_response_xml=process.retorno.content.decode("utf-8"), + ) + + def _document_qrcode(self): + super()._document_qrcode() + + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_infCTeSupl = self.env[ + "l10n_br_fiscal.document.supplement" + ].create( + { + "qrcode": record.get_cte_qrcode(), + } + ) + + def get_cte_qrcode(self): + # TODO: Tratar + # if self.document_type != MODELO_FISCAL_CTE: + # return + + processador = self._edoc_processor() + # if self.cte_transmission == "1": + # return processador.monta_qrcode(self.document_key) + return processador.monta_qrcode(self.document_key) + + # serialized_doc = self.serialize()[0] + # xml = processador.assina_raiz(serialized_doc, serialized_doc.infNFe.Id) + # return processador._generate_qrcode_contingency(serialized_doc, xml) + + def _need_compute_cte_tags(self): + if ( + self.state_edoc in [SITUACAO_EDOC_EM_DIGITACAO, SITUACAO_EDOC_A_ENVIAR] + and self.processador_edoc == PROCESSADOR_OCA + and self.document_type_id.code in ["57"] + and self.issuer == DOCUMENT_ISSUER_COMPANY + ): + return True + else: + return False + + def _cte_response_add_proc(self, ws_response_process): + """ + Inject the final NF-e, tag `cteProc`, into the response. + """ + xml_soap = ws_response_process.retorno.content + tree_soap = etree.fromstring(xml_soap) + prot_element = tree_soap.xpath("//cte:protCTe", namespaces=CTE_XML_NAMESPACE)[0] + proc_xml = self._cte_create_proc(prot_element) + if proc_xml: + # it is not always possible to create cteProc. + parser = XmlParser() + proc = parser.from_string(proc_xml.decode(), CteProc) + ws_response_process.processo = proc + ws_response_process.processo_xml = proc_xml + + def _cte_create_proc(self, prot_element): + """ + Create the `cteProc` XML by combining the CT-e and the authorization protocol. + + This method decodes the saved `enviCTe` message, extracts the CTe> tag, + and combines it with the provided authorization protocol element to create + the `cteProc` XML, which represents the finalized CT-e document. + + Args: + prot_element: The XML element containing the authorization protocol. + + Returns: + The assembled `cteProc` XML, or None if the `send_file_id` data is not + found. + + Note: + Useful for recreating the final CT-e XML, as SEFAZ does not provide the + complete XML upon consultation, only the authorization protocol. + """ + self.ensure_one() + + if not self.send_file_id.datas: + _logger.info( + "CT-e data not found when trying to assemble the " + "xml with the authorization protocol (cteProc)" + ) + return None + + processor = self._edoc_processor() + + # Extract the tag from the `enviCTe` message, which represents the CT-e + xml_send = base64.b64decode(self.send_file_id.datas) + tree_send = etree.fromstring(xml_send) + doc_element = tree_send.xpath("//cte:CTe", namespaces=CTE_XML_NAMESPACE)[0] + + # Assemble the `cteProc` using the erpbrasil.edoc library. + proc_xml = processor.monta_cte_proc(doc=doc_element, prot=prot_element) + + return proc_xml + + def import_binding_cte(self, binding, edoc_type="out"): + document = ( + self.env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type=edoc_type, dry_run=False) + .build_from_binding("cte", "40", binding.CTe.infCte) + ) + + if edoc_type == "in" and document.company_id.cnpj_cpf != cnpj_cpf.formata( + binding.CTe.infCte.emit.CNPJ + ): + document.fiscal_operation_type = "in" + document.issuer = "partner" + + return document + + def _document_number(self): + # TODO: Criar campos no fiscal para codigo aleatorio e digito verificador, + # pois outros modelos também precisam dessescampos: CT-e, MDF-e etc + result = super()._document_number() + for record in self.filtered(filter_processador_edoc_cte): + if record.document_key: + try: + chave = ChaveEdoc(record.document_key) + record.cte40_cCT = chave.codigo_aleatorio + record.cte40_cDV = chave.digito_verificador + except Exception as e: + raise ValidationError( + _( + "%(name)s:\n %(error)s", + name=record.document_type_id.name, + error=e, + ) + ) from e + return result + + def make_pdf(self): + if not self.filtered(filter_processador_edoc_cte): + return super().make_pdf() + + attachment_data = { + "name": self.document_key + ".pdf", + "res_model": self._name, + "res_id": self.id, + "mimetype": "application/pdf", + "type": "binary", + } + report = self.env.ref("l10n_br_cte.report_dacte") + pdf_data = report._render_qweb_pdf(self.fiscal_line_ids.document_id.ids) + attachment_data["datas"] = base64.b64encode(pdf_data[0]) + file_pdf = self.file_report_id + self.file_report_id = False + file_pdf.unlink() + + self.file_report_id = self.env["ir.attachment"].create(attachment_data) diff --git a/l10n_br_cte/models/document_cargo_quantity_infos.py b/l10n_br_cte/models/document_cargo_quantity_infos.py new file mode 100644 index 000000000000..57b5e99397c2 --- /dev/null +++ b/l10n_br_cte/models/document_cargo_quantity_infos.py @@ -0,0 +1,26 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeCargoQuantityInfos(spec_models.SpecModel): + _name = "l10n_br_cte.cargo.quantity.infos" + _inherit = "cte.40.tcte_infq" + _description = "Informações de quantidades da Carga do CT-e" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_cUnid = fields.Selection( + required=True, + ) + + cte40_tpMed = fields.Char( + required=True, + ) + + cte40_qCarga = fields.Float( + required=True, + ) diff --git a/l10n_br_cte/models/document_comment.py b/l10n_br_cte/models/document_comment.py new file mode 100644 index 000000000000..e02083676884 --- /dev/null +++ b/l10n_br_cte/models/document_comment.py @@ -0,0 +1,35 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeComment(spec_models.StackedModel): + _name = "l10n_br_fiscal.comment" + _inherit = ["l10n_br_fiscal.comment", "cte.40.tcte_obscont", "cte.40.tcte_obsfisco"] + + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_obscont" + _cte40_stacking_skip_paths = ("cte40_ObsCont_compl_id", "cte40_ObsFisco_compl_id") + + cte40_xCampo = fields.Char() + + cte40_xTexto = fields.Text() + + def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + if xsd_field == "cte40_xCampo": + return self.name[:20].strip() + if xsd_field == "cte40_xTexto": + if "doc" in self.env.context: + doc_id = self.env.context["doc"] + doc = self.env["l10n_br_fiscal.document"].browse(doc_id) + vals = {"user": self.env.user, "ctx": self._context, "doc": doc} + message = self.compute_message(vals).strip() + if self.comment_type == "fiscal": + return message[:60] + return message[:160] + return super()._export_field(xsd_field, class_obj, member_spec, export_value) diff --git a/l10n_br_cte/models/document_line.py b/l10n_br_cte/models/document_line.py new file mode 100644 index 000000000000..babbba3c8811 --- /dev/null +++ b/l10n_br_cte/models/document_line.py @@ -0,0 +1,26 @@ +# Copyright 2023 KMEE +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeLine(spec_models.StackedModel): + _name = "l10n_br_fiscal.document.line" + _inherit = ["l10n_br_fiscal.document.line", "cte.40.tcte_vprest_comp"] + + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_vprest_comp" + _cte40_stacking_skip_paths = ("cte40_Comp_vPrest_id",) + + ########################## + # CT-e tag: comp + ########################## + + cte40_xNome = fields.Text(related="name") + + cte40_vComp = fields.Monetary(related="amount_total") diff --git a/l10n_br_cte/models/document_related.py b/l10n_br_cte/models/document_related.py new file mode 100644 index 000000000000..dde298e111d1 --- /dev/null +++ b/l10n_br_cte/models/document_related.py @@ -0,0 +1,113 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeRelated(spec_models.StackedModel): + _name = "l10n_br_fiscal.document.related" + _inherit = [ + "l10n_br_fiscal.document.related", + "cte.40.tcte_infdoc", + "cte.40.tcte_infctecomp", + ] + + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_infdoc" + + # InfNFe + cte40_chave = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_chave", + ) + + cte40_tpDoc = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_tpDoc", + ) + + # infOutros + + cte40_descOutros = fields.Char(string="Descrição do documento") + + cte40_nDoc = fields.Char(string="Número", default="123123") + + cte40_dEmi = fields.Date( + string="Data de Emissão", + help="Data de Emissão\nFormato AAAA-MM-DD", + ) + + cte40_vDocFisc = fields.Monetary( + string="Valor do documento", + default=1000.0, + currency_field="brl_currency_id", + ) + + cte40_dPrev = fields.Date( + string="Data prevista de entrega", + help="Data prevista de entrega\nFormato AAAA-MM-DD", + ) + + cte40_infDoc = fields.Selection( + related="cte40_choice_infNF_infNFE_infOutros", string="infDoc" + ) + + # infCteNorm + cte40_chCTe = fields.Char(compute="_compute_chCte", string="chCte") + + ########################## + # CT-e tag: infCTeComp + # Compute Methods + ########################## + + def _compute_chCTe(self): + records = "" + for rec in self: + if rec.cte40_Id: + records += rec.document_key + self.cte40_chCTe = records + + cte40_choice_infNF_infNFE_infOutros = fields.Selection( + selection=[ + ("cte40_infNF", "infNF"), # TODO + ("cte40_infNFe", "infNFe"), + ("cte40_infOutros", "Outros"), + ], + compute="_compute_cte_data", + inverse="_inverse_cte40_choice_infNF_infNFE_infOutros", + string="CHOICE", + ) + + @api.depends("document_type_id") + def _compute_cte_data(self): + """Set schema data which are not just related fields""" + for rec in self: + if rec.document_type_id: + if rec.document_type_id.code in ("55",): + rec.cte40_choice_infNF_infNFE_infOutros = "cte40_infNFe" + rec.cte40_chave = rec.document_key + elif rec.document_type_id.code in ("00", "10", "59", "65", "99"): + rec.cte40_choice_infNF_infNFE_infOutros = "cte40_infOutros" + rec.cte40_tpDoc = rec.document_type_id.code + + def _inverse_cte40_chave(self): + for rec in self: + if rec.cte40_chave: + rec.document_key = rec.cte40_chave + + def _inverse_cte40_tpDoc(self): + for rec in self: + if rec.cte40_tpDoc: + rec.document_type_id = rec.cte40_tpDoc + + def _inverse_cte40_choice_infNF_infNFE_infOutros(self): + for rec in self: + if rec.cte40_choice_infNF_infNFE_infOutros == "cte40_infNFe": + rec.document_type_id = self.env.ref("l10n_br_fiscal.document_55") + if rec.cte40_choice_infNF_infNFE_infOutros == "infOutros": + rec.document_type_id = self.env.ref("l10n_br_fiscal.document_01") diff --git a/l10n_br_cte/models/document_supplement.py b/l10n_br_cte/models/document_supplement.py new file mode 100644 index 000000000000..9625c7441c19 --- /dev/null +++ b/l10n_br_cte/models/document_supplement.py @@ -0,0 +1,18 @@ +# Copyright 2023 KMEE (Luiz Felipe do Divino ) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeSupplement(spec_models.StackedModel): + _name = "l10n_br_fiscal.document.supplement" + _inherit = ["l10n_br_fiscal.document.supplement", "cte.40.tcte_infctesupl"] + + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_infctesupl" + + cte40_qrCodCTe = fields.Char(related="qrcode") diff --git a/l10n_br_cte/models/document_transported_vehicles.py b/l10n_br_cte/models/document_transported_vehicles.py new file mode 100644 index 000000000000..004ce2627172 --- /dev/null +++ b/l10n_br_cte/models/document_transported_vehicles.py @@ -0,0 +1,42 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeCargoQuantityInfos(spec_models.SpecModel): + _name = "l10n_br_cte.transported.vehicles" + _inherit = "cte.40.veicnovos" + _description = "Informações dos veículos transportados" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + currency_id = fields.Many2one( + comodel_name="res.currency", + related="document_id.company_id.currency_id", + ) + + cte40_chassi = fields.Char(string="Chassi do veículo", required=True, size=17) + + cte40_cCor = fields.Char(string="Cor do veículo", required=True, size=4) + + cte40_xCor = fields.Char(string="Descrição da cor", required=True) + + cte40_cMod = fields.Char( + string="Código Marca Modelo", + required=True, + ) + + cte40_vUnit = fields.Monetary( + string="Valor Unitário do Veículo", + required=True, + currency_field="currency_id", + ) + + cte40_vFrete = fields.Monetary( + string="Frete Unitário", + required=True, + currency_field="currency_id", + ) diff --git a/l10n_br_cte/models/document_type.py b/l10n_br_cte/models/document_type.py new file mode 100644 index 000000000000..972f15bb43d1 --- /dev/null +++ b/l10n_br_cte/models/document_type.py @@ -0,0 +1,9 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class DocumentType(models.Model): + _inherit = "l10n_br_fiscal.document.type" + _cte_search_keys = ["code"] diff --git a/l10n_br_cte/models/dutoviario.py b/l10n_br_cte/models/dutoviario.py new file mode 100644 index 000000000000..1dad821045f9 --- /dev/null +++ b/l10n_br_cte/models/dutoviario.py @@ -0,0 +1,30 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class Duto(spec_models.StackedModel): + _name = "l10n_br_cte.modal.duto" + _inherit = "cte.40.duto" + _description = "Modal Dutoviario CTe" + + _cte40_stacking_mixin = "cte.40.duto" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_dutoviario_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_dutoviario_v4_00" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_dIni = fields.Date(related="document_id.cte40_dIni") + + cte40_dFim = fields.Date(related="document_id.cte40_dFim") + + cte40_vTar = fields.Float(related="document_id.cte40_vTar") + + def _prepare_dacte_values(self): + if not self: + return {} diff --git a/l10n_br_cte/models/ferroviario.py b/l10n_br_cte/models/ferroviario.py new file mode 100644 index 000000000000..24211db2606c --- /dev/null +++ b/l10n_br_cte/models/ferroviario.py @@ -0,0 +1,51 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class Ferrov(spec_models.StackedModel): + _name = "l10n_br_cte.modal.ferrov" + _inherit = "cte.40.ferrov" + _description = "Modal Ferroviario CTe" + + _cte40_stacking_mixin = "cte.40.ferrov" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_ferroviario_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_ferroviario_v4_00" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_tpTraf = fields.Selection(related="document_id.cte40_tpTraf") + + cte40_fluxo = fields.Char(related="document_id.cte40_fluxo") + + cte40_vFrete = fields.Monetary( + related="document_id.cte40_vFrete", currency_field="currency_id" + ) + + currency_id = fields.Many2one( + comodel_name="res.currency", + default=lambda self: self.env.company.currency_id, + ) + + cte40_chCTeFerroOrigem = fields.Char(related="document_id.cte40_chCTeFerroOrigem") + + cte40_respFat = fields.Selection(related="document_id.cte40_respFat") + + cte40_ferrEmi = fields.Selection(related="document_id.cte40_ferrEmi") + + cte40_ferroEnv = fields.One2many(compute="_compute_railroad") + + @api.depends("document_id.cte40_ferroEnv") + def _compute_railroad(self): + for record in self: + record.cte40_ferroEnv = [(6, 0, record.document_id.cte40_ferroEnv.ids)] + + def _prepare_dacte_values(self): + if not self: + return {} diff --git a/l10n_br_cte/models/res_city.py b/l10n_br_cte/models/res_city.py new file mode 100644 index 000000000000..9543aa6f487b --- /dev/null +++ b/l10n_br_cte/models/res_city.py @@ -0,0 +1,20 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ResCity(models.Model): + _inherit = "res.city" + _cte_search_keys = ["ibge_code"] + + @api.model + def match_or_create_m2o(self, rec_dict, parent_dict, model=None): + """If city not found, break hard, don't create it""" + + if rec_dict.get("ibge_code"): + domain = [("ibge_code", "=", rec_dict.get("ibge_code"))] + match = self.search(domain, limit=1) + if match: + return match.id + return False diff --git a/l10n_br_cte/models/res_company.py b/l10n_br_cte/models/res_company.py new file mode 100644 index 000000000000..63146d1a04b4 --- /dev/null +++ b/l10n_br_cte/models/res_company.py @@ -0,0 +1,151 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + +from ..constants.cte import ( + CTE_ENVIRONMENT_DEFAULT, + CTE_ENVIRONMENTS, + CTE_TRANSMISSION_DEFAULT, + CTE_TRANSMISSIONS, + CTE_TYPE, + CTE_TYPE_DEFAULT, + CTE_VERSION_DEFAULT, + CTE_VERSIONS, +) + +PROCESSADOR_ERPBRASIL_EDOC = "oca" +PROCESSADOR = [(PROCESSADOR_ERPBRASIL_EDOC, "erpbrasil.edoc")] + + +class ResCompany(spec_models.SpecModel): + _name = "res.company" + _inherit = ["res.company", "cte.40.tcte_emit"] + _cte_search_keys = ["cte40_CNPJ", "cte40_xNome", "cte40_xFant"] + + ########################## + # CT-e models fields + ########################## + + cte_default_serie_id = fields.Many2one( + comodel_name="l10n_br_fiscal.document.serie", + string="CT-e Default Serie", + ) + + cte_dacte_layout = fields.Selection( + selection=[("1", "Paisagem"), ("2", "Retrato")], + string="CT-e DACTE Layout", + default="1", + ) + + cte_transmission = fields.Selection( + selection=CTE_TRANSMISSIONS, + string="CTe Transmission", + copy=False, + default=CTE_TRANSMISSION_DEFAULT, + ) + + cte_type = fields.Selection( + selection=CTE_TYPE, + string="CTe Type", + default=CTE_TYPE_DEFAULT, + ) + + cte_environment = fields.Selection( + selection=CTE_ENVIRONMENTS, + string="CTe Environment", + default=CTE_ENVIRONMENT_DEFAULT, + ) + + cte_version = fields.Selection( + selection=CTE_VERSIONS, + string="CTe Version", + default=CTE_VERSION_DEFAULT, + ) + + processador_edoc = fields.Selection( + selection_add=PROCESSADOR, + ) + + cte_authorize_accountant_download_xml = fields.Boolean( + string="Include Accountant Partner data in persons authorized to " + "download CTe XML", + default=False, + ) + + cte40_enderEmit = fields.Many2one( + comodel_name="res.partner", + related="partner_id", + readonly=False, + ) + + cte40_choice_emit = fields.Selection( + [("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ ou CPF?", + compute="_compute_cte_data", + ) + + cte40_CNPJ = fields.Char(related="partner_id.cte40_CNPJ") + + cte40_CPF = fields.Char(related="partner_id.cte40_CPF") + + cte40_xNome = fields.Char(related="partner_id.legal_name") + + cte40_xFant = fields.Char(related="partner_id.name") + + cte40_IE = fields.Char(related="partner_id.cte40_IE") + + cte40_fone = fields.Char(related="partner_id.cte40_fone") + + cte40_CRT = fields.Selection(related="tax_framework") + + dacte_margin_top = fields.Integer( + default=5, help="Top margin in mm for the DACTE layout." + ) + + dacte_margin_right = fields.Integer( + default=5, help="Right margin in mm for the DACTE layout." + ) + + dacte_margin_bottom = fields.Integer( + default=5, help="Bottom margin in mm for the DACTE layout." + ) + + dacte_margin_left = fields.Integer( + default=5, help="Left margin in mm for the DACTE layout." + ) + + def _compute_cte_data(self): + # compute because a simple related field makes the match_record fail + for rec in self: + if rec.partner_id.is_company: + rec.cte40_choice_emit = "cte40_CNPJ" + else: + rec.cte40_choice_emit = "cte40_CPF" + + def _build_attr(self, node, fields, vals, path, attr): + if attr[0] == "enderEmit" and self.env.context.get("edoc_type") == "in": + # we don't want to try build a related partner_id for enderEmit + # when importing an CTe + # instead later the emit tag will be imported as the + # document partner_id (dest) and the enderEmit data will be + # injected in the same res.partner record. + return + return super()._build_attr(node, fields, vals, path, attr) + + @api.model + def _prepare_import_dict( + self, values, model=None, parent_dict=None, defaults_model=None + ): + # we disable enderEmit related creation with dry_run=True + context = self._context.copy() + context["dry_run"] = True + values = super(ResCompany, self.with_context(**context))._prepare_import_dict( + values, model, parent_dict, defaults_model + ) + if not values.get("name"): + values["name"] = values.get("cte40_xFant") or values.get("cte40_xNome") + return values diff --git a/l10n_br_cte/models/res_config_settings.py b/l10n_br_cte/models/res_config_settings.py new file mode 100644 index 000000000000..f9c49cfdced4 --- /dev/null +++ b/l10n_br_cte/models/res_config_settings.py @@ -0,0 +1,22 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + cte_authorize_accountant_download_xml = fields.Boolean( + string="Include Accountant Partner data in persons authorized to " + "download CTe XML", + related="company_id.cte_authorize_accountant_download_xml", + readonly=False, + ) + + cte_transmission = fields.Selection( + string="NFe Transmission", + related="company_id.cte_transmission", + readonly=False, + ) diff --git a/l10n_br_cte/models/res_country.py b/l10n_br_cte/models/res_country.py new file mode 100644 index 000000000000..e4d8595f2366 --- /dev/null +++ b/l10n_br_cte/models/res_country.py @@ -0,0 +1,20 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ResCountry(models.Model): + _inherit = "res.country" + _cte_search_keys = ["bc_code"] + + @api.model + def match_or_create_m2o(self, rec_dict, parent_dict, model=None): + """If country not found, break hard, don't create it""" + + if rec_dict.get("bc_code"): + domain = [("bc_code", "=", rec_dict.get("bc_code"))] + match = self.search(domain, limit=1) + if match: + return match.id + return False diff --git a/l10n_br_cte/models/res_country_state.py b/l10n_br_cte/models/res_country_state.py new file mode 100644 index 000000000000..8b74126d5cbb --- /dev/null +++ b/l10n_br_cte/models/res_country_state.py @@ -0,0 +1,21 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ResCountryState(models.Model): + _inherit = "res.country.state" + _cte_search_keys = ["ibge_code", "code"] + _cte_extra_domain = [("ibge_code", "!=", False)] + + @api.model + def match_or_create_m2o(self, rec_dict, parent_dict, model=None): + """If state not found, break hard, don't create it""" + + if rec_dict.get("code"): + domain = [("code", "=", rec_dict.get("code")), ("ibge_code", "!=", False)] + match = self.search(domain, limit=1) + if match: + return match.id + return False diff --git a/l10n_br_cte/models/res_partner.py b/l10n_br_cte/models/res_partner.py new file mode 100644 index 000000000000..047bfdd39f56 --- /dev/null +++ b/l10n_br_cte/models/res_partner.py @@ -0,0 +1,570 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging + +from erpbrasil.base.fiscal import cnpj_cpf + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + +_logger = logging.getLogger(__name__) + +try: + from erpbrasil.base.misc import format_zipcode, punctuation_rm +except ImportError: + _logger.error("Biblioteca erpbrasil.base não instalada") + + +class ResPartner(spec_models.SpecModel): + _name = "res.partner" + _inherit = [ + "res.partner", + "cte.40.tendereco", + "cte.40.tlocal", + "cte.40.tendeemi", + "cte.40.tcte_dest", + "cte.40.tcte_rem", + "cte.40.exped", + "cte.40.receb", + "cte.40.tresptec", + "cte.40.tcte_autxml", + "cte.40.tenderfer", + ] + _cte_search_keys = ["cte40_CNPJ", "cte40_CPF", "cte40_xNome"] + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_dutoviario_v4_00" + ) + + @api.model + def _prepare_import_dict( + self, values, model=None, parent_dict=None, defaults_model=None + ): + values = super()._prepare_import_dict( + values, model, parent_dict, defaults_model + ) + if not values.get("name") and values.get("legal_name"): + values["name"] = values["legal_name"] + return values + + # cte.40.tlocal / cte.40.enderEmit / 'cte.40.enderDest + # TODO: may be not store=True -> then override match + + cte40_cInt = fields.Char( + string="Código interno da Ferrovia envolvida", + help="Código interno da Ferrovia envolvida\nUso da transportadora", + ) + + cte40_CNPJ = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_CNPJ", + store=True, + compute_sudo=True, + ) + cte40_CPF = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_CPF", + store=True, + compute_sudo=True, + ) + cte40_xLgr = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_nro = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_xCpl = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_xBairro = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_cMun = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_xMun = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + # Char overriding Selection: + cte40_UF = fields.Char( + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + + # Same problem with Tendereco that cte has, it has to use m2o fields + cte40_enderToma = fields.Many2one( + comodel_name="res.partner", + compute="_compute_cte40_enderToma", + compute_sudo=True, + ) + + cte40_enderReme = fields.Many2one( + comodel_name="res.partner", + compute="_compute_cte40_enderReme", + compute_sudo=True, + ) + + cte40_enderDest = fields.Many2one( + comodel_name="res.partner", + compute="_compute_cte40_enderDest", + compute_sudo=True, + ) + + cte40_enderExped = fields.Many2one( + comodel_name="res.partner", + compute="_compute_cte40_enderExped", + compute_sudo=True, + ) + + cte40_enderReceb = fields.Many2one( + comodel_name="res.partner", + compute="_compute_cte40_enderReceb", + compute_sudo=True, + ) + + cte40_enderFerro = fields.Many2one( + comodel_name="res.partner", + compute="_compute_cte40_enderFerro", + compute_sudo=True, + ) + + # Emit + cte40_choice_emit = fields.Selection( + selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ/CPF do Emitente", + compute="_compute_cte_data", + compute_sudo=True, + ) + + # cte.40.tendereco + cte40_CEP = fields.Char( + compute="_compute_cte_data", inverse="_inverse_cte40_CEP", compute_sudo=True + ) + cte40_cPais = fields.Char( + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_xPais = fields.Char( + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_fone = fields.Char( + compute="_compute_cte_data", inverse="_inverse_cte40_fone", compute_sudo=True + ) + + # cte.40.dest + cte40_xNome = fields.Char(related="legal_name") + cte40_xFant = fields.Char(related="name", string="Nome Fantasia") + cte40_IE = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_IE", + compute_sudo=True, + ) + cte40_ISUF = fields.Char(related="suframa") + cte40_email = fields.Char(related="email") + cte40_xEnder = fields.Char( + compute="_compute_cte40_xEnder", + compute_sudo=True, + ) + + # cte.40.infresptec + cte40_xContato = fields.Char(related="legal_name") + + cte40_choice_tlocal = fields.Selection( + selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ/CPF do Parceiro", + compute="_compute_cte_data", + compute_sudo=True, + ) + + cte40_choice_toma = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + cte40_choice_dest = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + cte40_choice_rem = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + cte40_choice_dest = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + cte40_choice_receb = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + cte40_choice_exped = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + # cte.40.autXML + cte40_choice_autxml = fields.Selection( + selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ/CPF do Parceiro Autorizado", + compute="_compute_cte_data", + compute_sudo=True, + ) + + # cte.40.transporta + cte40_choice_transporta = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ], + string="CNPJ or CPF", + compute="_compute_cte_data", + compute_sudo=True, + ) + + def _compute_cte40_xEnder(self): + for rec in self: + # Campos do endereço são separados no Emitente e Destinatario + # porém no caso da Transportadadora o campo do endereço é maior + # porém sem os detalhes como complemento e bairro, mas + # operacionalmente são importantes, por isso caso existam o + # Complemento e o Bairro é melhor agrega-los. + # campo street retorna "street_name, street_number" + endereco = rec.street + if rec.street2: + endereco += " - " + rec.street2 + if rec.district: + endereco += " - " + rec.district + + rec.cte40_xEnder = endereco + + def _compute_cte40_enderToma(self): + for rec in self: + rec.cte40_enderToma = rec.id + + def _compute_cte40_enderDest(self): + for rec in self: + rec.cte40_enderDest = rec.id + + def _compute_cte40_enderReme(self): + for rec in self: + rec.cte40_enderReme = rec.id + + def _compute_cte40_enderReceb(self): + for rec in self: + rec.cte40_enderReceb = rec.id + + def _compute_cte40_enderExped(self): + for rec in self: + rec.cte40_enderExped = rec.id + + def _compute_cte40_enderFerro(self): + for rec in self: + rec.cte40_enderFerro = rec.id + + @api.depends("company_type", "inscr_est", "cnpj_cpf", "country_id") + def _compute_cte_data(self): + """Set schema data which are not just related fields""" + for rec in self: + cnpj_cpf = punctuation_rm(rec.cnpj_cpf) + if cnpj_cpf: + if rec.country_id.code != "BR": + rec.cte40_choice_toma = "cte40_idEstrangeiro" + rec.cte40_choice_dest = "cte40_idEstrangeiro" + rec.cte40_choice_rem = "cte40_idEstrangeiro" + rec.cte40_choice_receb = "cte40_idEstrangeiro" + rec.cte40_choice_exped = "cte40_idEstrangeiro" + rec.cte40_choice_tlocal = False + elif rec.is_company: + rec.cte40_choice_tlocal = "cte40_CNPJ" + rec.cte40_choice_toma = "cte40_CNPJ" + rec.cte40_choice_emit = "cte40_CNPJ" + rec.cte40_choice_dest = "cte40_CNPJ" + rec.cte40_choice_rem = "cte40_CNPJ" + rec.cte40_choice_receb = "cte40_CNPJ" + rec.cte40_choice_exped = "cte40_CNPJ" + rec.cte40_choice_autxml = "cte40_CNPJ" + rec.cte40_choice_transporta = "cte40_CNPJ" + rec.cte40_CNPJ = cnpj_cpf + rec.cte40_CPF = None + else: + rec.cte40_choice_tlocal = "cte40_CPF" + rec.cte40_choice_toma = "cte40_CPF" + rec.cte40_choice_emit = "cte40_CPF" + rec.cte40_choice_dest = "cte40_CPF" + rec.cte40_choice_rem = "cte40_CPF" + rec.cte40_choice_receb = "cte40_CPF" + rec.cte40_choice_exped = "cte40_CPF" + rec.cte40_choice_autxml = "cte40_CPF" + rec.cte40_choice_transporta = "cte40_CPF" + rec.cte40_CPF = cnpj_cpf + rec.cte40_CNPJ = None + else: + rec.cte40_choice_tlocal = False + rec.cte40_choice_toma = False + rec.cte40_choice_emit = False + rec.cte40_choice_dest = False + rec.cte40_choice_rem = False + rec.cte40_choice_receb = False + rec.cte40_choice_exped = False + rec.cte40_choice_autxml = False + rec.cte40_choice_transporta = False + rec.cte40_CNPJ = "" + rec.cte40_CPF = "" + + if rec.inscr_est: + rec.cte40_IE = punctuation_rm(rec.inscr_est) + else: + rec.cte40_IE = None + + rec.cte40_CEP = punctuation_rm(rec.zip) + rec.cte40_fone = punctuation_rm(rec.phone or "").replace(" ", "") + + def _inverse_cte40_CNPJ(self): + for rec in self: + if rec.cte40_CNPJ: + rec.is_company = True + rec.cte40_choice_tlocal = "cte40_CPF" + rec.cte40_choice_emit = "cte40_CPF" + if rec.country_id.code != "BR": + rec.cte40_choice_toma = "cte40_idEstrangeiro" + rec.cte40_choice_dest = "cte40_idEstrangeiro" + rec.cte40_choice_rem = "cte40_idEstrangeiro" + rec.cte40_choice_receb = "cte40_idEstrangeiro" + rec.cte40_choice_exped = "cte40_idEstrangeiro" + else: + rec.cte40_choice_toma = "cte40_CNPJ" + rec.cte40_choice_dest = "cte40_CNPJ" + rec.cte40_choice_rem = "cte40_CNPJ" + rec.cte40_choice_receb = "cte40_CNPJ" + rec.cte40_choice_exped = "cte40_CNPJ" + rec.cte40_choice_toma = "cte40_CPF" + rec.cte40_choice_dest = "cte40_CPF" + rec.cte40_choice_rem = "cte40_CPF" + rec.cte40_choice_receb = "cte40_CPF" + rec.cte40_choice_exped = "cte40_CPF" + rec.cte40_choice_autxml = "cte40_CPF" + rec.cte40_choice_transporta = "cte40_CPF" + rec.cnpj_cpf = cnpj_cpf.formata(str(rec.cte40_CNPJ)) + + def _inverse_cte40_CPF(self): + for rec in self: + if rec.cte40_CPF: + rec.is_company = False + rec.cte40_choice_tlocal = "cte40_CNPJ" + rec.cte40_choice_emit = "cte40_CNPJ" + if rec.country_id.code != "BR": + rec.cte40_choice_toma = "cte40_idEstrangeiro" + rec.cte40_choice_dest = "cte40_idEstrangeiro" + rec.cte40_choice_rem = "cte40_idEstrangeiro" + rec.cte40_choice_receb = "cte40_idEstrangeiro" + rec.cte40_choice_exped = "cte40_idEstrangeiro" + else: + rec.cte40_choice_toma = "cte40_CPF" + rec.cte40_choice_dest = "cte40_CPF" + rec.cte40_choice_rem = "cte40_CPF" + rec.cte40_choice_receb = "cte40_CPF" + rec.cte40_choice_exped = "cte40_CPF" + rec.cte40_choice_autxml = "cte40_CNPJ" + rec.cte40_choice_transporta = "cte40_CNPJ" + rec.cnpj_cpf = cnpj_cpf.formata(str(rec.cte40_CPF)) + + def _inverse_cte40_IE(self): + for rec in self: + if rec.cte40_IE: + rec.inscr_est = str(rec.cte40_IE) + + def _inverse_cte40_CEP(self): + for rec in self: + if rec.cte40_CEP: + country_code = rec.country_id.code if rec.country_id else "BR" + rec.zip = format_zipcode(rec.cte40_CEP, country_code) + + def _inverse_cte40_fone(self): + for rec in self: + if rec.cte40_fone: + rec.phone = rec.cte40_fone + + @api.model + def match_or_create_m2o(self, rec_dict, parent_dict, model=None): + if model is not None and model != self: + return False + + if parent_dict.get("cte40_CNPJ", False): + rec_dict["cnpj_cpf"] = parent_dict["cte40_CNPJ"] + + if rec_dict.get("cte40_CNPJ", False): + rec_dict["cnpj_cpf"] = rec_dict["cte40_CNPJ"] + + if rec_dict.get("cnpj_cpf", False): + domain_cnpj = [ + "|", + ("cnpj_cpf", "=", rec_dict["cnpj_cpf"]), + ("cnpj_cpf", "=", cnpj_cpf.formata(rec_dict["cnpj_cpf"])), + ] + match = self.search(domain_cnpj, limit=1) + if match: + return match.id + + vals = self._prepare_import_dict( + rec_dict, model=model, parent_dict=parent_dict, defaults_model=model + ) + if self._context.get("dry_run", False): + rec_id = self.new(vals).id + else: + rec_id = self.with_context(parent_dict=parent_dict).create(vals).id + return rec_id + + def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + # Se a NF-e é emitida em homologação altera o nome do destinatário + if ( + xsd_field == "cte40_xNome" + and class_obj._name + in ["cte.40.tcte_rem", "cte.40.tcte_dest", "cte.40.exped", "cte.40.receb"] + and self.env.context.get("tpAmb") == "2" + ): + return "CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL" + + if xsd_field in ("cte40_CNPJ", "cte40_CPF"): + # Caso o CNPJ/CPF esteja em branco e o parceiro tenha um parent_id + # É exportado o CNPJ/CPF do parent_id é importate para o endereço + # de entrega/retirada + if not self.cnpj_cpf and self.parent_id: + cnpj_cpf = punctuation_rm(self.parent_id.cnpj_cpf) + else: + cnpj_cpf = punctuation_rm(self.cnpj_cpf) + + if xsd_field == self.cte40_choice_tlocal: + return cnpj_cpf + + if self.country_id.code != "BR": + if xsd_field == "cte40_xBairro": + return "EX" + + if xsd_field == "cte40_xMun": + return "EXTERIOR" + + if xsd_field == "cte40_cMun": + return "9999999" + + if xsd_field == "cte40_UF": + return "EX" + + if xsd_field == "cte40_idEstrangeiro": + return self.vat or self.cnpj_cpf or self.rg or "EXTERIOR" + + return super()._export_field(xsd_field, class_obj, member_spec, export_value) + + ########################## + # NF-e tag: enderXXX + # Compute Methods + ########################## + + @api.depends( + "street_name", + "street_number", + "street2", + "district", + "city_id", + "state_id", + "country_id", + ) + def _compute_cte40_ender(self): + for rec in self: + rec.cte40_xLgr = rec.street_name + rec.cte40_nro = rec.street_number + rec.cte40_xCpl = rec.street2 + rec.cte40_xBairro = rec.district + rec.cte40_cMun = rec.city_id.ibge_code + rec.cte40_xMun = rec.city_id.name + rec.cte40_UF = rec.state_id.code + rec.cte40_cPais = rec.country_id.bc_code + rec.cte40_xPais = rec.country_id.name + + def _inverse_cte40_ender(self): + for rec in self: + if rec.cte40_cMun and rec.cte40_UF: + city_id = self.env["res.city"].search( + [("ibge_code", "=", rec.cte40_cMun)] + ) + if rec.cte40_cPais: + country_id = self.env["res.country"].search( + [("bc_code", "=", rec.cte40_cPais)] + ) + else: + country_id = self.env["res.country"].search([("code", "=", "BR")]) + + state_id = self.env["res.country.state"].search( + [("code", "=", rec.cte40_UF), ("country_id", "=", country_id.id)] + ) + + rec.street_name = rec.cte40_xLgr + rec.street_number = rec.cte40_nro + rec.street2 = rec.cte40_xCpl + rec.district = rec.cte40_xBairro + rec.city_id = city_id + rec.country_id = country_id + rec.state_id = state_id diff --git a/l10n_br_cte/models/rodoviario.py b/l10n_br_cte/models/rodoviario.py new file mode 100644 index 000000000000..dc1b84886958 --- /dev/null +++ b/l10n_br_cte/models/rodoviario.py @@ -0,0 +1,72 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + +from ..constants.modal import TUF + + +class Rodo(spec_models.StackedModel): + _name = "l10n_br_cte.modal.rodo" + _inherit = "cte.40.rodo" + _description = "Modal Rodoviario CTe" + + _cte40_stacking_mixin = "cte.40.rodo" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_rodoviario_v4_00" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_RNTRC = fields.Char(related="document_id.cte40_RNTRC") + + cte40_occ = fields.One2many(related="document_id.cte40_occ") + + +class Occ(spec_models.StackedModel): + _name = "l10n_br_cte.modal.rodo.occ" + _inherit = "cte.40.occ" + _description = "Ordens de Coleta associados" + + _cte40_stacking_mixin = "cte.40.occ" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_rodoviario_v4_00" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_serie = fields.Char(string="Série da OCC") + + cte40_nOcc = fields.Char(string="Número da Ordem de coleta") + + cte40_dEmi = fields.Date( + string="Data de emissão da ordem de coleta", + help="Data de emissão da ordem de coleta\nFormato AAAA-MM-DD", + ) + + cte40_CNPJ = fields.Char( + string="Número do CNPJ", + help="Número do CNPJ\nInformar os zeros não significativos.", + ) + + cte40_cInt = fields.Char( + string="Código interno de uso da transportadora", + help=( + "Código interno de uso da transportadora\nUso intermo das " + "transportadoras." + ), + ) + + cte40_IE = fields.Char(string="Inscrição Estadual") + + cte40_UF = fields.Selection( + TUF, + string="Sigla da UF", + help="Sigla da UF\nInformar EX para operações com o exterior.", + ) + + cte40_fone = fields.Char(string="Telefone") diff --git a/l10n_br_cte/readme/CONFIGURE.rst b/l10n_br_cte/readme/CONFIGURE.rst new file mode 100644 index 000000000000..63de5b32e262 --- /dev/null +++ b/l10n_br_cte/readme/CONFIGURE.rst @@ -0,0 +1 @@ +Para configurar este módulo, você precisa definir um certificado digital na empresa e também definir o processador edoc da empresa. diff --git a/l10n_br_cte/readme/CONTRIBUTORS.rst b/l10n_br_cte/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..b514bba4ef3c --- /dev/null +++ b/l10n_br_cte/readme/CONTRIBUTORS.rst @@ -0,0 +1,17 @@ + +* `KMEE `_: + + * Luis Felipe Mileo + * Ygor Carvalho + +* `ESCODOO `_: + + * Marcel Savegnago + +* `AKRETION `_: + + * Raphaël Valyi + +* `Engenere `_: + + * Antônio S. Pereira Neto diff --git a/l10n_br_cte/readme/DESCRIPTION.rst b/l10n_br_cte/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..f76ae240183e --- /dev/null +++ b/l10n_br_cte/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +Este módulo permite a emissão de CT-e (Conhecimento de Transporte). + +Mais especificamente ele: + * mapea os campos de CT-e do módulo ``l10n_br_cte_spec`` com os campos Odoo. + * usa a logica do módulo ``spec_driven_model`` para realizar esse mapeamento de forma dinâmica, em especial ele usa o sistema de modelos com várias camadas, ou ``StackedModel``, com os modelos ``l10n_br_fiscal.document`` e ``l10n_br_fiscal.document.related`` que tem varios niveis hierarquicos de elementos XML que estão sendo denormalizados dentro desses modelos Odoo  + * tem wizards para implementar a comunicação SOAP de CT-e com a SEFAZ (Autorização, Cancelamento, Encerramento...) diff --git a/l10n_br_cte/readme/ROADMAP.rst b/l10n_br_cte/readme/ROADMAP.rst new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_cte/readme/USAGE.rst b/l10n_br_cte/readme/USAGE.rst new file mode 100644 index 000000000000..2700e93d02b0 --- /dev/null +++ b/l10n_br_cte/readme/USAGE.rst @@ -0,0 +1,16 @@ +**Passo a Passo:** + +1. **Criar uma Fatura:** + - Defina o tipo de documento como **57 (CTe - Conhecimento de Transporte)**. + +2. **Configurar o Parceiro da Fatura:** + - Configure o parceiro responsável pelo pagamento do CTe e os parceiros como Rementente, Expedidor, Destinatário e Recebedor. + +3. **Adicionar uma Linha na Aba Produtos:** + - Adicione uma linha de fatura e selecione o produto Frete ou outro que esteja previamente configurado. + +4. **Acesse os detalhes fiscais da fatura e informe os demais dados necessário para emissão do CT-e:** + - Preencha os campos obrigatórios para emissão do CT-e. + +5. **Valide o CT-e, verifique os dados do XML e envie para a SEFAZ:** + - Após preencher todos os dados necessários, valide o CT-e e envie para a SEFAZ. diff --git a/l10n_br_cte/report/__init__.py b/l10n_br_cte/report/__init__.py new file mode 100644 index 000000000000..a248cf2162f5 --- /dev/null +++ b/l10n_br_cte/report/__init__.py @@ -0,0 +1 @@ +from . import ir_actions_report diff --git a/l10n_br_cte/report/dacte_report.xml b/l10n_br_cte/report/dacte_report.xml new file mode 100644 index 000000000000..2247f26abca9 --- /dev/null +++ b/l10n_br_cte/report/dacte_report.xml @@ -0,0 +1,12 @@ + + + DACTE + l10n_br_fiscal.document + qweb-pdf + main_template_dacte + main_template_dacte + '%s' % (object.document_key) + + report + + diff --git a/l10n_br_cte/report/ir_actions_report.py b/l10n_br_cte/report/ir_actions_report.py new file mode 100644 index 000000000000..255f45ada93c --- /dev/null +++ b/l10n_br_cte/report/ir_actions_report.py @@ -0,0 +1,81 @@ +# Copyright 2024 Engenere.one +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import base64 +from io import BytesIO + +from brazilfiscalreport.dacte import Dacte, DacteConfig, Margins + +from odoo import _, api, models +from odoo.exceptions import UserError + + +class IrActionsReport(models.Model): + _inherit = "ir.actions.report" + + def _render_qweb_html(self, res_ids, data=None): + if self.report_name == "main_template_dacte": + return + + return super()._render_qweb_html(res_ids, data=data) + + def _render_qweb_pdf(self, res_ids, data=None): + if self.report_name not in ["main_template_dacte"]: + return super()._render_qweb_pdf(res_ids, data=data) + + cte = self.env["l10n_br_fiscal.document"].search([("id", "in", res_ids)]) + + return self._render_dacte(cte) + + def _render_dacte(self, cte): + if cte.document_type != "57": + raise UserError(_("You can only print a DACTE of a CTe(57).")) + + cte_xml = False + if cte.authorization_file_id: + cte_xml = base64.b64decode(cte.authorization_file_id.datas) + elif cte.send_file_id: + cte_xml = base64.b64decode(cte.send_file_id.datas) + + if not cte_xml: + raise UserError(_("No xml file was found.")) + + return self.render_dacte_brazilfiscalreport(cte, cte_xml) + + def render_dacte_brazilfiscalreport(self, cte, cte_xml): + logo = False + if cte.issuer == "company" and cte.company_id.logo: + logo = base64.b64decode(cte.company_id.logo) + elif cte.issuer != "company" and cte.company_id.logo_web: + logo = base64.b64decode(cte.company_id.logo_web) + + if logo: + tmpLogo = BytesIO() + tmpLogo.write(logo) + tmpLogo.seek(0) + else: + tmpLogo = False + config = self._get_dacte_config(tmpLogo, cte.company_id) + + dacte = Dacte(xml=cte_xml, config=config) + + tmpDacte = BytesIO() + dacte.output(tmpDacte) + dacte_file = tmpDacte.getvalue() + tmpDacte.close() + + return dacte_file, "pdf" + + @api.model + def _get_dacte_config(self, tmpLogo, company): + margins = Margins( + top=company.dacte_margin_top, + right=company.dacte_margin_right, + bottom=company.dacte_margin_bottom, + left=company.dacte_margin_left, + ) + dacte_config = { + "logo": tmpLogo, + "margins": margins, + } + return DacteConfig(**dacte_config) diff --git a/l10n_br_cte/security/ir.model.access.csv b/l10n_br_cte/security/ir.model.access.csv new file mode 100644 index 000000000000..1ee6536b59b4 --- /dev/null +++ b/l10n_br_cte/security/ir.model.access.csv @@ -0,0 +1,16 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +l10n_br_cte_modal_aereo_user,l10n_br_cte_modal_aereo_user,model_l10n_br_cte_modal_aereo,base.group_user,1,1,1,1 +l10n_br_cte_modal_aereo_peri_user,l10n_br_cte_modal_aereo_peri_user,model_l10n_br_cte_modal_aereo_peri,base.group_user,1,1,1,1 + +l10n_br_cte_modal_rodoviario_user,l10n_br_cte_modal_rodoviario_user,model_l10n_br_cte_modal_rodo,base.group_user,1,1,1,1 +l10n_br_cte_modal_rodoviario_occ_user,l10n_br_cte_modal_rodoviario_occ_user,model_l10n_br_cte_modal_rodo_occ,base.group_user,1,1,1,1 + +l10n_br_cte_modal_ferrov_user,l10n_br_cte_modal_ferrov_user,model_l10n_br_cte_modal_ferrov,base.group_user,1,1,1,1 + +l10n_br_cte_modal_aquaviario_user,l10n_br_cte_modal_aquav_user,model_l10n_br_cte_modal_aquav,base.group_user,1,1,1,1 +l10n_br_cte_modal_aquaviario_balsa_user,l10n_br_cte_modal_aquav_balsa_user,model_l10n_br_cte_modal_aquav_balsa,base.group_user,1,1,1,1 + +l10n_br_cte_modal_duto_user,l10n_br_cte_modal_duto_user,model_l10n_br_cte_modal_duto,base.group_user,1,1,1,1 + +l10n_br_cte_cargo_quantity_infos_user,l10n_br_cte_cargo_quantity_infos_user,model_l10n_br_cte_cargo_quantity_infos,base.group_user,1,1,1,1 +l10n_br_cte_transported_vehicles_user,l10n_br_cte_transported_vehicles_user,model_l10n_br_cte_transported_vehicles,base.group_user,1,1,1,1 diff --git a/l10n_br_cte/static/description/icon.png b/l10n_br_cte/static/description/icon.png new file mode 100644 index 000000000000..4515bffd318d Binary files /dev/null and b/l10n_br_cte/static/description/icon.png differ diff --git a/l10n_br_cte/static/description/index.html b/l10n_br_cte/static/description/index.html new file mode 100644 index 000000000000..7ce244bcb41a --- /dev/null +++ b/l10n_br_cte/static/description/index.html @@ -0,0 +1,479 @@ + + + + + +CT-e + + + +
+

CT-e

+ + +

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

+

Este módulo permite a emissão de CT-e (Conhecimento de Transporte).

+
+
Mais especificamente ele:
+
    +
  • mapea os campos de CT-e do módulo l10n_br_cte_spec com os campos Odoo.
  • +
  • usa a logica do módulo spec_driven_model para realizar esse mapeamento de forma dinâmica, em especial ele usa o sistema de modelos com várias camadas, ou StackedModel, com os modelos l10n_br_fiscal.document e l10n_br_fiscal.document.related que tem varios niveis hierarquicos de elementos XML que estão sendo denormalizados dentro desses modelos Odoo
  • +
  • tem wizards para implementar a comunicação SOAP de CT-e com a SEFAZ (Autorização, Cancelamento, Encerramento…)
  • +
+
+
+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Configuration

+

Para configurar este módulo, você precisa definir um certificado digital na empresa e também definir o processador edoc da empresa.

+
+
+

Usage

+

Passo a Passo:

+
    +
  1. Criar uma Fatura: +- Defina o tipo de documento como 57 (CTe - Conhecimento de Transporte).
  2. +
  3. Configurar o Parceiro da Fatura: +- Configure o parceiro responsável pelo pagamento do CTe e os parceiros como Rementente, Expedidor, Destinatário e Recebedor.
  4. +
  5. Adicionar uma Linha na Aba Produtos: +- Adicione uma linha de fatura e selecione o produto Frete ou outro que esteja previamente configurado.
  6. +
  7. Acesse os detalhes fiscais da fatura e informe os demais dados necessário para emissão do CT-e: +- Preencha os campos obrigatórios para emissão do CT-e.
  8. +
  9. Valide o CT-e, verifique os dados do XML e envie para a SEFAZ: +- Após preencher todos os dados necessários, valide o CT-e e envie para a SEFAZ.
  10. +
+
+
+

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

+
    +
  • KMEE
  • +
  • Escodoo
  • +
+
+
+

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:

+

mileo marcelsavegnago

+

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_cte/tests/__init__.py b/l10n_br_cte/tests/__init__.py new file mode 100644 index 000000000000..a9d1058fa8bb --- /dev/null +++ b/l10n_br_cte/tests/__init__.py @@ -0,0 +1,9 @@ +from . import test_cte_serialize +from . import test_cte_serialize_lc +from . import test_cte_serialize_sn +from . import test_cte_import +from . import test_cte_structure +from . import test_cte_res_partner +from . import test_cte_document +from . import test_res_partner +from . import test_cte_dacte diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240759594315000157570010000057311040445890.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240759594315000157570010000057311040445890.xml new file mode 100644 index 000000000000..cc05dae58e7b --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240759594315000157570010000057311040445890.xml @@ -0,0 +1,200 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 573 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 0 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3501152 + Alumínio + SP + 01 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 59594315000157 + 755338250133 + TESTE - Simples Nacional + TESTE - Simples Nacional + + Rua Paulo Dias + 586 + Vila Santa Luzia + 3501152 + Alumínio + 18125000 + SP + 2130109965 + + 1 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 90 + 1 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + 12345678 + + 01 + 01 + + XYZ + + + + 02 + 02 + + ABC + + + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057311040645894.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057311040645894.xml new file mode 100644 index 000000000000..2c05057add52 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057311040645894.xml @@ -0,0 +1,202 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 573 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 4 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 01 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + 12345678 + + 01 + 01 + + XYZ + + + + 02 + 02 + + ABC + + + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057411040645890.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057411040645890.xml new file mode 100644 index 000000000000..be8747c9cfb0 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057411040645890.xml @@ -0,0 +1,214 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 574 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 0 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 02 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + TEST123 + OCA56789 + 2024-11-22 + + Dimensão Padrão + + + TEST_CL + TAR123 + 0.00 + + + 1234 + 15 + + 300.0000 + 1 + + + + 5678 + 20 + + 500.0000 + 3 + + + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057511040645897.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057511040645897.xml new file mode 100644 index 000000000000..c392518fec8e --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057511040645897.xml @@ -0,0 +1,200 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 575 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 7 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 03 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + 47.00 + 1200.00 + Navio Mercante 123 + + Balsa A + + + Balsa B + + Viagem001 + S + IRIN12345 + 0 + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057611040645893.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057611040645893.xml new file mode 100644 index 000000000000..21c97f44ed96 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057611040645893.xml @@ -0,0 +1,189 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 576 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 3 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 04 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + 1 + Fluxo Norte-Sul + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057711040645890.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057711040645890.xml new file mode 100644 index 000000000000..dd1f1f13c7cf --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057711040645890.xml @@ -0,0 +1,190 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 577 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 0 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 05 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + 1500.000000 + 2024-01-01 + 2024-12-31 + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe43120178408960000182570010000000041000000047.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe43120178408960000182570010000000041000000047.xml new file mode 100644 index 000000000000..4e9f11593b81 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe43120178408960000182570010000000041000000047.xml @@ -0,0 +1,162 @@ + + + + + 3 + 00000572 + 6353 + SERV. TRANSPORTE + 57 + 1 + 572 + 2012-01-06T17:25:56-02:00 + 1 + 1 + 7 + 2 + 0 + 0 + 104 + 4213609 + PORTO UNIAO + SC + 01 + 0 + 4213609 + PORTO UNIAO + SC + 4128203 + UNIAO DA VITORIA + PR + 1 + 9 + + 0 + + + + MASTER + NOTA FISCAL DE PRODUTOR RURAL N. 253-254-255 + + + 78408960000182 + 251079554 + KERBER E CIA. LTDA. + PEDREIRA + + ESTRADA VELHA DE PALMAS + S/N + CAIXA POSTAL 268 + RIO DA AREIA + 4213609 + PORTO UNIAO + 89400000 + SC + 4235224933 + + + + 78408960000182 + 251079554 + KERBER E CIA. LTDA. + PEDREIRA + 4235224933 + + ESTRADA VELHA DE PALMAS + S/N + CAIXA POSTAL 268 + RIO DA AREIA + 4213609 + PORTO UNIAO + 89400000 + SC + 1058 + Brasil + + pedreira@kerberecia.com.br + + + 78408960000182 + 251079554 + KERBER E CIA. LTDA. + 4235224933 + + ESTRADA VELHA DE PALMAS + S/N + CAIXA POSTAL 268 + RIO DA AREIA + 4213609 + PORTO UNIAO + 89400000 + SC + 1058 + Brasil + + + + 81639791000104 + 3010264714 + HOBI E CIA LTDA. - MATRIZ + 4235211922 + + AUTO VIA JOAO REOLON + 02105 + CENTRO + 4128203 + UNIAO DA VITORIA + 84600000 + PR + 1058 + Brasil + + + + 81639791000104 + 3010264714 + HOBI E CIA LTDA. - MATRIZ + 4235211922 + + AUTO VIA JOAO REOLON + 02105 + CENTRO + 4128203 + UNIAO DA VITORIA + 84600000 + PR + 1058 + Brasil + + + + 2300.00 + 2300.00 + + + + + 00 + 2300.00 + 12.00 + 276.00 + + + + + + 174.38 + Pedra Brita + Pedra Brita a Granel + + 00 + TON + 9.3000 + + + + + 04150238 + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160724686092000173570010000000031000000024.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160724686092000173570010000000031000000024.xml new file mode 100644 index 000000000000..efaf7c991dba --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160724686092000173570010000000031000000024.xml @@ -0,0 +1,130 @@ + + + + + 51 + 570005757 + 5353 + PREST. DE SERV. TRANSPORTE A ESTAB. COMERCIAL.. + 57 + 1 + 571 + 2016-07-07T17:58:40-02:00 + 1 + 1 + 0 + 2 + 0 + 0 + 2.0.1 + 5108402 + VARZEA GRANDE + MT + 01 + 0 + 5101407 + ARIPUANA + MT + 5108402 + VARZEA GRANDE + MT + 1 + 9 + + 0 + + + + MASTER + + NOTA FISCAL DE PRODUTOR RURAL N. 253-254-255 + + + 24686092000173 + 136268870 + P J TOSTA TRANSPORTES ME + + RUA RENATO JOSE DOS SANTOS + 10 + COHAB PRIMAVERA + 5108402 + VARZEA GRANDE + 78132712 + MT + 6599893021 + + + + 72304553915 + ISENTO + ROGERIO MARCIO TOLARDO + 6635651335 + + GLEBA GUARIBA VI LOTE RURAL 38 + SN + ZONA RURAL + 5101407 + ARIPUANA + 78325000 + MT + 1058 + BRASIL + + + + 03851469000122 + 131952927 + FRICAL FRIGORIFICO LTDA EPP + 6536342236 + + ESTRADA SOUZA LIMA + SN + SOUZA LIMA + 5108402 + VARZEA GRANDE + 78110000 + MT + 1058 + BRASIL + + + + 4000.00 + 4000.00 + + + + + 90 + 1 + + + + + + 79400.00 + GADO + GADO + + 01 + KILO + 50.0000 + + + + + 99 + NOTA FISCAL MANUAL + 123456 + 2016-07-05 + 79400.00 + + + + + 05277204 + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160824686092000173570010000000031000000024.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160824686092000173570010000000031000000024.xml new file mode 100644 index 000000000000..b7af3a4f2055 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160824686092000173570010000000031000000024.xml @@ -0,0 +1,130 @@ + + + + + 51 + 570005757 + 5353 + PREST. DE SERV. TRANSPORTE A ESTAB. COMERCIAL.. + 57 + 1 + 575 + 2016-07-07T17:58:40-02:00 + 1 + 1 + 0 + 2 + 0 + 0 + 2.0.1 + 5108402 + VARZEA GRANDE + MT + 01 + 0 + 5101407 + ARIPUANA + MT + 5108402 + VARZEA GRANDE + MT + 1 + 9 + + 0 + + + + MASTER + + NOTA FISCAL DE PRODUTOR RURAL N. 253-254-255 + + + 24686092000173 + 136268870 + P J TOSTA TRANSPORTES ME + + RUA RENATO JOSE DOS SANTOS + 10 + COHAB PRIMAVERA + 5108402 + VARZEA GRANDE + 78132712 + MT + 6599893021 + + + + 72304553915 + ISENTO + ROGERIO MARCIO TOLARDO + 6635651335 + + GLEBA GUARIBA VI LOTE RURAL 38 + SN + ZONA RURAL + 5101407 + ARIPUANA + 78325000 + MT + 1058 + BRASIL + + + + 03851469000122 + 131952927 + FRICAL FRIGORIFICO LTDA EPP + 6536342236 + + ESTRADA SOUZA LIMA + SN + SOUZA LIMA + 5108402 + VARZEA GRANDE + 78110000 + MT + 1058 + BRASIL + + + + 4000.00 + 4000.00 + + + + + 90 + 1 + + + + + + 79400.00 + GADO + GADO + + 01 + KILO + 50.0000 + + + + + 99 + NOTA FISCAL MANUAL + 123456 + 2016-07-05 + 79400.00 + + + + + 05277204 + + + + + diff --git a/l10n_br_cte/tests/test_cte_dacte.py b/l10n_br_cte/tests/test_cte_dacte.py new file mode 100644 index 000000000000..386717d78ad5 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_dacte.py @@ -0,0 +1,39 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.exceptions import UserError +from odoo.tests.common import TransactionCase + + +class TestDacteGeneration(TransactionCase): + def setUp(self): + super().setUp() + + def test_generate_dacte_brazil_fiscal_report(self): + cte = self.env.ref("l10n_br_cte.demo_cte_lc_modal_rodoviario") + cte.action_document_confirm() + cte.view_pdf() + + self.assertTrue(cte.file_report_id) + + def test_generate_dacte_document_type_error(self): + dacte_report = self.env["ir.actions.report"].search( + [("report_name", "=", "main_template_dacte")] + ) + cte = self.env.ref("l10n_br_cte.demo_cte_lc_modal_rodoviario") + cte.document_type_id = self.env.ref("l10n_br_fiscal.document_01") + cte.action_document_confirm() + with self.assertRaises(UserError) as captured_exception: + dacte_report._render_qweb_pdf([cte.id]) + self.assertEqual( + captured_exception.exception.args[0], + "You can only print a DACTE of a CTe(57).", + ) + + def test_generate_dacte_brazil_fiscal_report_partner(self): + cte = self.env.ref("l10n_br_cte.demo_cte_lc_modal_rodoviario") + cte.action_document_confirm() + cte.issuer = "partner" + cte.view_pdf() + + self.assertTrue(cte.file_report_id) diff --git a/l10n_br_cte/tests/test_cte_document.py b/l10n_br_cte/tests/test_cte_document.py new file mode 100644 index 000000000000..5af40c9a37b7 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_document.py @@ -0,0 +1,81 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from datetime import datetime + +from odoo.tests import SavepointCase + + +class CTeDocumentTest(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + FiscalDocument = cls.env["l10n_br_fiscal.document"] + + cls.acre_state = cls.env.ref("base.state_br_ac") + cls.cte_document_type_id = cls.env.ref("l10n_br_fiscal.document_57") + cls.sn_company_id = cls.env.ref("l10n_br_base.empresa_simples_nacional") + cls.sn_company_id.processador_edoc = "oca" + cls.cte_id = FiscalDocument.create( + { + "document_type_id": cls.cte_document_type_id.id, + "company_id": cls.sn_company_id.id, + "document_number": "70000", + "document_serie": "30", + "document_date": datetime.now(), + } + ) + + # TODO: Tratar + # def test_cte_compute_fields(self): + # self.cte_id.fiscal_additional_data = "TEST FISCAL ADDITIONAL DATA" + # self.cte_id.customer_additional_data = "TEST CUSTOMER ADDITIONAL DATA" + + # self.assertTrue(self.cte_id.cte40_infAdFisco) + # self.assertTrue(self.cte_id.cte40_infCpl) + + # TODO: Tratar + # def test_cte_inverse_fields(self): + # self.cte_id.cte40_UFIni = self.acre_state.code + # self.cte_id.cte40_UFFim = self.acre_state.code + # self.assertEqual(self.cte_id.cte_initial_state_id, self.acre_state) + # self.assertEqual(self.cte_id.cte_final_state_id, self.acre_state) + + # self.cte_id.cte40_UF = self.acre_state.ibge_code + # self.assertEqual(self.cte_id.company_id.partner_id.state_id, self.acre_state) + + # self.cte_id.cte40_infMunCarrega = [ + # ( + # 0, + # 0, + # { + # "cte40_cMunCarrega": "1200013", + # "cte40_xMunCarrega": "Acrelândia", + # }, + # ) + # ] + # self.assertIn( + # self.env.ref("l10n_br_base.city_1200013"), + # self.cte_id.cte_loading_city_ids, + # ) + + # def test_cte_processor(self): + # processor = self.cte_id._edoc_processor() + # self.assertTrue(isinstance(processor, CTeAdapter)) + + # self.cte_id.document_type_id = False + # processor = self.cte_id._edoc_processor() + # self.assertFalse(isinstance(processor, CTeAdapter)) + + # self.cte_id.document_type_id = self.cte_document_type_id + + # self.cte_id.company_id.certificate_nfe_id = False + # with self.assertRaises(UserError): + # processor = self.cte_id._edoc_processor() + + def test_generate_key(self): + self.cte_id._generate_key() + self.assertTrue(self.cte_id.document_key) + self.assertTrue(self.cte_id.key_random_code) + self.assertTrue(self.cte_id.key_check_digit) diff --git a/l10n_br_cte/tests/test_cte_import.py b/l10n_br_cte/tests/test_cte_import.py new file mode 100644 index 000000000000..9d2613ac5aea --- /dev/null +++ b/l10n_br_cte/tests/test_cte_import.py @@ -0,0 +1,67 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging + +import pkg_resources +from nfelib.cte.bindings.v4_0.cte_v4_00 import Tcte + +from odoo.models import NewId +from odoo.tests import SavepointCase + +from odoo.addons import l10n_br_cte + +_logger = logging.getLogger(__name__) + + +class CTeImportTest(SavepointCase): + def test_import_in_cte_dry_run(self): + res_items = ( + "tests", + "cte", + "v4_00", + "leiauteCTe", + "CTe51160824686092000173570010000000031000000024.xml", + ) + + resource_path = "/".join(res_items) + cte_stream = pkg_resources.resource_stream(l10n_br_cte.__name__, resource_path) + binding = Tcte.from_xml(cte_stream.read().decode()) + cte = ( + self.env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type="in") + .build_from_binding("cte", "40", binding.infCte, dry_run=True) + ) + assert isinstance(cte.id, NewId) + self._check_cte(cte) + + def test_import_in_cte(self): + res_items = ( + "tests", + "cte", + "v4_00", + "leiauteCTe", + "CTe51160724686092000173570010000000031000000024.xml", + ) + + resource_path = "/".join(res_items) + cte_stream = pkg_resources.resource_stream(l10n_br_cte.__name__, resource_path) + binding = Tcte.from_xml(cte_stream.read().decode()) + cte = ( + self.env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type="in") + .build_from_binding("cte", "40", binding.infCte, dry_run=False) + ) + + assert isinstance(cte.id, int) + self._check_cte(cte) + + def _check_cte(self, cte): + self.assertEqual(type(cte)._name, "l10n_br_fiscal.document") + + self.assertEqual(cte.cte40_UFIni, "MT") + self.assertEqual(cte.cte40_UFFim, "MT") + + self.assertEqual(cte.cte40_verProc, "2.0.1") + + def test_import_out_cte(self): + "(can be useful after an ERP migration)" diff --git a/l10n_br_cte/tests/test_cte_res_partner.py b/l10n_br_cte/tests/test_cte_res_partner.py new file mode 100644 index 000000000000..cda6d4cf43b0 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_res_partner.py @@ -0,0 +1,64 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from erpbrasil.base.fiscal import cnpj_cpf +from erpbrasil.base.misc import format_zipcode + +from odoo.tests import TransactionCase + + +class TestCTeResPartner(TransactionCase): + def setUp(self): + super().setUp() + + self.partner_id = self.env.ref("l10n_br_base.res_partner_kmee") + + def test_compute_fields(self): + self.partner_id.country_id = self.env.ref("base.us") + + self.assertEqual(self.partner_id.cte40_choice_toma, "cte40_idEstrangeiro") + self.assertEqual(self.partner_id.cte40_cPais, self.env.ref("base.us").bc_code) + + def test_inverse_fields(self): + # self.partner_id.cte40_idEstrangeiro = "999999999999" + # self.assertEqual(self.partner_id.vat, self.partner_id.cte40_idEstrangeiro) + + self.partner_id.cte40_CNPJ = "97414612000162" + self.assertEqual( + self.partner_id.cnpj_cpf, cnpj_cpf.formata(self.partner_id.cte40_CNPJ) + ) + + self.partner_id.cte40_CPF = "48737433032" + self.assertEqual( + self.partner_id.cnpj_cpf, cnpj_cpf.formata(self.partner_id.cte40_CPF) + ) + + self.partner_id.cte40_IE = "630514648079" + self.assertEqual(self.partner_id.inscr_est, self.partner_id.cte40_IE) + + self.partner_id.cte40_CEP = "04324240" + self.assertEqual(self.partner_id.zip, format_zipcode(self.partner_id.cte40_CEP)) + + self.partner_id.cte40_fone = "(99) 9999-9999" + self.assertEqual(self.partner_id.phone, self.partner_id.cte40_fone) + + self.partner_id.cte40_cPais = "1058" + self.partner_id.cte40_UF = "SP" + self.partner_id.cte40_cMun = "3550308" + self.assertEqual( + self.partner_id.country_id.bc_code, self.partner_id.cte40_cPais + ) + self.assertEqual(self.partner_id.state_id.code, self.partner_id.cte40_UF) + self.assertEqual(self.partner_id.city_id.ibge_code, self.partner_id.cte40_cMun) + + self.partner_id.cte40_xLgr = "TESTE" + self.assertEqual(self.partner_id.street_name, self.partner_id.cte40_xLgr) + + self.partner_id.cte40_nro = "999" + self.assertEqual(self.partner_id.street_number, self.partner_id.cte40_nro) + + self.partner_id.cte40_xCpl = "TESTE" + self.assertEqual(self.partner_id.street2, self.partner_id.cte40_xCpl) + + self.partner_id.cte40_xBairro = "TESTE" + self.assertEqual(self.partner_id.district, self.partner_id.cte40_xBairro) diff --git a/l10n_br_cte/tests/test_cte_serialize.py b/l10n_br_cte/tests/test_cte_serialize.py new file mode 100644 index 000000000000..fb424f849c65 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_serialize.py @@ -0,0 +1,213 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +import os +from datetime import datetime + +from xmldiff import main + +from odoo.tests.common import TransactionCase +from odoo.tools import config + +from odoo.addons import l10n_br_cte + +_logger = logging.getLogger(__name__) + + +class TestCTeSerialize(TransactionCase): + def setUp(self, cte_list): + super().setUp() + self.cte_list = cte_list + for cte_data in self.cte_list: + cte = self.env.ref(cte_data["record_ref"]) + cte_data["cte"] = cte + self.prepare_test_cte(cte) + + def prepare_test_cte(self, cte): + """ + Performs actions necessary to prepare an CTe of the demo data to + perform the tests + """ + if cte.state != "em_digitacao": # 2nd test run + cte.action_document_back2draft() + + cte.fiscal_line_ids.name = "Frete" + cte.fiscal_line_ids._onchange_fiscal_operation_line_id() + cte.fiscal_line_ids.cfop_id = cte.env.ref("l10n_br_fiscal.cfop_5352") + cte._compute_amount() + + cte.action_document_confirm() + cte.document_date = datetime.strptime( + "2020-01-01T11:00:00", "%Y-%m-%dT%H:%M:%S" + ) + cte.cte40_cCT = "57000111" + + if cte.cte_modal == "01": + self.prepare_modal_rodoviario_data(cte) + elif cte.cte_modal == "02": + self.prepare_modal_aereo_data(cte) + elif cte.cte_modal == "03": + self.prepare_modal_aquaviario_data(cte) + elif cte.cte_modal == "04": + self.prepare_modal_ferroviario_data(cte) + elif cte.cte_modal == "05": + self.prepare_modal_dutoviario_data(cte) + + cte._document_export() + + def prepare_modal_rodoviario_data(self, cte): + cte.cte40_RNTRC = "12345678" + cte.cte40_occ = [ + ( + 0, + 0, + { + "cte40_serie": "01", + "cte40_nOcc": "01", + "cte40_cInt": "XYZ", + }, + ), + ( + 0, + 0, + { + "cte40_serie": "02", + "cte40_nOcc": "02", + "cte40_cInt": "ABC", + }, + ), + ] + + def prepare_modal_aereo_data(self, cte): + # Dados gerais do modal aéreo + cte.cte40_nMinu = "TEST123" # Número do Minuta + cte.cte40_nOCA = "OCA56789" # Número do OCA + cte.cte40_dPrevAereo = datetime.strptime( + "2024-11-22", "%Y-%m-%d" + ).date() # Data prevista de entrega + cte.cte40_CL = "TEST_CL" # Código de Localidade + cte.cte40_cTar = "TAR123" # Código da Tarifa + cte.cte40_vTar = 2500.00 # Valor da Tarifa + cte.cte40_xDime = "Dimensão Padrão" # Dimensões do volume + + # Lista de produtos perigosos + cte.cte40_peri = [ + ( + 0, + 0, + { + "cte40_nONU": "1234", # Número ONU do produto perigoso + "cte40_qTotEmb": "15", # Quantidade total de volumes embarcados + "cte40_qTotProd": 300.0, # Quantidade total do produto perigoso + "cte40_uniAP": "1", # Unidade de Medida do Produto + }, + ), + ( + 0, + 0, + { + "cte40_nONU": "5678", + "cte40_qTotEmb": "20", + "cte40_qTotProd": 500.0, + "cte40_uniAP": "3", + }, + ), + ] + + def prepare_modal_aquaviario_data(self, cte): + # Dados gerais do modal aquaviário + cte.cte40_vAFRMM = ( + 1200.00 + ) # Valor do Adicional de Frete para Renovação da Marinha Mercante + # cte.cte40_vPrest = 3500.00 # Valor total do frete aquaviário + cte.cte40_xNavio = "Navio Mercante 123" # Nome do navio + cte.cte40_nViag = "Viagem001" # Número da viagem + cte.cte40_direc = "S" # Direção de navegação: 1 = Cabotagem, 2 = Longo curso + cte.cte40_irin = "IRIN12345" # Inscrição do Registro Internacional de Navios + cte.cte40_tpNav = "0" # Tipo de navegação: 01 = Interior, 02 = Cabotagem, etc. + + # Informações das balsas transportadas + cte.cte40_balsa = [ + ( + 0, + 0, + { + "cte40_xBalsa": "Balsa A", # Identificador da primeira balsa + }, + ), + ( + 0, + 0, + { + "cte40_xBalsa": "Balsa B", # Identificador da segunda balsa + }, + ), + ] + + def prepare_modal_dutoviario_data(self, cte): + # Dados gerais do modal dutoviário + cte.cte40_dIni = "2024-01-01" # Data de início da operação dutoviária + cte.cte40_dFim = "2024-12-31" # Data de término da operação dutoviária + cte.cte40_vTar = 1500.00 # Valor da tarifa aplicada no transporte + + def prepare_modal_ferroviario_data(self, cte): + # Dados gerais do modal ferroviário + cte.cte40_tpTraf = "1" # Tipo de Tráfego: 1 = Nacional, 2 = Internacional + cte.cte40_fluxo = "Fluxo Norte-Sul" # Fluxo de transporte + cte.cte40_vFrete = 5000.00 # Valor do frete ferroviário + cte.cte40_chCTeFerroOrigem = ( + "CTE123456789" + ) # Chave do CTe Ferroviário de origem + cte.cte40_respFat = ( + "1" + ) # Responsável pelo Faturamento: 1 = Emitente, 2 = Receptor + cte.cte40_ferrEmi = ( + "1" + ) # Emissor do documento: 1 = Ferrovia Emitente, 2 = Outro + # cte.cte40_ferroEnv = [ + # ( + # 0, + # 0, + # { + # "cte40_CNPJ": "12345678000199", # CNPJ da ferrovia envolvida + # "cte40_cInt": "FERRO001", # Código interno da ferrovia + # "cte40_IE": "ISENTO", # Inscrição Estadual + # "cte40_xNome": "Ferrovia Teste LTDA", # Nome ou razão social + # "cte40_enderFerro": ( + # 0, + # 0, + # { + # "cte40_xLgr": "Rua da Ferrovia", + # "cte40_nro": "123", + # "cte40_xCpl": "Prédio 2", + # "cte40_xBairro": "Centro", + # "cte40_cMun": "1234567", # Código do município IBGE + # "cte40_xMun": "Cidade Ferrovia", + # "cte40_CEP": "12345000", + # "cte40_UF": "SP", # Unidade Federativa + # }, + # ), + # }, + # ), + # ] + + def serialize_xml(self, cte_data): + cte = cte_data["cte"] + xml_path = os.path.join( + l10n_br_cte.__path__[0], + "tests", + "cte", + "v4_00", + "leiauteCTe", + cte_data["xml_file"], + ) + output = os.path.join( + config["data_dir"], + "filestore", + self.cr.dbname, + cte.send_file_id.store_fname, + ) + _logger.info(f"XML file saved at {output}") + diff = main.diff_files(output, xml_path) + return diff diff --git a/l10n_br_cte/tests/test_cte_serialize_lc.py b/l10n_br_cte/tests/test_cte_serialize_lc.py new file mode 100644 index 000000000000..7c43edc2ecd9 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_serialize_lc.py @@ -0,0 +1,41 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging + +from .test_cte_serialize import TestCTeSerialize + +_logger = logging.getLogger(__name__) + + +class TestCTeExportLC(TestCTeSerialize): + def setUp(self): + cte_list = [ + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_rodoviario", + "xml_file": "CTe35240781583054000129570010000057311040645894.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_aereo", + "xml_file": "CTe35240781583054000129570010000057411040645890.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_aquaviario", + "xml_file": "CTe35240781583054000129570010000057511040645897.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_ferroviario", + "xml_file": "CTe35240781583054000129570010000057611040645893.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_dutoviario", + "xml_file": "CTe35240781583054000129570010000057711040645890.xml", + }, + ] + + super().setUp(cte_list) + + def test_serialize_xml(self): + for cte_data in self.cte_list: + diff = self.serialize_xml(cte_data) + _logger.info("Diff with expected XML (if any): %s" % (diff,)) + assert len(diff) == 0 diff --git a/l10n_br_cte/tests/test_cte_serialize_sn.py b/l10n_br_cte/tests/test_cte_serialize_sn.py new file mode 100644 index 000000000000..a65ff2d3db34 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_serialize_sn.py @@ -0,0 +1,25 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging + +from .test_cte_serialize import TestCTeSerialize + +_logger = logging.getLogger(__name__) + + +class TestCTeExportSN(TestCTeSerialize): + def setUp(self): + cte_list = [ + { + "record_ref": "l10n_br_cte.demo_cte_sn_modal_rodoviario", + "xml_file": "CTe35240759594315000157570010000057311040445890.xml", + }, + ] + + super().setUp(cte_list) + + def test_serialize_xml(self): + for cte_data in self.cte_list: + diff = self.serialize_xml(cte_data) + _logger.info("Diff with expected XML (if any): %s" % (diff,)) + assert len(diff) == 0 diff --git a/l10n_br_cte/tests/test_cte_structure.py b/l10n_br_cte/tests/test_cte_structure.py new file mode 100644 index 000000000000..c08007807676 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_structure.py @@ -0,0 +1,164 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from io import StringIO + +from odoo.tests import SavepointCase + +from odoo.addons.spec_driven_model.models.spec_models import SpecModel + + +class CTeStructure(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + @classmethod + def get_stacked_tree(cls, klass): + """ + # > means the content of the m2o is stacked in the parent + # - means standard m2o. Eventually followd by the mapped Odoo model + # ≡ means o2m. Eventually followd by the mapped Odoo model + """ + spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + spec_prefix = "cte40" + stacking_settings = { + "odoo_module": getattr(klass, f"_{spec_prefix}_odoo_module"), + "stacking_mixin": getattr(klass, f"_{spec_prefix}_stacking_mixin"), + "stacking_points": getattr(klass, f"_{spec_prefix}_stacking_points"), + "stacking_skip_paths": getattr( + klass, f"_{spec_prefix}_stacking_skip_paths", [] + ), + "stacking_force_paths": getattr( + klass, f"_{spec_prefix}_stacking_force_paths", [] + ), + } + node = SpecModel._odoo_name_to_class( + stacking_settings["stacking_mixin"], spec_module + ) + tree = StringIO() + visited = set() + for kind, n, path, field_path, child_concrete in klass._visit_stack( + cls.env, node, stacking_settings + ): + visited.add(n) + path_items = path.split(".") + indent = " ".join(["" for i in range(0, len(path_items))]) + if kind == "stacked": + line = "\n%s> <%s>" % (indent, path.split(".")[-1]) + elif kind == "one2many": + line = "\n%s \u2261 <%s> %s" % ( + indent, + field_path, + child_concrete or "", + ) + elif kind == "many2one": + line = "\n%s - <%s> %s" % (indent, field_path, child_concrete or "") + tree.write(line.rstrip()) + tree_txt = tree.getvalue() + return tree_txt, visited + + def test_inherited_fields(self): + assert "cte40_CNPJ" in self.env["res.company"]._fields.keys() + + def test_concrete_spec(self): + # this ensure basic SQL is set up + self.assertEqual( + len( + self.env["cte.40.tcte_infnfe"].search( + [("cte40_chave", "=", "NO_RECORD")] + ) + ), + 0, + ) + + # TODO: Nao achei um exemplo de m2o concreto para concreto + # def test_m2o_concrete_to_concrete_spec(self): + # self.assertEqual( + # self.env["cte.40.infnfe"] + # ._fields["cte40_infUnidCarga_infNFe_id"] + # .comodel_name, + # "cte.40.tunidcarga", + # ) + + def test_o2m_concrete_to_concrete_spec(self): + self.assertEqual( + self.env["cte.40.tcte_infdoc"]._fields["cte40_infOutros"].comodel_name, + "cte.40.infoutros", + ) + + def test_m2o_stacked_to_odoo(self): + self.assertEqual( + self.env["l10n_br_fiscal.document"]._fields["cte40_enderReme"].comodel_name, + "res.partner", + ) + + def test_o2m_to_odoo(self): + self.assertEqual( + self.env["l10n_br_fiscal.document"]._fields["cte40_occ"].comodel_name, + "l10n_br_cte.modal.rodo.occ", + ) + self.assertEqual( + len( + self.env["l10n_br_cte.modal.rodo.occ"].search( + [("cte40_nOcc", "=", "NO_RECORD")] + ) + ), + 0, + ) + + def test_m2o_stacked_to_concrete(self): + # not stacked because optional + model = ( + self.env["l10n_br_fiscal.document"] + ._fields["cte40_infSolicNFF"] + .comodel_name + ) + self.assertEqual(model, "cte.40.tcte_infsolicnff") + + # TODO: Tratar + # def test_m2o_stacked(self): + # # not stacked because optional + # cte_model = self.env["l10n_br_fiscal.document"] + # # cte40_cana is optional so its fields shoudn't be stacked + # assert "cte40_XXX" not in cte_model._fields.keys() + + def test_doc_stacking_points(self): + doc_keys = [ + "cte40_ide", + "cte40_toma3", + "cte40_toma4", + # "cte40_enderToma", + "cte40_compl", + "cte40_fluxo", + "cte40_entrega", + "cte40_comData", + "cte40_semHora", + "cte40_vPrest", + "cte40_imp", + "cte40_ICMS", + "cte40_infCTeNorm", + "cte40_infCarga", + "cte40_infModal", + ] + keys = [ + k + for k in self.env["l10n_br_fiscal.document"] + .with_context(spec_schema="cte", spec_version="40") + ._get_stacking_points() + .keys() + ] + self.assertEqual(sorted(keys), sorted(doc_keys)) + + # TODO: Tratar + # def test_doc_tree(self): + # base_class = self.env["l10n_br_fiscal.document"] + # tree, visited = self.get_stacked_tree(base_class) + # self.assertEqual(tree, CTe.INFCTE_TREE) + # self.assertEqual(len(visited), 15) # all stacked classes + + def test_m2o_force_stack(self): + pass + + def test_doc_visit_stack(self): + pass diff --git a/l10n_br_cte/tests/test_res_partner.py b/l10n_br_cte/tests/test_res_partner.py new file mode 100644 index 000000000000..3566ec06f679 --- /dev/null +++ b/l10n_br_cte/tests/test_res_partner.py @@ -0,0 +1,71 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestResPartner(TransactionCase): + def setUp(self): + super(TestResPartner, self).setUp() + # Basic setup for the test + self.partner_model = self.env["res.partner"] + self.partner = self.partner_model.create( + { + "name": "Test Partner", + "cnpj_cpf": "87697453000105", + "zip": "12345000", + "phone": "(11) 91234-5678", + "email": "test@company.com", + "country_id": self.env.ref("base.br").id, + "is_company": True, + } + ) + + def test_compute_cte40_xEnder(self): + """Test the computation of the field cte40_xEnder""" + self.partner.write( + { + "street": "Test Street", + "street2": "Apt 101", + "district": "Downtown", + } + ) + self.partner._compute_cte40_xEnder() + self.assertEqual( + self.partner.cte40_xEnder, + "Test Street - Apt 101 - Downtown", + "The cte40_xEnder field was not computed correctly", + ) + + def test_compute_cte_data(self): + """Test the computation of fields related to CNPJ/CPF""" + self.partner._compute_cte_data() + self.assertEqual( + self.partner.cte40_CNPJ, "87697453000105", "CNPJ was not computed correctly" + ) + self.assertFalse(self.partner.cte40_CPF, "CPF should not be set for companies") + + def test_inverse_cte40_CNPJ(self): + """Test the inverse method for the CNPJ field""" + self.partner.cte40_CNPJ = "21524956000162" + self.partner._inverse_cte40_CNPJ() + self.assertEqual( + self.partner.cnpj_cpf, + "21.524.956/0001-62", + "CNPJ was not formatted correctly", + ) + + def test_inverse_cte40_CEP(self): + """Test the inverse method for the ZIP code""" + self.partner.cte40_CEP = "12345999" + self.partner._inverse_cte40_CEP() + self.assertEqual( + self.partner.zip, "12345-999", "ZIP code was not formatted correctly" + ) + + def test_match_or_create_m2o(self): + """Test the match_or_create_m2o method""" + parent_dict = {"cte40_CNPJ": "87697453000105"} + rec_dict = {} + result_id = self.partner_model.match_or_create_m2o(rec_dict, parent_dict) + self.assertTrue(result_id, "Could not create or find a Many2One record") diff --git a/l10n_br_cte/views/cte_action.xml b/l10n_br_cte/views/cte_action.xml new file mode 100644 index 000000000000..221453ddb923 --- /dev/null +++ b/l10n_br_cte/views/cte_action.xml @@ -0,0 +1,47 @@ + + + + + + CT-e + ir.actions.act_window + l10n_br_fiscal.document + tree,form + + + + +

+ Add a new CT-e +

+
+
+ +
diff --git a/l10n_br_cte/views/cte_menu.xml b/l10n_br_cte/views/cte_menu.xml new file mode 100644 index 000000000000..c52f8ff8920d --- /dev/null +++ b/l10n_br_cte/views/cte_menu.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/l10n_br_cte/views/document.xml b/l10n_br_cte/views/document.xml new file mode 100644 index 000000000000..cb0a578b2fdf --- /dev/null +++ b/l10n_br_cte/views/document.xml @@ -0,0 +1,183 @@ + + + + + + >cte.document.form.view (in l10n_br_cte) + l10n_br_fiscal.document + + 5 + + + [('document_type_id.code', 'in', ['57', '08', '09', '10', '11', '26', '67', '8B'])] + + + [('document_type_id.code', 'in', ['57', '08', '09', '10', '11', '26', '67', '8B'])] + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+
+ + + + + + + + + + + +
+ + + + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
diff --git a/l10n_br_cte/views/modal/modal_aereo.xml b/l10n_br_cte/views/modal/modal_aereo.xml new file mode 100644 index 000000000000..eaa86fccea46 --- /dev/null +++ b/l10n_br_cte/views/modal/modal_aereo.xml @@ -0,0 +1,21 @@ + + + + + modal.aereo.peri.form.view (in l10n_br_cte) + l10n_br_cte.modal.aereo.peri + +
+ + + + + + + + +
+
+
+
diff --git a/l10n_br_cte/views/modal/modal_aquaviario.xml b/l10n_br_cte/views/modal/modal_aquaviario.xml new file mode 100644 index 000000000000..28933d4a09eb --- /dev/null +++ b/l10n_br_cte/views/modal/modal_aquaviario.xml @@ -0,0 +1,18 @@ + + + + + modal.aquaviario.balsa.form.view (in l10n_br_cte) + l10n_br_cte.modal.aquav.balsa + +
+ + + + + +
+
+
+
diff --git a/l10n_br_cte/views/modal/modal_ferroviario.xml b/l10n_br_cte/views/modal/modal_ferroviario.xml new file mode 100644 index 000000000000..2c11907ab735 --- /dev/null +++ b/l10n_br_cte/views/modal/modal_ferroviario.xml @@ -0,0 +1,51 @@ + + + + + res.partner.ferroenv.form.view (in l10n_br_cte) + res.partner + +
+ + + + + + + + + +
+
+
+ + + res.partner.tenderfer.form.view + res.partner + +
+ + + + + + + + + + + + +
+
+
+
diff --git a/l10n_br_cte/views/modal/modal_rodoviario.xml b/l10n_br_cte/views/modal/modal_rodoviario.xml new file mode 100644 index 000000000000..338957199239 --- /dev/null +++ b/l10n_br_cte/views/modal/modal_rodoviario.xml @@ -0,0 +1,25 @@ + + + + + modal.rodoviario.occ.form.view (in l10n_br_cte) + l10n_br_cte.modal.rodo.occ + +
+ + + + + + + + + + + + +
+
+
+
diff --git a/l10n_br_cte/views/res_company.xml b/l10n_br_cte/views/res_company.xml new file mode 100644 index 000000000000..028649e95ea5 --- /dev/null +++ b/l10n_br_cte/views/res_company.xml @@ -0,0 +1,36 @@ + + + + + + cte.res.company.form + res.company + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/l10n_br_cte/wizards/document_correction_wizard.xml b/l10n_br_cte/wizards/document_correction_wizard.xml new file mode 100644 index 000000000000..7b93ccffab13 --- /dev/null +++ b/l10n_br_cte/wizards/document_correction_wizard.xml @@ -0,0 +1,30 @@ + + + + + + l10n_br_fiscal.document.correction.wizard + + + + + + + +
+ Por favor, para carta de correção de CT-e, no campo Justificativa informe por linha (Grupo Alterado;Campo Alterado;Valor Alterado).
+ Exemplo:
+ compl;xObs;Nova Observação
+ ide;cfop;6353 +
+
+
+
+
+ +
diff --git a/l10n_br_cte_spec/README.rst b/l10n_br_cte_spec/README.rst index 6fbd8be6c3fd..ff00c3cd862c 100644 --- a/l10n_br_cte_spec/README.rst +++ b/l10n_br_cte_spec/README.rst @@ -32,11 +32,7 @@ Este módulo contem a estrutura de dados do Conhecimento de Transporte Eletrôni Este módulo não faz nada sozinho, ele precisaria de um modulo `l10n_br_cte` que mapearia esses mixins nos documentos fiscais Odoo de forma semelhante a forma como o módulo `l10n_br_nfe` faz como o módulo `l10n_br_nfe_spec`. -Este módulo inclue os principais leiautes persistantes de CT-e: - -* CT-e (Conhecimento de Transporte Eletrônico) -* CT-e OS (Conhecimento de transporte eletrônico para outros serviço - +Este módulo inclui os principais layouts persistentes do CT-e (Conhecimento de Transporte Eletrônico). Geração ~~~~~~~ @@ -48,7 +44,7 @@ https://github.com/akretion/xsdata-odoo O comando usado foi:: - export XSDATA_SCHEMA=cte; export XSDATA_VERSION=30; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese" + export XSDATA_SCHEMA=cte; export XSDATA_VERSION=40; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese" xsdata generate nfelib/cte/schemas/v4_0 --package nfelib.cte.odoo.v4_0 --output=odoo .. IMPORTANT:: diff --git a/l10n_br_cte_spec/models/__init__.py b/l10n_br_cte_spec/models/__init__.py index 1d382931ae2d..3140ceedcffa 100644 --- a/l10n_br_cte_spec/models/__init__.py +++ b/l10n_br_cte_spec/models/__init__.py @@ -1,2 +1,2 @@ -from . import spec_models +from . import spec_mixin from . import v4_0 diff --git a/l10n_br_cte_spec/models/spec_models.py b/l10n_br_cte_spec/models/spec_mixin.py similarity index 77% rename from l10n_br_cte_spec/models/spec_models.py rename to l10n_br_cte_spec/models/spec_mixin.py index 52d5afaccfb3..799fffbfe806 100644 --- a/l10n_br_cte_spec/models/spec_models.py +++ b/l10n_br_cte_spec/models/spec_mixin.py @@ -8,12 +8,10 @@ class CteSpecMixin(models.AbstractModel): _description = "Abstract Model" _name = "spec.mixin.cte" _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _binding_module = "nfelib.cte.bindings.v4_0.cte_v4_00" - _spec_tab_name = "cte" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" brl_currency_id = fields.Many2one( comodel_name="res.currency", diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_aereo_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_aereo_v4_00.py index ca0ea2c6db56..4f9683a151d4 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_aereo_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_aereo_v4_00.py @@ -30,7 +30,10 @@ "06", "gelo seco para refrigeração (especificar no campo observações a quantidade)", ), - ("07", "não restrito (especificar a Disposição Especial no campo observações)"), + ( + "07", + "não restrito (especificar a Disposição Especial no campo observações)", + ), ( "08", "artigo perigoso em carga consolidada (especificar a quantidade no campo observações)", @@ -39,10 +42,22 @@ "09", "autorização da autoridade governamental anexa (especificar no campo observações)", ), - ("10", "baterias de íons de lítio em conformidade com a Seção II da PI965 – CAO"), - ("11", "baterias de íons de lítio em conformidade com a Seção II da PI966"), - ("12", "baterias de íons de lítio em conformidade com a Seção II da PI967"), - ("13", "baterias de metal lítio em conformidade com a Seção II da PI968 — CAO"), + ( + "10", + "baterias de íons de lítio em conformidade com a Seção II da PI965 – CAO", + ), + ( + "11", + "baterias de íons de lítio em conformidade com a Seção II da PI966", + ), + ( + "12", + "baterias de íons de lítio em conformidade com a Seção II da PI967", + ), + ( + "13", + "baterias de metal lítio em conformidade com a Seção II da PI968 — CAO", + ), ("14", "baterias de metal lítio em conformidade com a Seção II da PI969"), ("15", "baterias de metal lítio em conformidade com a Seção II da PI970"), ("99", "outro (especificar no campo observações)"), @@ -51,6 +66,7 @@ class Aereo(models.AbstractModel): "Informações do modal Aéreo" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aereo" _inherit = "spec.mixin.cte" @@ -87,11 +103,15 @@ class Aereo(models.AbstractModel): ) cte40_natCarga = fields.Many2one( - comodel_name="cte.40.natcarga", string="Natureza da carga", xsd_required=True + comodel_name="cte.40.natcarga", + string="Natureza da carga", + xsd_required=True, ) cte40_tarifa = fields.Many2one( - comodel_name="cte.40.tarifa", string="Informações de tarifa", xsd_required=True + comodel_name="cte.40.tarifa", + string="Informações de tarifa", + xsd_required=True, ) cte40_peri = fields.One2many( @@ -109,6 +129,7 @@ class Aereo(models.AbstractModel): class NatCarga(models.AbstractModel): "Natureza da carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.natcarga" _inherit = "spec.mixin.cte" @@ -126,9 +147,9 @@ class NatCarga(models.AbstractModel): ) - class Tarifa(models.AbstractModel): "Informações de tarifa" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tarifa" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_aquaviario_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_aquaviario_v4_00.py index cc85f0bb6e25..e574119a2003 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_aquaviario_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_aquaviario_v4_00.py @@ -24,6 +24,7 @@ class Aquav(models.AbstractModel): "Informações do modal Aquaviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aquav" _inherit = "spec.mixin.cte" @@ -45,10 +46,14 @@ class Aquav(models.AbstractModel): help=("AFRMM (Adicional de Frete para Renovação da Marinha Mercante)"), ) - cte40_xNavio = fields.Char(string="Identificação do Navio", xsd_required=True) + cte40_xNavio = fields.Char( + string="Identificação do Navio", xsd_required=True + ) cte40_balsa = fields.One2many( - "cte.40.balsa", "cte40_balsa_aquav_id", string="Grupo de informações das balsas" + "cte.40.balsa", + "cte40_balsa_aquav_id", + string="Grupo de informações das balsas", ) cte40_nViag = fields.Char(string="Número da Viagem") @@ -88,6 +93,7 @@ class Aquav(models.AbstractModel): class Balsa(models.AbstractModel): "Grupo de informações das balsas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.balsa" _inherit = "spec.mixin.cte" @@ -96,7 +102,9 @@ class Balsa(models.AbstractModel): cte40_balsa_aquav_id = fields.Many2one( comodel_name="cte.40.aquav", xsd_implicit=True, ondelete="cascade" ) - cte40_xBalsa = fields.Char(string="Identificador da Balsa", xsd_required=True) + cte40_xBalsa = fields.Char( + string="Identificador da Balsa", xsd_required=True + ) class DetCont(models.AbstractModel): @@ -112,14 +120,18 @@ class DetCont(models.AbstractModel): comodel_name="cte.40.aquav", xsd_implicit=True, ondelete="cascade" ) cte40_nCont = fields.Char( - string="Identificação do Container", xsd_required=True, xsd_type="TContainer" + string="Identificação do Container", + xsd_required=True, + xsd_type="TContainer", ) cte40_lacre = fields.One2many( "cte.40.lacre", "cte40_lacre_detCont_id", string="Grupo de informações dos lacres", - help=("Grupo de informações dos lacres dos cointainers da qtde da carga"), + help=( + "Grupo de informações dos lacres dos cointainers da qtde da carga" + ), ) cte40_infDoc = fields.Many2one( @@ -131,6 +143,7 @@ class DetCont(models.AbstractModel): class Lacre(models.AbstractModel): "Grupo de informações dos lacres dos cointainers da qtde da carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.lacre" _inherit = "spec.mixin.cte" @@ -144,6 +157,7 @@ class Lacre(models.AbstractModel): class AquavInfDoc(models.AbstractModel): "Informações dos documentos dos conteiners" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aquav_infdoc" _inherit = "spec.mixin.cte" @@ -168,13 +182,16 @@ class AquavInfDoc(models.AbstractModel): class AquavInfNf(models.AbstractModel): "Informações das NF" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aquav_infnf" _inherit = "spec.mixin.cte" _binding_type = "Aquav.DetCont.InfDoc.InfNf" cte40_infNF_infDoc_id = fields.Many2one( - comodel_name="cte.40.aquav_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.aquav_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_serie = fields.Char(string="Série", xsd_required=True) @@ -190,13 +207,16 @@ class AquavInfNf(models.AbstractModel): class AquavInfNfe(models.AbstractModel): "Informações das NFe" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aquav_infnfe" _inherit = "spec.mixin.cte" _binding_type = "Aquav.DetCont.InfDoc.InfNfe" cte40_infNFe_infDoc_id = fields.Many2one( - comodel_name="cte.40.aquav_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.aquav_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_chave = fields.Char( string="Chave de acesso da NF-e", xsd_required=True, xsd_type="TChDFe" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_dutoviario_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_dutoviario_v4_00.py index 049ecddcd0da..499f1fb6b22b 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_dutoviario_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_dutoviario_v4_00.py @@ -10,6 +10,7 @@ class Duto(models.AbstractModel): "Informações do modal Dutoviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.duto" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_ferroviario_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_ferroviario_v4_00.py index 528246f366f4..83469dc6a1c2 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_ferroviario_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_ferroviario_v4_00.py @@ -32,6 +32,7 @@ class TenderFer(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tenderfer" _inherit = "spec.mixin.cte" @@ -58,7 +59,10 @@ class TenderFer(models.AbstractModel): cte40_xMun = fields.Char( string="Nome do município", xsd_required=True, - help=("Nome do município\nInformar EXTERIOR para operações com o " "exterior."), + help=( + "Nome do município\nInformar EXTERIOR para operações com o " + "exterior." + ), ) cte40_CEP = fields.Char(string="CEP", xsd_required=True) @@ -74,6 +78,7 @@ class TenderFer(models.AbstractModel): class Ferrov(models.AbstractModel): "Informações do modal Ferroviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.ferrov" _inherit = "spec.mixin.cte" @@ -108,6 +113,7 @@ class Ferrov(models.AbstractModel): class TrafMut(models.AbstractModel): "Detalhamento de informações para o tráfego mútuo" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.trafmut" _inherit = "spec.mixin.cte" @@ -157,6 +163,7 @@ class TrafMut(models.AbstractModel): class FerroEnv(models.AbstractModel): "Informações das Ferrovias Envolvidas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.ferroenv" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_os_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_os_v4_00.py index cd85efc1bdc9..00a35ea8047e 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_os_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_os_v4_00.py @@ -25,6 +25,7 @@ class RodoOs(models.AbstractModel): "Informações do modal Rodoviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.rodoos" _inherit = "spec.mixin.cte" @@ -53,7 +54,9 @@ class RodoOs(models.AbstractModel): ), ) - cte40_veic = fields.Many2one(comodel_name="cte.40.veic", string="Dados do Veículo") + cte40_veic = fields.Many2one( + comodel_name="cte.40.veic", string="Dados do Veículo" + ) cte40_infFretamento = fields.Many2one( comodel_name="cte.40.inffretamento", @@ -64,6 +67,7 @@ class RodoOs(models.AbstractModel): class Veic(models.AbstractModel): "Dados do Veículo" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.veic" _inherit = "spec.mixin.cte" @@ -163,6 +167,7 @@ class Prop(models.AbstractModel): class InfFretamento(models.AbstractModel): "Dados do fretamento (apenas para Transporte de Pessoas)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.inffretamento" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_v4_00.py index 569ac6087f79..e9c048fe476c 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_v4_00.py @@ -12,6 +12,7 @@ class Rodo(models.AbstractModel): "Informações do modal Rodoviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.rodo" _inherit = "spec.mixin.cte" @@ -35,6 +36,7 @@ class Rodo(models.AbstractModel): class Occ(models.AbstractModel): "Ordens de Coleta associados" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.occ" _inherit = "spec.mixin.cte" @@ -45,7 +47,9 @@ class Occ(models.AbstractModel): ) cte40_serie = fields.Char(string="Série da OCC") - cte40_nOcc = fields.Char(string="Número da Ordem de coleta", xsd_required=True) + cte40_nOcc = fields.Char( + string="Número da Ordem de coleta", xsd_required=True + ) cte40_dEmi = fields.Date( string="Data de emissão da ordem de coleta", diff --git a/l10n_br_cte_spec/models/v4_0/cte_multi_modal_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_multi_modal_v4_00.py index df1d6a616358..34201d7161f9 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_multi_modal_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_multi_modal_v4_00.py @@ -16,6 +16,7 @@ class Multimodal(models.AbstractModel): "Informações do Multimodal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.multimodal" _inherit = "spec.mixin.cte" @@ -32,7 +33,8 @@ class Multimodal(models.AbstractModel): string="Indicador Negociável", xsd_required=True, help=( - "Indicador Negociável\nPreencher com: 0 - Não Negociável; 1 - " "Negociável" + "Indicador Negociável\nPreencher com: 0 - Não Negociável; 1 - " + "Negociável" ), ) @@ -44,6 +46,7 @@ class Multimodal(models.AbstractModel): class MultimodalSeg(models.AbstractModel): "Informações de Seguro do Multimodal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.multimodal_seg" _inherit = "spec.mixin.cte" @@ -73,6 +76,7 @@ class MultimodalSeg(models.AbstractModel): class InfSeg(models.AbstractModel): "Informações da seguradora" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infseg" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py index 235fa73e02f7..b6efa7c4946e 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py @@ -100,6 +100,12 @@ ("3", "3"), ] +"Tipos Finalidade de CT-e Simplificado" +TFINCTESIMP = [ + ("4", "4"), + ("5", "5"), +] + "Tipo Finalidade da GTV-e" TFINGTVE = [ ("4", "4"), @@ -113,6 +119,13 @@ ("04", "04"), ] +"Tipo Modal transporte do CTe Simplificado" +TMODTRANSPSIMP = [ + ("01", "01"), + ("02", "02"), + ("03", "03"), +] + "Tipo processo de emissão do CT-e" TPROCEMI = [ ("0", "0"), @@ -183,6 +196,13 @@ ("1", "1"), ] +"""indica se a prestação é total ou parcial em relação as notas do + documento anterior""" +INFDOCANT_TPPREST = [ + ("1", "Total"), + ("2", "Parcial"), +] + "Tipo da Espécie" INFESPECIE_TPESPECIE = [ ("1", "Numerário"), @@ -216,6 +236,27 @@ ("05", "MMBTU"), ] +"Tipo da Medida" +INFQ_TPMED = [ + ("00", "Cubagem da NF-e"), + ("01", "Cubagem Aferida pelo Transportador"), + ("02", "Peso Bruto da NF-e"), + ("03", "Peso Bruto Aferido pelo Transportador"), + ("04", "Peso Cubado"), + ("05", "Peso Base do Cálculo do Frete"), + ("06", "Peso para uso Operacional"), + ("07", "Caixas"), + ("08", "Paletes"), + ("09", "Sacas"), + ("10", "Containers"), + ("11", "Rolos"), + ("12", "Bombonas"), + ("13", "Latas"), + ("14", "Litragem"), + ("15", "Milhão de BTU (British Thermal Units)"), + ("99", "Outros"), +] + "Tipo de hora" NOINTER_TPHOR = [ ("4", "No intervalo de tempo"), @@ -260,15 +301,23 @@ ("4", "Outros"), ] +"Indicador do papel do tomador na prestação do serviço" +TOMA_INDIETOMA = [ + ("1", "Contribuinte ICMS"), + ("2", "Contribuinte isento de inscrição"), + ("9", "Não Contribuinte"), +] + "Tomador do Serviço" TOMA_TOMA = [ ("0", "Remetente"), - ("1", "Destinatário"), + ("1", "Expedidor"), ] class TrespTec(models.AbstractModel): "Tipo Dados da Responsável Técnico" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tresptec" _inherit = "spec.mixin.cte" @@ -334,6 +383,7 @@ class TrespTec(models.AbstractModel): class TendOrg(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendorg" _inherit = "spec.mixin.cte" @@ -360,7 +410,10 @@ class TendOrg(models.AbstractModel): cte40_xMun = fields.Char( string="Nome do município", xsd_required=True, - help=("Nome do município\nInformar EXTERIOR para operações com o " "exterior."), + help=( + "Nome do município\nInformar EXTERIOR para operações com o " + "exterior." + ), ) cte40_CEP = fields.Char(string="CEP") @@ -382,6 +435,7 @@ class TendOrg(models.AbstractModel): class TendReEnt(models.AbstractModel): "Tipo Dados do Local de Retirada ou Entrega" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendreent" _inherit = "spec.mixin.cte" @@ -424,7 +478,10 @@ class TendReEnt(models.AbstractModel): cte40_xMun = fields.Char( string="Nome do município", xsd_required=True, - help=("Nome do município\nInformar EXTERIOR para operações com o " "exterior."), + help=( + "Nome do município\nInformar EXTERIOR para operações com o " + "exterior." + ), ) cte40_UF = fields.Selection( @@ -438,6 +495,7 @@ class TendReEnt(models.AbstractModel): class TendeEmi(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendeemi" _inherit = "spec.mixin.cte" @@ -460,10 +518,15 @@ class TendeEmi(models.AbstractModel): cte40_xMun = fields.Char(string="Nome do município", xsd_required=True) - cte40_CEP = fields.Char(string="CEP", help="CEP\nInformar zeros não significativos") + cte40_CEP = fields.Char( + string="CEP", help="CEP\nInformar zeros não significativos" + ) cte40_UF = fields.Selection( - TUF_SEM_EX, string="Sigla da UF", xsd_required=True, xsd_type="TUF_sem_EX" + TUF_SEM_EX, + string="Sigla da UF", + xsd_required=True, + xsd_type="TUF_sem_EX", ) cte40_fone = fields.Char(string="Telefone", xsd_type="TFone") @@ -471,6 +534,7 @@ class TendeEmi(models.AbstractModel): class Tendereco(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendereco" _inherit = "spec.mixin.cte" @@ -497,7 +561,10 @@ class Tendereco(models.AbstractModel): cte40_xMun = fields.Char( string="Nome do município", xsd_required=True, - help=("Nome do município\nInformar EXTERIOR para operações com o " "exterior."), + help=( + "Nome do município\nInformar EXTERIOR para operações com o " + "exterior." + ), ) cte40_CEP = fields.Char( @@ -513,7 +580,8 @@ class Tendereco(models.AbstractModel): ) cte40_cPais = fields.Char( - string="Código do país", help="Código do país\nUtilizar a tabela do BACEN" + string="Código do país", + help="Código do país\nUtilizar a tabela do BACEN", ) cte40_xPais = fields.Char(string="Nome do país") @@ -521,6 +589,7 @@ class Tendereco(models.AbstractModel): class Tendernac(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendernac" _inherit = "spec.mixin.cte" @@ -548,7 +617,8 @@ class Tendernac(models.AbstractModel): string="Nome do município, ", xsd_required=True, help=( - "Nome do município, , informar EXTERIOR para operações com o " "exterior." + "Nome do município, , informar EXTERIOR para operações com o " + "exterior." ), ) @@ -565,6 +635,7 @@ class Tendernac(models.AbstractModel): class Timp(models.AbstractModel): "Tipo Dados do Imposto CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.timp" _inherit = "spec.mixin.cte" @@ -573,6 +644,7 @@ class Timp(models.AbstractModel): class TimpOs(models.AbstractModel): "Tipo Dados do Imposto para CT-e OS" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.timpos" _inherit = "spec.mixin.cte" @@ -581,6 +653,7 @@ class TimpOs(models.AbstractModel): class Tlocal(models.AbstractModel): "Tipo Dados do Local de Origem ou Destino" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tlocal" _inherit = "spec.mixin.cte" @@ -602,6 +675,7 @@ class Tlocal(models.AbstractModel): class TprotCte(models.AbstractModel): "Tipo Protocolo de status resultado do processamento da CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcte" _inherit = "spec.mixin.cte" @@ -622,6 +696,7 @@ class TprotCte(models.AbstractModel): class TprotCteInfProt(models.AbstractModel): "Dados do protocolo de status" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcte_infprot" _inherit = "spec.mixin.cte" @@ -650,7 +725,9 @@ class TprotCteInfProt(models.AbstractModel): string="Data e hora de processamento", xsd_required=True, xsd_type="TDateTimeUTC", - help=("Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD."), + help=( + "Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD." + ), ) cte40_nProt = fields.Char( @@ -666,7 +743,9 @@ class TprotCteInfProt(models.AbstractModel): ), ) - cte40_cStat = fields.Char(string="Código do status do CT-e", xsd_required=True) + cte40_cStat = fields.Char( + string="Código do status do CT-e", xsd_required=True + ) cte40_xMotivo = fields.Char( string="Descrição literal do status do CT-e", @@ -679,6 +758,7 @@ class TprotCteInfProt(models.AbstractModel): class TprotCteInfFisco(models.AbstractModel): "Mensagem do Fisco" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcte_inffisco" _inherit = "spec.mixin.cte" @@ -717,6 +797,7 @@ class TprotCteOs(models.AbstractModel): class TprotCteOsInfProt(models.AbstractModel): "Dados do protocolo de status" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcteos_infprot" _inherit = "spec.mixin.cte" @@ -745,7 +826,9 @@ class TprotCteOsInfProt(models.AbstractModel): string="Data e hora de processamento", xsd_required=True, xsd_type="TDateTimeUTC", - help=("Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD."), + help=( + "Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD." + ), ) cte40_nProt = fields.Char( @@ -761,7 +844,9 @@ class TprotCteOsInfProt(models.AbstractModel): ), ) - cte40_cStat = fields.Char(string="Código do status do CT-e", xsd_required=True) + cte40_cStat = fields.Char( + string="Código do status do CT-e", xsd_required=True + ) cte40_xMotivo = fields.Char( string="Descrição literal do status do CT-e", @@ -774,6 +859,7 @@ class TprotCteOsInfProt(models.AbstractModel): class TprotCteOsInfFisco(models.AbstractModel): "Mensagem do Fisco" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcteos_inffisco" _inherit = "spec.mixin.cte" @@ -812,6 +898,7 @@ class TprotGtve(models.AbstractModel): class TprotGtveInfProt(models.AbstractModel): "Dados do protocolo de status" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotgtve_infprot" _inherit = "spec.mixin.cte" @@ -840,7 +927,9 @@ class TprotGtveInfProt(models.AbstractModel): string="Data e hora de processamento", xsd_required=True, xsd_type="TDateTimeUTC", - help=("Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD."), + help=( + "Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD." + ), ) cte40_nProt = fields.Char( @@ -856,7 +945,9 @@ class TprotGtveInfProt(models.AbstractModel): ), ) - cte40_cStat = fields.Char(string="Código do status da GTV-e", xsd_required=True) + cte40_cStat = fields.Char( + string="Código do status da GTV-e", xsd_required=True + ) cte40_xMotivo = fields.Char( string="Descrição literal do status da GTV-e", @@ -869,6 +960,7 @@ class TprotGtveInfProt(models.AbstractModel): class TprotGtveInfFisco(models.AbstractModel): "Mensagem do Fisco" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotgtve_inffisco" _inherit = "spec.mixin.cte" @@ -885,22 +977,32 @@ class TprotGtveInfFisco(models.AbstractModel): class TunidCarga(models.AbstractModel): "Tipo Dados Unidade de Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tunidcarga" _inherit = "spec.mixin.cte" _binding_type = "TunidCarga" + cte40_infUnidCarga_infNFe_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infnfe", + xsd_implicit=True, + ondelete="cascade", + ) cte40_infUnidCarga_infNF_id = fields.Many2one( comodel_name="cte.40.tcte_infnf", xsd_implicit=True, ondelete="cascade" ) cte40_infUnidCarga_infNFe_id = fields.Many2one( - comodel_name="cte.40.tcte_infnfe", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infnfe", + xsd_implicit=True, + ondelete="cascade", ) cte40_infUnidCarga_infOutros_id = fields.Many2one( comodel_name="cte.40.infoutros", xsd_implicit=True, ondelete="cascade" ) cte40_infUnidCarga_TUnidadeTransp_id = fields.Many2one( - comodel_name="cte.40.tunidadetransp", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tunidadetransp", + xsd_implicit=True, + ondelete="cascade", ) cte40_tpUnidCarga = fields.Selection( TTIPOUNIDCARGA, @@ -938,6 +1040,7 @@ class TunidCarga(models.AbstractModel): class LacUnidCarga(models.AbstractModel): "Lacres das Unidades de Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.lacunidcarga" _inherit = "spec.mixin.cte" @@ -974,6 +1077,7 @@ class TcteOs(models.AbstractModel): class TcteOsInfCte(models.AbstractModel): "Informações do CT-e do tipo GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infcte" _inherit = "spec.mixin.cte" @@ -988,7 +1092,9 @@ class TcteOsInfCte(models.AbstractModel): cte40_compl = fields.Many2one( comodel_name="cte.40.tcteos_compl", string="Dados complementares do CT-e", - help=("Dados complementares do CT-e para fins operacionais ou comerciais"), + help=( + "Dados complementares do CT-e para fins operacionais ou comerciais" + ), ) cte40_emit = fields.Many2one( @@ -1012,6 +1118,7 @@ class TcteOsInfCte(models.AbstractModel): comodel_name="cte.40.tcteos_vprest", string="Valores da Prestação de Serviço", xsd_required=True, + xsd_type="TDec_1302", ) cte40_imp = fields.Many2one( @@ -1070,6 +1177,7 @@ class TcteOsInfCte(models.AbstractModel): class TcteOsIde(models.AbstractModel): "Identificação da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_ide" _inherit = "spec.mixin.cte" @@ -1127,7 +1235,10 @@ class TcteOsIde(models.AbstractModel): cte40_dhEmi = fields.Char( string="Data e hora de emissão do CT-e OS", xsd_required=True, - help=("Data e hora de emissão do CT-e OS\nFormato AAAA-MM-DDTHH:MM:DD " "TZD"), + help=( + "Data e hora de emissão do CT-e OS\nFormato AAAA-MM-DDTHH:MM:DD " + "TZD" + ), ) cte40_tpImp = fields.Selection( @@ -1287,7 +1398,8 @@ class TcteOsIde(models.AbstractModel): string="UF do início da prestação", xsd_type="TUf", help=( - "UF do início da prestação\nInformar 'EX' para operações com o " "exterior." + "UF do início da prestação\nInformar 'EX' para operações com o " + "exterior." ), ) @@ -1334,11 +1446,14 @@ class TcteOsIde(models.AbstractModel): ), ) - cte40_xJust = fields.Char(string="Justificativa da entrada em contingência") + cte40_xJust = fields.Char( + string="Justificativa da entrada em contingência" + ) class InfPercurso(models.AbstractModel): "Informações do Percurso do CT-e Outros Serviços" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infpercurso" _inherit = "spec.mixin.cte" @@ -1361,6 +1476,7 @@ class InfPercurso(models.AbstractModel): class TcteOsCompl(models.AbstractModel): "Dados complementares da GTV-e para fins operacionais ou comerciais" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_compl" _inherit = "spec.mixin.cte" @@ -1418,11 +1534,15 @@ class TcteOsObsCont(models.AbstractModel): _binding_type = "TcteOs.InfCte.Compl.ObsCont" cte40_ObsCont_compl_id = fields.Many2one( - comodel_name="cte.40.tcteos_compl", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_compl", + xsd_implicit=True, + ondelete="cascade", ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TcteOsObsFisco(models.AbstractModel): @@ -1435,15 +1555,20 @@ class TcteOsObsFisco(models.AbstractModel): _binding_type = "TcteOs.InfCte.Compl.ObsFisco" cte40_ObsFisco_compl_id = fields.Many2one( - comodel_name="cte.40.tcteos_compl", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_compl", + xsd_implicit=True, + ondelete="cascade", ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TcteOsEmit(models.AbstractModel): "Identificação do Emitente da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_emit" _inherit = "spec.mixin.cte" @@ -1456,10 +1581,13 @@ class TcteOsEmit(models.AbstractModel): help="CNPJ do emitente\nInformar zeros não significativos", ) - cte40_IE = fields.Char(string="Inscrição Estadual do Emitente", xsd_required=True) + cte40_IE = fields.Char( + string="Inscrição Estadual do Emitente", xsd_required=True + ) cte40_IEST = fields.Char( - string="Inscrição Estadual", help="Inscrição Estadual do Substituto Tributário" + string="Inscrição Estadual", + help="Inscrição Estadual do Substituto Tributário", ) cte40_xNome = fields.Char( @@ -1543,11 +1671,13 @@ class TcteOsToma(models.AbstractModel): xsd_type="TEndereco", ) - cte40_email = fields.Char(string="Endereço de email") + cte40_email = fields.Char(string="Endereço de email", xsd_type="TEmail") class TcteOsVPrest(models.AbstractModel): - "Valores da Prestação de Serviço" + """Valorl da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS""" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_vprest" _inherit = "spec.mixin.cte" @@ -1580,13 +1710,16 @@ class TcteOsVPrest(models.AbstractModel): class TcteOsVPrestComp(models.AbstractModel): "Componentes do Valor da Prestação" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_vprest_comp" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.VPrest.Comp" cte40_Comp_vPrest_id = fields.Many2one( - comodel_name="cte.40.tcteos_vprest", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_vprest", + xsd_implicit=True, + ondelete="cascade", ) cte40_xNome = fields.Char( string="Nome do componente", @@ -1607,6 +1740,7 @@ class TcteOsVPrestComp(models.AbstractModel): class TcteOsImp(models.AbstractModel): "Informações relativas aos Impostos" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_imp" _inherit = "spec.mixin.cte" @@ -1655,11 +1789,15 @@ class InfTribFed(models.AbstractModel): _binding_type = "TcteOs.InfCte.Imp.InfTribFed" cte40_vPIS = fields.Monetary( - string="Valor do PIS", xsd_type="TDec_1302", currency_field="brl_currency_id" + string="Valor do PIS", + xsd_type="TDec_1302", + currency_field="brl_currency_id", ) cte40_vCOFINS = fields.Monetary( - string="Valor COFINS", xsd_type="TDec_1302", currency_field="brl_currency_id" + string="Valor COFINS", + xsd_type="TDec_1302", + currency_field="brl_currency_id", ) cte40_vIR = fields.Monetary( @@ -1669,11 +1807,15 @@ class InfTribFed(models.AbstractModel): ) cte40_vINSS = fields.Monetary( - string="Valor do INSS", xsd_type="TDec_1302", currency_field="brl_currency_id" + string="Valor do INSS", + xsd_type="TDec_1302", + currency_field="brl_currency_id", ) cte40_vCSLL = fields.Monetary( - string="Valor do CSLL", xsd_type="TDec_1302", currency_field="brl_currency_id" + string="Valor do CSLL", + xsd_type="TDec_1302", + currency_field="brl_currency_id", ) @@ -1687,7 +1829,9 @@ class TcteOsAutXml(models.AbstractModel): _binding_type = "TcteOs.InfCte.AutXml" cte40_autXML_infCte_id = fields.Many2one( - comodel_name="cte.40.tcteos_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_CNPJ = fields.Char( string="CNPJ do autorizado", @@ -1708,6 +1852,7 @@ class TcteOsAutXml(models.AbstractModel): class TcteOsInfCteNorm(models.AbstractModel): "Grupo de informações do CT-e Normal e Substituto" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctenorm" _inherit = "spec.mixin.cte" @@ -1745,7 +1890,8 @@ class TcteOsInfCteNorm(models.AbstractModel): cte40_refCTeCanc = fields.Char( string="Chave de acesso do CT-e Cancelado", help=( - "Chave de acesso do CT-e Cancelado\nSomente para Transporte de " "Valores" + "Chave de acesso do CT-e Cancelado\nSomente para Transporte de " + "Valores" ), ) @@ -1763,6 +1909,7 @@ class TcteOsInfCteNorm(models.AbstractModel): class InfServico(models.AbstractModel): "Informações da Prestação do Serviço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infservico" _inherit = "spec.mixin.cte" @@ -1811,13 +1958,16 @@ class TcteOsInfQ(models.AbstractModel): class InfDocRef(models.AbstractModel): "Informações dos documentos referenciados" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infdocref" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteNorm.InfDocRef" cte40_infDocRef_infCTeNorm_id = fields.Many2one( - comodel_name="cte.40.tcteos_infctenorm", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infctenorm", + xsd_implicit=True, + ondelete="cascade", ) cte40_nDoc = fields.Char( string="Número", choice="infdocref", xsd_choice_required=True @@ -1857,13 +2007,16 @@ class InfDocRef(models.AbstractModel): class TcteOsSeg(models.AbstractModel): "Informações de Seguro da Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_seg" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteNorm.Seg" cte40_seg_infCTeNorm_id = fields.Many2one( - comodel_name="cte.40.tcteos_infctenorm", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infctenorm", + xsd_implicit=True, + ondelete="cascade", ) cte40_respSeg = fields.Selection( SEG_RESPSEG, @@ -1885,6 +2038,7 @@ class TcteOsSeg(models.AbstractModel): class TcteOsInfModal(models.AbstractModel): "Informações do modal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infmodal" _inherit = "spec.mixin.cte" @@ -1899,6 +2053,7 @@ class TcteOsInfModal(models.AbstractModel): class TcteOsInfCteSub(models.AbstractModel): "Informações do CT-e de substituição" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctesub" _inherit = "spec.mixin.cte" @@ -1913,6 +2068,7 @@ class TcteOsInfCteSub(models.AbstractModel): class TcteOsCobr(models.AbstractModel): "Dados da cobrança do CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_cobr" _inherit = "spec.mixin.cte" @@ -1929,6 +2085,7 @@ class TcteOsCobr(models.AbstractModel): class TcteOsFat(models.AbstractModel): "Dados da fatura" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_fat" _inherit = "spec.mixin.cte" @@ -1957,13 +2114,16 @@ class TcteOsFat(models.AbstractModel): class TcteOsDup(models.AbstractModel): "Dados das duplicatas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_dup" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteNorm.Cobr.Dup" cte40_dup_cobr_id = fields.Many2one( - comodel_name="cte.40.tcteos_cobr", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_cobr", + xsd_implicit=True, + ondelete="cascade", ) cte40_nDup = fields.Char(string="Número da duplicata") @@ -1982,15 +2142,20 @@ class TcteOsDup(models.AbstractModel): class InfGtve(models.AbstractModel): "Informações das GTV-e relacionadas ao CT-e OS" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infgtve" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteNorm.InfGtve" cte40_infGTVe_infCTeNorm_id = fields.Many2one( - comodel_name="cte.40.tcteos_infctenorm", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infctenorm", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_chCTe = fields.Char( + string="Chave de acesso da GTV-e", xsd_required=True ) - cte40_chCTe = fields.Char(string="Chave de acesso da GTV-e", xsd_required=True) cte40_comp = fields.One2many( "cte.40.tcteos_infctenorm_comp", @@ -2001,6 +2166,7 @@ class InfGtve(models.AbstractModel): class TcteOsInfCteNormComp(models.AbstractModel): "Componentes do Valor da Prestação" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctenorm_comp" _inherit = "spec.mixin.cte" @@ -2037,21 +2203,27 @@ class TcteOsInfCteNormComp(models.AbstractModel): class TcteOsInfCteComp(models.AbstractModel): "Detalhamento do CT-e complementado" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctecomp" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteComp" cte40_infCteComp_infCte_id = fields.Many2one( - comodel_name="cte.40.tcteos_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_chCTe = fields.Char( - string="Chave do CT-e complementado", xsd_required=True, xsd_type="TChDFe" + string="Chave do CT-e complementado", + xsd_required=True, + xsd_type="TChDFe", ) class TcteOsInfCteSupl(models.AbstractModel): "Informações suplementares da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctesupl" _inherit = "spec.mixin.cte" @@ -2064,6 +2236,7 @@ class TcteOsInfCteSupl(models.AbstractModel): class Tgtve(models.AbstractModel): "Tipo Guia de Transporte de Valores Eletrônica (Modelo 64)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve" _inherit = "spec.mixin.cte" @@ -2085,6 +2258,7 @@ class Tgtve(models.AbstractModel): class TgtveInfCte(models.AbstractModel): "Informações do CT-e do tipo GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_infcte" _inherit = "spec.mixin.cte" @@ -2099,7 +2273,10 @@ class TgtveInfCte(models.AbstractModel): cte40_compl = fields.Many2one( comodel_name="cte.40.tgtve_compl", string="Dados complementares da GTV-e", - help=("Dados complementares da GTV-e para fins operacionais ou " "comerciais"), + help=( + "Dados complementares da GTV-e para fins operacionais ou " + "comerciais" + ), ) cte40_emit = fields.Many2one( @@ -2185,6 +2362,7 @@ class TgtveInfCte(models.AbstractModel): class TgtveIde(models.AbstractModel): "Identificação da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_ide" _inherit = "spec.mixin.cte" @@ -2195,7 +2373,9 @@ class TgtveIde(models.AbstractModel): string="Código da UF do emitente da GTV-e", xsd_required=True, xsd_type="TCodUfIBGE", - help=("Código da UF do emitente da GTV-e.\nUtilizar a Tabela do IBGE."), + help=( + "Código da UF do emitente da GTV-e.\nUtilizar a Tabela do IBGE." + ), ) cte40_cCT = fields.Char( @@ -2233,12 +2413,16 @@ class TgtveIde(models.AbstractModel): help="Série da GTV-e\nPreencher com '0' no caso de série única", ) - cte40_nCT = fields.Char(string="Número da GTV-e", xsd_required=True, xsd_type="TNF") + cte40_nCT = fields.Char( + string="Número da GTV-e", xsd_required=True, xsd_type="TNF" + ) cte40_dhEmi = fields.Char( string="Data e hora de emissão da GTV-e", xsd_required=True, - help=("Data e hora de emissão da GTV-e\nFormato AAAA-MM-DDTHH:MM:DD TZD"), + help=( + "Data e hora de emissão da GTV-e\nFormato AAAA-MM-DDTHH:MM:DD TZD" + ), ) cte40_tpImp = fields.Selection( @@ -2357,13 +2541,18 @@ class TgtveIde(models.AbstractModel): cte40_dhSaidaOrig = fields.Char( string="Data e hora de saida da origem", xsd_required=True, - help=("Data e hora de saida da origem\nFormato AAAA-MM-DDTHH:MM:DD TZD"), + help=( + "Data e hora de saida da origem\nFormato AAAA-MM-DDTHH:MM:DD TZD" + ), ) cte40_dhChegadaDest = fields.Char( string="Data e hora de chegada no destino", xsd_required=True, - help=("Data e hora de chegada no destino\nFormato AAAA-MM-DDTHH:MM:DD " "TZD"), + help=( + "Data e hora de chegada no destino\nFormato AAAA-MM-DDTHH:MM:DD " + "TZD" + ), ) cte40_toma = fields.Many2one( @@ -2391,7 +2580,9 @@ class TgtveIde(models.AbstractModel): ), ) - cte40_xJust = fields.Char(string="Justificativa da entrada em contingência") + cte40_xJust = fields.Char( + string="Justificativa da entrada em contingência" + ) class TgtveToma(models.AbstractModel): @@ -2481,6 +2672,7 @@ class TomaTerceiro(models.AbstractModel): class TgtveCompl(models.AbstractModel): "Dados complementares da GTV-e para fins operacionais ou comerciais" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_compl" _inherit = "spec.mixin.cte" @@ -2538,11 +2730,15 @@ class TgtveObsCont(models.AbstractModel): _binding_type = "Tgtve.InfCte.Compl.ObsCont" cte40_ObsCont_compl_id = fields.Many2one( - comodel_name="cte.40.tgtve_compl", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tgtve_compl", + xsd_implicit=True, + ondelete="cascade", ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TgtveObsFisco(models.AbstractModel): @@ -2555,15 +2751,20 @@ class TgtveObsFisco(models.AbstractModel): _binding_type = "Tgtve.InfCte.Compl.ObsFisco" cte40_ObsFisco_compl_id = fields.Many2one( - comodel_name="cte.40.tgtve_compl", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tgtve_compl", + xsd_implicit=True, + ondelete="cascade", ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TgtveEmit(models.AbstractModel): "Identificação do Emitente da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_emit" _inherit = "spec.mixin.cte" @@ -2576,10 +2777,13 @@ class TgtveEmit(models.AbstractModel): help="CNPJ do emitente\nInformar zeros não significativos", ) - cte40_IE = fields.Char(string="Inscrição Estadual do Emitente", xsd_required=True) + cte40_IE = fields.Char( + string="Inscrição Estadual do Emitente", xsd_required=True + ) cte40_IEST = fields.Char( - string="Inscrição Estadual", help="Inscrição Estadual do Substituto Tributário" + string="Inscrição Estadual", + help="Inscrição Estadual do Substituto Tributário", ) cte40_xNome = fields.Char( @@ -2722,6 +2926,7 @@ class TgtveDest(models.AbstractModel): class DetGtv(models.AbstractModel): "Grupo de informações detalhadas da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.detgtv" _inherit = "spec.mixin.cte" @@ -2748,13 +2953,15 @@ class DetGtv(models.AbstractModel): "cte40_infVeiculo_detGTV_id", string="Grupo de informações", help=( - "Grupo de informações dos veículos utilizados no transporte de " "valores" + "Grupo de informações dos veículos utilizados no transporte de " + "valores" ), ) class TgtveInfEspecie(models.AbstractModel): "Informações das Espécies transportadas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_infespecie" _inherit = "spec.mixin.cte" @@ -2767,7 +2974,9 @@ class TgtveInfEspecie(models.AbstractModel): INFESPECIE_TPESPECIE, string="Tipo da Espécie", xsd_required=True, - help=("Tipo da Espécie\n1 - Cédula\n2 - Cheque\n3 - Moeda\n4 - Outros"), + help=( + "Tipo da Espécie\n1 - Cédula\n2 - Cheque\n3 - Moeda\n4 - Outros" + ), ) cte40_vEspecie = fields.Monetary( @@ -2831,7 +3040,9 @@ class TgtveAutXml(models.AbstractModel): _binding_type = "Tgtve.InfCte.AutXml" cte40_autXML_infCte_id = fields.Many2one( - comodel_name="cte.40.tgtve_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tgtve_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_CNPJ = fields.Char( string="CNPJ do autorizado", @@ -2852,6 +3063,7 @@ class TgtveAutXml(models.AbstractModel): class TgtveInfCteSupl(models.AbstractModel): "Informações suplementares da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_infctesupl" _inherit = "spec.mixin.cte" @@ -2864,6 +3076,7 @@ class TgtveInfCteSupl(models.AbstractModel): class TretCte(models.AbstractModel): "Tipo Retorno do Pedido de Autorização de CT-e (Modelo 57)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tretcte" _inherit = "spec.mixin.cte" @@ -2910,11 +3123,14 @@ class TretCte(models.AbstractModel): xsd_type="TProtCTe", ) - cte40_versao = fields.Char(string="versao", xsd_required=True, xsd_type="TVerCTe") + cte40_versao = fields.Char( + string="versao", xsd_required=True, xsd_type="TVerCTe" + ) class TretCteOs(models.AbstractModel): "Tipo Retorno do Pedido de Autorização de CT-e OS (Modelo 67)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tretcteos" _inherit = "spec.mixin.cte" @@ -2961,11 +3177,69 @@ class TretCteOs(models.AbstractModel): xsd_type="TProtCTeOS", ) - cte40_versao = fields.Char(string="versao", xsd_required=True, xsd_type="TVerCTe") + cte40_versao = fields.Char( + string="versao", xsd_required=True, xsd_type="TVerCTe" + ) + + +class TretCteSimp(models.AbstractModel): + """Tipo Retorno do Pedido de Autorização de CT-e Simplificado (Modelo + 57)""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tretctesimp" + _inherit = "spec.mixin.cte" + _binding_type = "TretCteSimp" + + cte40_tpAmb = fields.Selection( + TAMB, + string="Identificação do Ambiente", + xsd_required=True, + xsd_type="TAmb", + help="Identificação do Ambiente:\n1 - Produção\n2 - Homologação", + ) + + cte40_cUF = fields.Selection( + TCODUFIBGE, + string="Identificação da UF", + xsd_required=True, + xsd_type="TCodUfIBGE", + ) + + cte40_verAplic = fields.Char( + string="Versão do Aplicativo que processou", + xsd_required=True, + xsd_type="TVerAplic", + help="Versão do Aplicativo que processou a CT-e", + ) + + cte40_cStat = fields.Char( + string="código do status do retorno da consulta", + xsd_required=True, + xsd_type="TStat", + ) + + cte40_xMotivo = fields.Char( + string="Descrição literal do status", + xsd_required=True, + xsd_type="TMotivo", + help="Descrição literal do status do do retorno da consulta.", + ) + + cte40_protCTe = fields.Many2one( + comodel_name="cte.40.tprotcte", + string="Reposta ao processamento do CT-e", + xsd_type="TProtCTe", + ) + + cte40_versao = fields.Char( + string="versao", xsd_required=True, xsd_type="TVerCTe" + ) class TretGtve(models.AbstractModel): "Tipo Retorno do Pedido de Autorização de GTV-e (Modelo 64)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tretgtve" _inherit = "spec.mixin.cte" @@ -3012,21 +3286,31 @@ class TretGtve(models.AbstractModel): xsd_type="TProtGTVe", ) - cte40_versao = fields.Char(string="versao", xsd_required=True, xsd_type="TVerCTe") + cte40_versao = fields.Char( + string="versao", xsd_required=True, xsd_type="TVerCTe" + ) class TunidadeTransp(models.AbstractModel): "Tipo Dados Unidade de Transporte" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tunidadetransp" _inherit = "spec.mixin.cte" _binding_type = "TunidadeTransp" + cte40_infUnidTransp_infNFe_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infnfe", + xsd_implicit=True, + ondelete="cascade", + ) cte40_infUnidTransp_infNF_id = fields.Many2one( comodel_name="cte.40.tcte_infnf", xsd_implicit=True, ondelete="cascade" ) cte40_infUnidTransp_infNFe_id = fields.Many2one( - comodel_name="cte.40.tcte_infnfe", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infnfe", + xsd_implicit=True, + ondelete="cascade", ) cte40_infUnidTransp_infOutros_id = fields.Many2one( comodel_name="cte.40.infoutros", xsd_implicit=True, ondelete="cascade" @@ -3084,19 +3368,23 @@ class TunidadeTransp(models.AbstractModel): class LacUnidTransp(models.AbstractModel): "Lacres das Unidades de Transporte" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.lacunidtransp" _inherit = "spec.mixin.cte" _binding_type = "TunidadeTransp.LacUnidTransp" cte40_lacUnidTransp_TUnidadeTransp_id = fields.Many2one( - comodel_name="cte.40.tunidadetransp", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tunidadetransp", + xsd_implicit=True, + ondelete="cascade", ) cte40_nLacre = fields.Char(string="Número do lacre", xsd_required=True) class Tcte(models.AbstractModel): "Tipo Conhecimento de Transporte Eletrônico (Modelo 57)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte" _inherit = "spec.mixin.cte" @@ -3116,6 +3404,7 @@ class Tcte(models.AbstractModel): class TcteInfCte(models.AbstractModel): "Informações do CT-e do tipo GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infcte" _inherit = "spec.mixin.cte" @@ -3130,7 +3419,9 @@ class TcteInfCte(models.AbstractModel): cte40_compl = fields.Many2one( comodel_name="cte.40.tcte_compl", string="Dados complementares do CT-e", - help=("Dados complementares do CT-e para fins operacionais ou comerciais"), + help=( + "Dados complementares do CT-e para fins operacionais ou comerciais" + ), ) cte40_emit = fields.Many2one( @@ -3172,6 +3463,7 @@ class TcteInfCte(models.AbstractModel): comodel_name="cte.40.tcte_vprest", string="Valores da Prestação de Serviço", xsd_required=True, + xsd_type="TDec_1302", ) cte40_imp = fields.Many2one( @@ -3216,7 +3508,9 @@ class TcteInfCte(models.AbstractModel): cte40_infSolicNFF = fields.Many2one( comodel_name="cte.40.tcte_infsolicnff", string="Grupo de informações do pedido", - help=("Grupo de informações do pedido de emissão da Nota Fiscal Fácil"), + help=( + "Grupo de informações do pedido de emissão da Nota Fiscal Fácil" + ), ) cte40_infPAA = fields.Many2one( @@ -3243,6 +3537,7 @@ class TcteInfCte(models.AbstractModel): class TcteIde(models.AbstractModel): "Identificação da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_ide" _inherit = "spec.mixin.cte" @@ -3292,12 +3587,16 @@ class TcteIde(models.AbstractModel): help="Série do CT-e\nPreencher com '0' no caso de série única", ) - cte40_nCT = fields.Char(string="Número do CT-e", xsd_required=True, xsd_type="TNF") + cte40_nCT = fields.Char( + string="Número do CT-e", xsd_required=True, xsd_type="TNF" + ) cte40_dhEmi = fields.Char( string="Data e hora de emissão do CT-e", xsd_required=True, - help=("Data e hora de emissão do CT-e\nFormato AAAA-MM-DDTHH:MM:DD TZD"), + help=( + "Data e hora de emissão do CT-e\nFormato AAAA-MM-DDTHH:MM:DD TZD" + ), ) cte40_tpImp = fields.Selection( @@ -3337,7 +3636,9 @@ class TcteIde(models.AbstractModel): string="Tipo do Ambiente", xsd_required=True, xsd_type="TAmb", - help=("Tipo do Ambiente\nPreencher com:1 - Produção; 2 - Homologação."), + help=( + "Tipo do Ambiente\nPreencher com:1 - Produção; 2 - Homologação." + ), ) cte40_tpCTe = fields.Selection( @@ -3429,9 +3730,9 @@ class TcteIde(models.AbstractModel): string="Tipo do Serviço", xsd_required=True, help=( - "Tipo do Serviço\nPreencher com: \n0 - Normal;1 - " - "Subcontratação;\n2 - Redespacho;3 - Redespacho Intermediário; 4 -" - " Serviço Vinculado a Multimodal" + "Tipo do Serviço\nPreencher com: \n0 - Normal;\n1 - " + "Subcontratação;\n2 - Redespacho;\n3 - Redespacho Intermediário; " + "\n4 - Serviço Vinculado a Multimodal" ), ) @@ -3460,7 +3761,8 @@ class TcteIde(models.AbstractModel): xsd_required=True, xsd_type="TUf", help=( - "UF do início da prestação\nInformar 'EX' para operações com o " "exterior." + "UF do início da prestação\nInformar 'EX' para operações com o " + "exterior." ), ) @@ -3543,7 +3845,9 @@ class TcteIde(models.AbstractModel): ), ) - cte40_xJust = fields.Char(string="Justificativa da entrada em contingência") + cte40_xJust = fields.Char( + string="Justificativa da entrada em contingência" + ) class Toma3(models.AbstractModel): @@ -3637,6 +3941,7 @@ class TcteToma4(models.AbstractModel): class TcteCompl(models.AbstractModel): "Dados complementares da GTV-e para fins operacionais ou comerciais" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_compl" _inherit = "spec.mixin.cte" @@ -3662,7 +3967,7 @@ class TcteCompl(models.AbstractModel): cte40_xEmi = fields.Char(string="Funcionário emissor do CTe") cte40_fluxo = fields.Many2one( - comodel_name="cte.40.fluxo", + comodel_name="cte.40.tcte_fluxo", string="Previsão do fluxo da carga", help=( "Previsão do fluxo da carga\nPreenchimento obrigatório para o " @@ -3709,12 +4014,12 @@ class TcteCompl(models.AbstractModel): ) -class Fluxo(models.AbstractModel): +class TcteFluxo(models.AbstractModel): """Previsão do fluxo da carga Preenchimento obrigatório para o modal aéreo.""" _description = textwrap.dedent(" %s" % (__doc__,)) - _name = "cte.40.fluxo" + _name = "cte.40.tcte_fluxo" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.Compl.Fluxo" @@ -3731,7 +4036,9 @@ class Fluxo(models.AbstractModel): ), ) - cte40_pass = fields.One2many("cte.40.pass", "cte40_pass_fluxo_id", string="pass") + cte40_pass = fields.One2many( + "cte.40.tcte_pass", "cte40_pass_fluxo_id", string="pass" + ) cte40_xDest = fields.Char( string="Sigla ou código interno (xDest)", @@ -3748,14 +4055,14 @@ class Fluxo(models.AbstractModel): cte40_xRota = fields.Char(string="Código da Rota de Entrega") -class Pass(models.AbstractModel): +class TctePass(models.AbstractModel): _description = "pass" - _name = "cte.40.pass" + _name = "cte.40.tcte_pass" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.Compl.Fluxo.Pass" cte40_pass_fluxo_id = fields.Many2one( - comodel_name="cte.40.fluxo", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_fluxo", xsd_implicit=True, ondelete="cascade" ) cte40_xPass = fields.Char( string="Sigla ou código interno", @@ -3773,6 +4080,7 @@ class Pass(models.AbstractModel): class Entrega(models.AbstractModel): "Informações ref. a previsão de entrega" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.entrega" _inherit = "spec.mixin.cte" @@ -3784,7 +4092,8 @@ class Entrega(models.AbstractModel): choice="entrega", xsd_choice_required=True, help=( - "Entrega sem data definida\nEsta opção é proibida para o modal " "aéreo." + "Entrega sem data definida\nEsta opção é proibida para o modal " + "aéreo." ), ) @@ -3837,12 +4146,16 @@ class SemData(models.AbstractModel): SEMDATA_TPPER, string="Tipo de data/período programado", xsd_required=True, - help=("Tipo de data/período programado para entrega\n0- Sem data " "definida"), + help=( + "Tipo de data/período programado para entrega\n0- Sem data " + "definida" + ), ) class ComData(models.AbstractModel): "Entrega com data definida" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.comdata" _inherit = "spec.mixin.cte" @@ -3870,6 +4183,7 @@ class ComData(models.AbstractModel): class NoPeriodo(models.AbstractModel): "Entrega no período definido" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.noperiodo" _inherit = "spec.mixin.cte" @@ -3899,6 +4213,7 @@ class NoPeriodo(models.AbstractModel): class SemHora(models.AbstractModel): "Entrega sem hora definida" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.semhora" _inherit = "spec.mixin.cte" @@ -3914,6 +4229,7 @@ class SemHora(models.AbstractModel): class ComHora(models.AbstractModel): "Entrega com hora definida" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.comhora" _inherit = "spec.mixin.cte" @@ -3942,6 +4258,7 @@ class ComHora(models.AbstractModel): class NoInter(models.AbstractModel): "Entrega no intervalo de horário definido" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.nointer" _inherit = "spec.mixin.cte" @@ -3983,7 +4300,9 @@ class TcteObsCont(models.AbstractModel): ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TcteObsFisco(models.AbstractModel): @@ -4000,11 +4319,14 @@ class TcteObsFisco(models.AbstractModel): ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TcteEmit(models.AbstractModel): "Identificação do Emitente da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_emit" _inherit = "spec.mixin.cte" @@ -4037,7 +4359,8 @@ class TcteEmit(models.AbstractModel): ) cte40_IEST = fields.Char( - string="Inscrição Estadual", help="Inscrição Estadual do Substituto Tributário" + string="Inscrição Estadual", + help="Inscrição Estadual do Substituto Tributário", ) cte40_xNome = fields.Char( @@ -4128,6 +4451,7 @@ class TcteRem(models.AbstractModel): class Exped(models.AbstractModel): "Informações do Expedidor da Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.exped" _inherit = "spec.mixin.cte" @@ -4180,6 +4504,7 @@ class Exped(models.AbstractModel): class Receb(models.AbstractModel): "Informações do Recebedor da Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.receb" _inherit = "spec.mixin.cte" @@ -4296,7 +4621,9 @@ class TcteDest(models.AbstractModel): class TcteVPrest(models.AbstractModel): - "Valores da Prestação de Serviço" + """Valorl da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS""" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_vprest" _inherit = "spec.mixin.cte" @@ -4329,13 +4656,16 @@ class TcteVPrest(models.AbstractModel): class TcteVPrestComp(models.AbstractModel): "Componentes do Valor da Prestação" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_vprest_comp" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.VPrest.Comp" cte40_Comp_vPrest_id = fields.Many2one( - comodel_name="cte.40.tcte_vprest", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_vprest", + xsd_implicit=True, + ondelete="cascade", ) cte40_xNome = fields.Char( string="Nome do componente", @@ -4356,6 +4686,7 @@ class TcteVPrestComp(models.AbstractModel): class TcteImp(models.AbstractModel): "Informações relativas aos Impostos" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_imp" _inherit = "spec.mixin.cte" @@ -4393,7 +4724,9 @@ class TcteAutXml(models.AbstractModel): _binding_type = "Tcte.InfCte.AutXml" cte40_autXML_infCte_id = fields.Many2one( - comodel_name="cte.40.tcte_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_CNPJ = fields.Char( string="CNPJ do autorizado", @@ -4414,6 +4747,7 @@ class TcteAutXml(models.AbstractModel): class TcteInfSolicNff(models.AbstractModel): "Grupo de informações do pedido de emissão da Nota Fiscal Fácil" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infsolicnff" _inherit = "spec.mixin.cte" @@ -4432,6 +4766,7 @@ class TcteInfSolicNff(models.AbstractModel): class TcteInfPaa(models.AbstractModel): "Grupo de Informação do Provedor de Assinatura e Autorização" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infpaa" _inherit = "spec.mixin.cte" @@ -4454,6 +4789,7 @@ class TcteInfPaa(models.AbstractModel): class TctePaasignature(models.AbstractModel): "Assinatura RSA do Emitente para DFe gerados por PAA" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_paasignature" _inherit = "spec.mixin.cte" @@ -4480,13 +4816,14 @@ class TctePaasignature(models.AbstractModel): class TcteInfCteNorm(models.AbstractModel): "Grupo de informações do CT-e Normal e Substituto" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infctenorm" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm" cte40_infCarga = fields.Many2one( - comodel_name="cte.40.infcarga", + comodel_name="cte.40.tcte_infcarga", string="Informações da Carga do CT-e", xsd_required=True, ) @@ -4504,7 +4841,8 @@ class TcteInfCteNorm(models.AbstractModel): ) cte40_docAnt = fields.Many2one( - comodel_name="cte.40.docant", string="Documentos de Transporte Anterior" + comodel_name="cte.40.docant", + string="Documentos de Transporte Anterior", ) cte40_infModal = fields.Many2one( @@ -4529,7 +4867,8 @@ class TcteInfCteNorm(models.AbstractModel): ) cte40_infGlobalizado = fields.Many2one( - comodel_name="cte.40.infglobalizado", string="Informações do CT-e Globalizado" + comodel_name="cte.40.infglobalizado", + string="Informações do CT-e Globalizado", ) cte40_infServVinc = fields.Many2one( @@ -4539,10 +4878,11 @@ class TcteInfCteNorm(models.AbstractModel): ) -class InfCarga(models.AbstractModel): +class TcteInfCarga(models.AbstractModel): "Informações da Carga do CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) - _name = "cte.40.infcarga" + _name = "cte.40.tcte_infcarga" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.InfCarga" @@ -4559,8 +4899,11 @@ class InfCarga(models.AbstractModel): cte40_proPred = fields.Char( string="Produto predominante", xsd_required=True, - help=("Produto predominante\nInformar a descrição do produto " "predominante"), - ) + help=( + "Produto predominante\nInformar a descrição do produto " + "predominante" + ), + ) cte40_xOutCat = fields.Char( string="Outras características da carga", @@ -4615,7 +4958,9 @@ class TcteInfQ(models.AbstractModel): _binding_type = "Tcte.InfCte.InfCteNorm.InfCarga.InfQ" cte40_infQ_infCarga_id = fields.Many2one( - comodel_name="cte.40.infcarga", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infcarga", + xsd_implicit=True, + ondelete="cascade", ) cte40_cUnid = fields.Selection( INFQ_CUNID, @@ -4702,7 +5047,9 @@ class TcteInfNf(models.AbstractModel): _binding_type = "Tcte.InfCte.InfCteNorm.InfDoc.InfNf" cte40_infNF_infDoc_id = fields.Many2one( - comodel_name="cte.40.tcte_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_nRoma = fields.Char(string="Número do Romaneio da NF") @@ -4835,13 +5182,16 @@ class TcteInfNf(models.AbstractModel): class TcteInfNfe(models.AbstractModel): "Informações das NF-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infnfe" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.InfDoc.InfNfe" cte40_infNFe_infDoc_id = fields.Many2one( - comodel_name="cte.40.tcte_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_chave = fields.Char( string="Chave de acesso da NF-e", xsd_required=True, xsd_type="TChDFe" @@ -4891,13 +5241,16 @@ class TcteInfNfe(models.AbstractModel): class InfOutros(models.AbstractModel): "Informações dos demais documentos" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infoutros" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.InfDoc.InfOutros" cte40_infOutros_infDoc_id = fields.Many2one( - comodel_name="cte.40.tcte_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_tpDoc = fields.Selection( INFOUTROS_TPDOC, @@ -4967,6 +5320,7 @@ class InfOutros(models.AbstractModel): class DocAnt(models.AbstractModel): "Documentos de Transporte Anterior" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.docant" _inherit = "spec.mixin.cte" @@ -4981,6 +5335,7 @@ class DocAnt(models.AbstractModel): class EmiDocAnt(models.AbstractModel): "Emissor do documento anterior" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.emidocant" _inherit = "spec.mixin.cte" @@ -5027,12 +5382,16 @@ class EmiDocAnt(models.AbstractModel): "cte.40.iddocant", "cte40_idDocAnt_emiDocAnt_id", string="Informações de identificação", - help=("Informações de identificação dos documentos de Transporte " "Anterior"), + help=( + "Informações de identificação dos documentos de Transporte " + "Anterior" + ), ) class IdDocAnt(models.AbstractModel): "Informações de identificação dos documentos de Transporte Anterior" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.iddocant" _inherit = "spec.mixin.cte" @@ -5062,10 +5421,13 @@ class IdDocAnt(models.AbstractModel): class IdDocAntPap(models.AbstractModel): "Documentos de transporte anterior em papel" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.iddocantpap" _inherit = "spec.mixin.cte" - _binding_type = "Tcte.InfCte.InfCteNorm.DocAnt.EmiDocAnt.IdDocAnt.IdDocAntPap" + _binding_type = ( + "Tcte.InfCte.InfCteNorm.DocAnt.EmiDocAnt.IdDocAnt.IdDocAntPap" + ) cte40_idDocAntPap_idDocAnt_id = fields.Many2one( comodel_name="cte.40.iddocant", xsd_implicit=True, ondelete="cascade" @@ -5084,23 +5446,32 @@ class IdDocAntPap(models.AbstractModel): ), ) - cte40_serie = fields.Char(string="Série do Documento Fiscal", xsd_required=True) + cte40_serie = fields.Char( + string="Série do Documento Fiscal", xsd_required=True + ) cte40_subser = fields.Char(string="Série do Documento Fiscal (subser)") - cte40_nDoc = fields.Char(string="Número do Documento Fiscal", xsd_required=True) + cte40_nDoc = fields.Char( + string="Número do Documento Fiscal", xsd_required=True + ) cte40_dEmi = fields.Date( - string="Data de emissão (AAAA-MM-DD)", xsd_required=True, xsd_type="TData" + string="Data de emissão (AAAA-MM-DD)", + xsd_required=True, + xsd_type="TData", ) class IdDocAntEle(models.AbstractModel): "Documentos de transporte anterior eletrônicos" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.iddocantele" _inherit = "spec.mixin.cte" - _binding_type = "Tcte.InfCte.InfCteNorm.DocAnt.EmiDocAnt.IdDocAnt.IdDocAntEle" + _binding_type = ( + "Tcte.InfCte.InfCteNorm.DocAnt.EmiDocAnt.IdDocAnt.IdDocAntEle" + ) cte40_idDocAntEle_idDocAnt_id = fields.Many2one( comodel_name="cte.40.iddocant", xsd_implicit=True, ondelete="cascade" @@ -5112,6 +5483,7 @@ class IdDocAntEle(models.AbstractModel): class TcteInfModal(models.AbstractModel): "Informações do modal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infmodal" _inherit = "spec.mixin.cte" @@ -5126,13 +5498,16 @@ class TcteInfModal(models.AbstractModel): class VeicNovos(models.AbstractModel): "informações dos veículos transportados" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.veicnovos" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.VeicNovos" cte40_veicNovos_infCTeNorm_id = fields.Many2one( - comodel_name="cte.40.tcte_infctenorm", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infctenorm", + xsd_implicit=True, + ondelete="cascade", ) cte40_chassi = fields.Char(string="Chassi do veículo", xsd_required=True) @@ -5167,6 +5542,7 @@ class VeicNovos(models.AbstractModel): class TcteCobr(models.AbstractModel): "Dados da cobrança do CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_cobr" _inherit = "spec.mixin.cte" @@ -5183,6 +5559,7 @@ class TcteCobr(models.AbstractModel): class TcteFat(models.AbstractModel): "Dados da fatura" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_fat" _inherit = "spec.mixin.cte" @@ -5211,6 +5588,7 @@ class TcteFat(models.AbstractModel): class TcteDup(models.AbstractModel): "Dados das duplicatas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_dup" _inherit = "spec.mixin.cte" @@ -5236,6 +5614,7 @@ class TcteDup(models.AbstractModel): class TcteInfCteSub(models.AbstractModel): "Informações do CT-e de substituição" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infctesub" _inherit = "spec.mixin.cte" @@ -5248,12 +5627,14 @@ class TcteInfCteSub(models.AbstractModel): ) cte40_indAlteraToma = fields.Selection( - INFCTESUB_INDALTERATOMA, string="Indicador de CT-e Alteração de Tomador" + INFCTESUB_INDALTERATOMA, + string="Indicador de CT-e Alteração de Tomador", ) class InfGlobalizado(models.AbstractModel): "Informações do CT-e Globalizado" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infglobalizado" _inherit = "spec.mixin.cte" @@ -5268,6 +5649,7 @@ class InfGlobalizado(models.AbstractModel): class InfServVinc(models.AbstractModel): "Informações do Serviço Vinculado a Multimodal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infservvinc" _inherit = "spec.mixin.cte" @@ -5282,13 +5664,16 @@ class InfServVinc(models.AbstractModel): class InfCteMultimodal(models.AbstractModel): "informações do CT-e multimodal vinculado" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infctemultimodal" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.InfServVinc.InfCteMultimodal" cte40_infCTeMultimodal_infServVinc_id = fields.Many2one( - comodel_name="cte.40.infservvinc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.infservvinc", + xsd_implicit=True, + ondelete="cascade", ) cte40_chCTeMultimodal = fields.Char( string="Chave de acesso do CT-e Multimodal", @@ -5299,21 +5684,27 @@ class InfCteMultimodal(models.AbstractModel): class TcteInfCteComp(models.AbstractModel): "Detalhamento do CT-e complementado" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infctecomp" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteComp" cte40_infCteComp_infCte_id = fields.Many2one( - comodel_name="cte.40.tcte_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_chCTe = fields.Char( - string="Chave do CT-e complementado", xsd_required=True, xsd_type="TChDFe" + string="Chave do CT-e complementado", + xsd_required=True, + xsd_type="TChDFe", ) class TcteInfCteSupl(models.AbstractModel): "Informações suplementares da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infctesupl" _inherit = "spec.mixin.cte" @@ -5322,3 +5713,1331 @@ class TcteInfCteSupl(models.AbstractModel): cte40_qrCodCTe = fields.Char( string="Texto com o QR-Code impresso no DACTE", xsd_required=True ) + + +class TcteSimp(models.AbstractModel): + """Tipo Conhecimento de Transporte Eletrônico (Modelo 57) - Modelo + Simplificado""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp" + + cte40_infCte = fields.Many2one( + comodel_name="cte.40.tctesimp_infcte", + string="Informações do CT-e", + xsd_required=True, + ) + + cte40_infCTeSupl = fields.Many2one( + comodel_name="cte.40.tctesimp_infctesupl", + string="Informações suplementares do CT-e", + ) + + +class TcteSimpInfCte(models.AbstractModel): + "Informações do CT-e do tipo GTV-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infcte" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte" + + cte40_ide = fields.Many2one( + comodel_name="cte.40.tctesimp_ide", + string="Identificação do CT-e", + xsd_required=True, + ) + + cte40_compl = fields.Many2one( + comodel_name="cte.40.tctesimp_compl", + string="Dados complementares do CT-e", + help=( + "Dados complementares do CT-e para fins operacionais ou comerciais" + ), + ) + + cte40_emit = fields.Many2one( + comodel_name="cte.40.tctesimp_emit", + string="Identificação do Emitente do CT-e", + xsd_required=True, + ) + + cte40_toma = fields.Many2one( + comodel_name="cte.40.tctesimp_toma", + string="Identificação do tomador do serviço", + xsd_required=True, + choice="infcte", + xsd_choice_required=True, + help="Identificação do tomador do serviço no CT-e", + ) + + cte40_infCarga = fields.Many2one( + comodel_name="cte.40.tctesimp_infcarga", + string="Informações da Carga do CT-e", + xsd_required=True, + ) + + cte40_det = fields.One2many( + "cte.40.det", + "cte40_det_infCte_id", + string="Detalhamento das entregas / prestações", + help="Detalhamento das entregas / prestações do CTe Simplificado", + ) + + cte40_infModal = fields.Many2one( + comodel_name="cte.40.tctesimp_infmodal", + string="Informações do modal", + xsd_required=True, + ) + + cte40_cobr = fields.Many2one( + comodel_name="cte.40.tctesimp_cobr", string="Dados da cobrança do CT-e" + ) + + cte40_infCteSub = fields.Many2one( + comodel_name="cte.40.tctesimp_infctesub", + string="Informações do CT-e de substituição", + ) + + cte40_imp = fields.Many2one( + comodel_name="cte.40.tctesimp_imp", + string="Informações relativas aos Impostos", + xsd_required=True, + ) + + cte40_total = fields.Many2one( + comodel_name="cte.40.total", + string="Valores Totais do CTe", + xsd_required=True, + ) + + cte40_autXML = fields.One2many( + "cte.40.tctesimp_autxml", + "cte40_autXML_infCte_id", + string="Autorizados para download do XML do DF-e", + help=( + "Autorizados para download do XML do DF-e\nInformar CNPJ ou CPF. " + "Preencher os zeros não significativos." + ), + ) + + cte40_infRespTec = fields.Many2one( + comodel_name="cte.40.tresptec", + string="Informações do Responsável Técnico", + xsd_type="TRespTec", + help="Informações do Responsável Técnico pela emissão do DF-e", + ) + + cte40_infSolicNFF = fields.Many2one( + comodel_name="cte.40.tctesimp_infsolicnff", + string="Grupo de informações do pedido", + help=( + "Grupo de informações do pedido de emissão da Nota Fiscal Fácil" + ), + ) + + cte40_infPAA = fields.Many2one( + comodel_name="cte.40.tctesimp_infpaa", + string="Grupo de Informação do Provedor", + help="Grupo de Informação do Provedor de Assinatura e Autorização", + ) + + cte40_versao = fields.Char( + string="Versão do leiaute", + xsd_required=True, + help="Versão do leiaute\nEx: '4.00'", + ) + + cte40_Id = fields.Char( + string="Identificador da tag a ser assinada", + xsd_required=True, + help=( + "Identificador da tag a ser assinada\nInformar a chave de acesso " + "do CT-e e precedida do literal 'CTe'" + ), + ) + + +class TcteSimpIde(models.AbstractModel): + "Identificação da GTV-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_ide" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Ide" + + cte40_cUF = fields.Selection( + TCODUFIBGE, + string="Código da UF do emitente do CT-e", + xsd_required=True, + xsd_type="TCodUfIBGE", + help=("Código da UF do emitente do CT-e.\nUtilizar a Tabela do IBGE."), + ) + + cte40_cCT = fields.Char( + string="Código numérico que compõe a Chave", + xsd_required=True, + help=( + "Código numérico que compõe a Chave de Acesso.\nNúmero aleatório " + "gerado pelo emitente para cada CT-e, com o objetivo de evitar " + "acessos indevidos ao documento." + ), + ) + + cte40_CFOP = fields.Char( + string="Código Fiscal de Operações e Prestações", + xsd_required=True, + xsd_type="TCfop", + ) + + cte40_natOp = fields.Char(string="Natureza da Operação", xsd_required=True) + + cte40_mod = fields.Selection( + TMODCT, + string="Modelo do documento fiscal", + xsd_required=True, + xsd_type="TModGTVe", + help=( + "Modelo do documento fiscal\nUtilizar o código 57 para " + "identificação do CT-e, emitido em substituição aos modelos de " + "conhecimentos em papel." + ), + ) + + cte40_serie = fields.Char( + string="Série do CT-e", + xsd_required=True, + help="Série do CT-e\nPreencher com '0' no caso de série única", + ) + + cte40_nCT = fields.Char( + string="Número do CT-e", xsd_required=True, xsd_type="TNF" + ) + + cte40_dhEmi = fields.Char( + string="Data e hora de emissão do CT-e", + xsd_required=True, + help=( + "Data e hora de emissão do CT-e\nFormato AAAA-MM-DDTHH:MM:DD TZD" + ), + ) + + cte40_tpImp = fields.Selection( + IDE_TPIMP, + string="Formato de impressão do DACTE", + xsd_required=True, + help=( + "Formato de impressão do DACTE\nPreencher com: 1 - Retrato; 2 - " + "Paisagem." + ), + ) + + cte40_tpEmis = fields.Selection( + IDE_TPEMIS, + string="Forma de emissão do CT-e", + xsd_required=True, + help=( + "Forma de emissão do CT-e\nPreencher com:\n1 - Normal;\n3 - Regime" + " Especial NFF; \n4 - EPEC pela SVC; \n7 - Autorização pela SVC-" + "RS;\n8 - Autorização pela SVC-SP" + ), + ) + + cte40_cDV = fields.Char( + string="Digito Verificador da chave de acesso", + xsd_required=True, + help=( + "Digito Verificador da chave de acesso do CT-e\nInformar o dígito" + " de controle da chave de acesso do CT-e, que deve ser calculado " + "com a aplicação do algoritmo módulo 11 (base 2,9) da chave de " + "acesso." + ), + ) + + cte40_tpAmb = fields.Selection( + TAMB, + string="Tipo do Ambiente", + xsd_required=True, + xsd_type="TAmb", + help=( + "Tipo do Ambiente\nPreencher com:1 - Produção; 2 - Homologação." + ), + ) + + cte40_tpCTe = fields.Selection( + TFINCTESIMP, + string="Tipo do CT-e Simplificado", + xsd_required=True, + help=( + "Tipo do CT-e Simplificado\nPreencher com:\n4 - CTe " + "Simplificado\n5 - Substituição CTe Simplificado" + ), + ) + + cte40_procEmi = fields.Selection( + TPROCEMI, + string="Identificador do processo de emissão", + xsd_required=True, + xsd_type="TProcEmi", + help=( + "Identificador do processo de emissão do CT-e\nPreencher com: " + "\n\t\t\t\t\t\t\t\t\t\t\t0 - emissão de CT-e com aplicativo do " + "contribuinte;\n\t\t\t\t\t\t\t\t\t\t\t3- emissão CT-e pelo " + "contribuinte com aplicativo fornecido pelo SEBRAE." + ), + ) + + cte40_verProc = fields.Char( + string="Versão do processo de emissão", + xsd_required=True, + help=( + "Versão do processo de emissão\nInformar a versão do aplicativo " + "emissor de CT-e." + ), + ) + + cte40_cMunEnv = fields.Char( + string="Código do Município de envio do CT-e", + xsd_required=True, + xsd_type="TCodMunIBGE", + help=( + "Código do Município de envio do CT-e (de onde o documento foi " + "transmitido)\nUtilizar a tabela do IBGE. Informar 9999999 para as" + " operações com o exterior." + ), + ) + + cte40_xMunEnv = fields.Char( + string="Nome do Município de envio do CT-e", + xsd_required=True, + help=( + "Nome do Município de envio do CT-e (de onde o documento foi " + "transmitido)\nInformar PAIS/Municipio para as operações com o " + "exterior." + ), + ) + + cte40_UFEnv = fields.Selection( + TUF, + string="Sigla da UF de envio do CT-e", + xsd_required=True, + xsd_type="TUf", + help=( + "Sigla da UF de envio do CT-e (de onde o documento foi " + "transmitido)\nInformar 'EX' para operações com o exterior." + ), + ) + + cte40_modal = fields.Selection( + TMODTRANSPSIMP, + string="Modal", + xsd_required=True, + help=("Modal\nPreencher com:\n01-Rodoviário\n02-Aéreo\n03-Aquaviário"), + ) + + cte40_tpServ = fields.Selection( + IDE_TPSERV, + string="Tipo do Serviço", + xsd_required=True, + help=( + "Tipo do Serviço\nPreencher com: \n0 - Normal;\n1 - " + "Subcontratação;\n2 - Redespacho;" + ), + ) + + cte40_UFIni = fields.Selection( + TUF, + string="UF do início da prestação", + xsd_required=True, + xsd_type="TUf", + help=( + "UF do início da prestação\nInformar 'EX' para operações com o " + "exterior." + ), + ) + + cte40_UFFim = fields.Selection( + TUF, + string="UF do término da prestação", + xsd_required=True, + xsd_type="TUf", + help=( + "UF do término da prestação\nInformar 'EX' para operações com o " + "exterior." + ), + ) + + cte40_retira = fields.Selection( + IDE_RETIRA, + string="Indicador se o Recebedor retira", + xsd_required=True, + help=( + "Indicador se o Recebedor retira no Aeroporto, Filial, Porto ou " + "Estação de Destino?\nPreencher com: 0 - sim; 1 - não" + ), + ) + + cte40_xDetRetira = fields.Char(string="Detalhes do retira") + + cte40_dhCont = fields.Datetime( + string="Data e Hora da entrada em contingência", + xsd_type="TDateTimeUTC", + help=( + "Data e Hora da entrada em contingência\nInformar a data e hora no" + " formato AAAA-MM-DDTHH:MM:SS" + ), + ) + + cte40_xJust = fields.Char( + string="Justificativa da entrada em contingência" + ) + + +class TcteSimpCompl(models.AbstractModel): + "Dados complementares da GTV-e para fins operacionais ou comerciais" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_compl" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl" + + cte40_xCaracAd = fields.Char( + string="Característica adicional do transporte", + help=( + "Característica adicional do transporte\nTexto livre:\nREENTREGA; " + "DEVOLUÇÃO; REFATURAMENTO; etc" + ), + ) + + cte40_xCaracSer = fields.Char( + string="Característica adicional do serviço", + help=( + "Característica adicional do serviço\nTexto " + "livre:\n\t\t\t\t\t\t\t\t\t\t\tENTREGA EXPRESSA; LOGÍSTICA " + "REVERSA; CONVENCIONAL; EMERGENCIAL; etc" + ), + ) + + cte40_fluxo = fields.Many2one( + comodel_name="cte.40.tctesimp_fluxo", + string="Previsão do fluxo da carga", + help=( + "Previsão do fluxo da carga\nPreenchimento obrigatório para o " + "modal aéreo." + ), + ) + + cte40_xObs = fields.Char(string="Observações Gerais") + + cte40_obsCont = fields.One2many( + "cte.40.tctesimp_obscont", + "cte40_ObsCont_compl_id", + string="Campo de uso livre do contribuinte", + help=( + "Campo de uso livre do contribuinte\nInformar o nome do campo no " + "atributo xCampo e o conteúdo do campo no XTexto" + ), + ) + + cte40_obsFisco = fields.One2many( + "cte.40.tctesimp_obsfisco", + "cte40_ObsFisco_compl_id", + string="ObsFisco", + help=( + "Campo de uso livre do contribuinte\nInformar o nome do campo no " + "atributo xCampo e o conteúdo do campo no XTexto" + ), + ) + + +class TcteSimpFluxo(models.AbstractModel): + """Previsão do fluxo da carga + Preenchimento obrigatório para o modal aéreo.""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_fluxo" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl.Fluxo" + + cte40_xOrig = fields.Char( + string="Sigla ou código interno", + help=( + "Sigla ou código interno da Filial/Porto/Estação/ Aeroporto de " + "Origem\nObservações para o modal " + "aéreo:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t- Preenchimento obrigatório " + "para o modal aéreo.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t- O código de " + "três letras IATA do aeroporto de partida deverá ser incluído como" + " primeira anotação. Quando não for possível, utilizar a sigla " + "OACI." + ), + ) + + cte40_pass = fields.One2many( + "cte.40.tctesimp_pass", "cte40_pass_fluxo_id", string="pass" + ) + + cte40_xDest = fields.Char( + string="Sigla ou código interno (xDest)", + help=( + "Sigla ou código interno da Filial/Porto/Estação/Aeroporto de " + "Destino\nObservações para o modal " + "aéreo:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t- Preenchimento obrigatório " + "para o modal aéreo.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t- Deverá ser " + "incluído o código de três letras IATA do aeroporto de destino. " + "Quando não for possível, utilizar a sigla OACI." + ), + ) + + cte40_xRota = fields.Char(string="Código da Rota de Entrega") + + +class TcteSimpPass(models.AbstractModel): + _description = "pass" + _name = "cte.40.tctesimp_pass" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl.Fluxo.Pass" + + cte40_pass_fluxo_id = fields.Many2one( + comodel_name="cte.40.tctesimp_fluxo", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_xPass = fields.Char( + string="Sigla ou código interno", + help=( + "Sigla ou código interno da Filial/Porto/Estação/Aeroporto de " + "Passagem\nObservação para o modal " + "aéreo:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t- O código de três " + "letras IATA, referente ao aeroporto de transferência, deverá ser " + "incluído, quando for o caso. Quando não for possível, utilizar a" + " sigla OACI. Qualquer solicitação de itinerário deverá ser " + "incluída." + ), + ) + + +class TcteSimpObsCont(models.AbstractModel): + """Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_obscont" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl.ObsCont" + + cte40_ObsCont_compl_id = fields.Many2one( + comodel_name="cte.40.tctesimp_compl", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) + + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) + + +class TcteSimpObsFisco(models.AbstractModel): + """Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_obsfisco" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl.ObsFisco" + + cte40_ObsFisco_compl_id = fields.Many2one( + comodel_name="cte.40.tctesimp_compl", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) + + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) + + +class TcteSimpEmit(models.AbstractModel): + "Identificação do Emitente da GTV-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_emit" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Emit" + + cte40_CNPJ = fields.Char( + string="CNPJ do emitente", + xsd_type="TCnpj", + help="CNPJ do emitente\nInformar zeros não significativos", + ) + + cte40_CPF = fields.Char( + string="CPF do emitente", + choice="emit", + xsd_choice_required=True, + xsd_type="TCpf", + help=( + "CPF do emitente\nInformar zeros não significativos.\n\nUsar com " + "série específica 920-969 para emitente pessoa física com " + "inscrição estadual" + ), + ) + + cte40_IE = fields.Char( + string="Inscrição Estadual do Emitente", + help=( + "Inscrição Estadual do Emitente\nA IE do emitente somente ficará " + "sem informação para o caso do Regime Especial da NFF (tpEmis=3)" + ), + ) + + cte40_IEST = fields.Char( + string="Inscrição Estadual", + help="Inscrição Estadual do Substituto Tributário", + ) + + cte40_xNome = fields.Char( + string="Razão social ou Nome do emitente", xsd_required=True + ) + + cte40_xFant = fields.Char(string="Nome fantasia") + + cte40_enderEmit = fields.Many2one( + comodel_name="cte.40.tendeemi", + string="Endereço do emitente", + xsd_required=True, + xsd_type="TEndeEmi", + ) + + cte40_CRT = fields.Selection( + TCRT, + string="Código do Regime Tributário", + xsd_required=True, + xsd_type="TCRT", + help=( + "Código do Regime Tributário\nInformar: 1=Simples Nacional; " + "\n2=Simples Nacional, excesso sublimite de receita " + "bruta;\n3=Regime Normal. \n4=Simples Nacional - Microempreendedor" + " Individual – MEI." + ), + ) + + +class TcteSimpToma(models.AbstractModel): + """Indicador do "papel" do tomador do serviço no GT-e""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_toma" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Toma" + + cte40_toma = fields.Selection( + TOMA_TOMA, + string="Tomador do Serviço", + xsd_required=True, + help=( + "Tomador do Serviço\nPreencher com:\n\n0-Remetente;\n1-" + "Expedidor;\n2-Recebedor;\n3-Destinatário\n4-Terceiro" + ), + ) + + cte40_indIEToma = fields.Selection( + TOMA_INDIETOMA, + string="Indicador do papel do tomador", + xsd_required=True, + help=( + "Indicador do papel do tomador na prestação do serviço:\n1 – " + "Contribuinte ICMS;\n2 – Contribuinte isento de inscrição;\n9 – " + "Não Contribuinte\nAplica-se ao tomador que for indicado no toma3 " + "ou toma4" + ), + ) + + cte40_CNPJ = fields.Char( + string="Número do CNPJ", + choice="toma", + xsd_choice_required=True, + xsd_type="TCnpjOpc", + help=( + "Número do CNPJ\nEm caso de empresa não estabelecida no Brasil, " + "será informado o CNPJ com " + "zeros.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\nInformar os zeros não " + "significativos." + ), + ) + + cte40_CPF = fields.Char( + string="Número do CPF", + choice="toma", + xsd_choice_required=True, + xsd_type="TCpf", + help="Número do CPF\nInformar os zeros não significativos.", + ) + + cte40_IE = fields.Char( + string="Inscrição Estadual", + help=( + "Inscrição Estadual\nInformar a IE do tomador ou ISENTO se tomador" + " é contribuinte do ICMS isento de inscrição no cadastro de " + "contribuintes do ICMS. Caso o tomador não seja contribuinte do " + "ICMS não informar o conteúdo." + ), + ) + + cte40_xNome = fields.Char(string="Razão Social ou Nome", xsd_required=True) + + cte40_ISUF = fields.Char( + string="Inscrição na SUFRAMA", + help=( + "Inscrição na SUFRAMA\n(Obrigatório nas operações com as áreas com" + " benefícios de incentivos fiscais sob controle da SUFRAMA)" + ), + ) + + cte40_fone = fields.Char(string="Telefone", xsd_type="TFone") + + cte40_enderToma = fields.Many2one( + comodel_name="cte.40.tendereco", + string="Dados do endereço", + xsd_required=True, + xsd_type="TEndereco", + ) + + cte40_email = fields.Char(string="Endereço de email", xsd_type="TEmail") + + +class TcteSimpInfCarga(models.AbstractModel): + "Informações da Carga do CT-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infcarga" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfCarga" + + cte40_vCarga = fields.Monetary( + string="Valor total da carga", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + cte40_proPred = fields.Char( + string="Produto predominante", + xsd_required=True, + help=( + "Produto predominante\nInformar a descrição do produto " + "predominante" + ), + ) + + cte40_xOutCat = fields.Char( + string="Outras características da carga", + help=( + "Outras características da carga\n'FRIA', 'GRANEL', 'REFRIGERADA'," + " 'Medidas: 12X12X12'" + ), + ) + + cte40_infQ = fields.One2many( + "cte.40.tctesimp_infq", + "cte40_infQ_infCarga_id", + string="Informações de quantidades da Carga", + help=( + "Informações de quantidades da Carga do CT-e\nPara o Aéreo é " + "obrigatório o preenchimento desse campo da seguinte forma.\n1 - " + "Peso Bruto, sempre em quilogramas (obrigatório);\n2 - Peso " + "Cubado; sempre em quilogramas;\n3 - Quantidade de volumes, sempre" + " em unidades (obrigatório);\n4 - Cubagem, sempre em metros " + "cúbicos (obrigatório apenas quando for impossível preencher as " + "dimensões da(s) embalagem(ens) na tag xDime do leiaute do Aéreo)." + ), + ) + + cte40_vCargaAverb = fields.Monetary( + string="Valor da Carga para efeito de averbação", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + help=( + "Valor da Carga para efeito de averbação\nNormalmente igual ao " + "valor declarado da mercadoria, diferente por exemplo, quando a " + "mercadoria transportada é isenta de tributos nacionais para " + "exportação, onde é preciso averbar um valor maior, pois no caso " + "de indenização, o valor a ser pago será maior" + ), + ) + + +class TcteSimpInfQ(models.AbstractModel): + """Informações de quantidades da Carga do CT-e + Para o Aéreo é obrigatório o preenchimento desse campo da seguinte forma. + 1 - Peso Bruto, sempre em quilogramas (obrigatório); + 2 - Peso Cubado; sempre em quilogramas; + 3 - Quantidade de volumes, sempre em unidades (obrigatório); + 4 - Cubagem, sempre em metros cúbicos (obrigatório apenas quando for impossível + preencher as dimensões da(s) embalagem(ens) na tag xDime do leiaute do + Aéreo).""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infq" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfCarga.InfQ" + + cte40_infQ_infCarga_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infcarga", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_cUnid = fields.Selection( + INFQ_CUNID, + string="Código da Unidade de Medida", + xsd_required=True, + help=( + "Código da Unidade de Medida\nPreencher " + "com:\n00-M3;\n01-KG;\n02-TON;\n03-UNIDADE;\n04-LITROS;\n05-MMBTU" + ), + ) + + cte40_tpMed = fields.Selection( + INFQ_TPMED, + string="Tipo da Medida", + xsd_required=True, + help=( + "Tipo da Medida\nInformar com:\n00-Cubagem da NF-e\n01-Cubagem " + "Aferida pelo Transportador\n02-Peso Bruto da NF-e\n03-Peso Bruto " + "Aferido pelo Transportador\n04-Peso Cubado\n05-Peso Base do " + "Cálculo do Frete\n06-Peso para uso Operacional\n07-Caixas\n08-" + "Paletes\n09-Sacas\n10-Containers\n11-Rolos\n12-Bombonas\n13-" + "Latas\n14-Litragem\n15-Milhão de BTU (British Thermal " + "Units)\n99-Outros" + ), + ) + + cte40_qCarga = fields.Float( + string="Quantidade", + xsd_required=True, + xsd_type="TDec_1104", + digits=( + 11, + 4, + ), + ) + + +class Det(models.AbstractModel): + "Detalhamento das entregas / prestações do CTe Simplificado" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.det" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det" + + cte40_det_infCte_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infcte", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_cMunIni = fields.Char( + string="Código do Município de início", + xsd_required=True, + xsd_type="TCodMunIBGE", + help=( + "Código do Município de início da prestação\nUtilizar a tabela do " + "IBGE. Informar 9999999 para operações com o exterior." + ), + ) + + cte40_xMunIni = fields.Char( + string="Nome do Município do início da prestação", + xsd_required=True, + help=( + "Nome do Município do início da prestação\nInformar 'EXTERIOR' " + "para operações com o exterior." + ), + ) + + cte40_cMunFim = fields.Char( + string="Código do Município de término", + xsd_required=True, + xsd_type="TCodMunIBGE", + help=( + "Código do Município de término da prestação\nUtilizar a tabela do" + " IBGE. Informar 9999999 para operações com o exterior." + ), + ) + + cte40_xMunFim = fields.Char( + string="Nome do Município do término", + xsd_required=True, + help=( + "Nome do Município do término da prestação\nInformar 'EXTERIOR' " + "para operações com o exterior." + ), + ) + + cte40_vPrest = fields.Monetary( + string="Valorl da Prestação do Serviço", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + help=( + "Valorl da Prestação do Serviço\nPode conter zeros quando o CT-e " + "for de complemento de ICMS" + ), + ) + + cte40_vRec = fields.Monetary( + string="Valor a Receber", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + cte40_comp = fields.One2many( + "cte.40.tctesimp_det_comp", + "cte40_Comp_det_id", + string="Componentes do Valor da Prestação", + ) + + cte40_infNFe = fields.One2many( + "cte.40.tctesimp_infnfe", + "cte40_infNFe_det_id", + string="Informações das NF-e", + choice="det", + xsd_choice_required=True, + ) + + cte40_infDocAnt = fields.One2many( + "cte.40.infdocant", + "cte40_infDocAnt_det_id", + string="Documentos anteriores", + choice="det", + xsd_choice_required=True, + ) + + cte40_nItem = fields.Char( + string="Número identificador do item agrupador", + xsd_required=True, + help="Número identificador do item agrupador da prestação", + ) + + +class TcteSimpDetComp(models.AbstractModel): + "Componentes do Valor da Prestação" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_det_comp" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det.Comp" + + cte40_Comp_det_id = fields.Many2one( + comodel_name="cte.40.det", xsd_implicit=True, ondelete="cascade" + ) + cte40_xNome = fields.Char( + string="Nome do componente", + xsd_required=True, + help=( + "Nome do componente\nExxemplos: FRETE PESO, FRETE VALOR, SEC/CAT, " + "ADEME, AGENDAMENTO, etc" + ), + ) + + cte40_vComp = fields.Monetary( + string="Valor do componente", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + +class TcteSimpInfNfe(models.AbstractModel): + "Informações das NF-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infnfe" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det.InfNfe" + + cte40_infNFe_det_id = fields.Many2one( + comodel_name="cte.40.det", xsd_implicit=True, ondelete="cascade" + ) + cte40_chNFe = fields.Char( + string="Chave de acesso da NF-e", xsd_required=True, xsd_type="TChDFe" + ) + + cte40_PIN = fields.Char( + string="PIN SUFRAMA", + help="PIN SUFRAMA\nPIN atribuído pela SUFRAMA para a operação.", + ) + + cte40_dPrev = fields.Date( + string="Data prevista de entrega", + xsd_type="TData", + help="Data prevista de entrega\nFormato AAAA-MM-DD", + ) + + cte40_infUnidCarga = fields.One2many( + "cte.40.tunidcarga", + "cte40_infUnidCarga_infNFe_id", + string="Informações das Unidades de Carga", + choice="infnfe", + xsd_choice_required=True, + xsd_type="TUnidCarga", + help=( + "Informações das Unidades de Carga " + "(Containeres/ULD/Outros)\nDispositivo de carga utilizada (Unit " + "Load Device - ULD) significa todo tipo de contêiner de carga, " + "vagão, contêiner de avião, palete de aeronave com rede ou palete " + "de aeronave com rede sobre um iglu." + ), + ) + + cte40_infUnidTransp = fields.One2many( + "cte.40.tunidadetransp", + "cte40_infUnidTransp_infNFe_id", + string="Informações das Unidades de Transporte", + choice="infnfe", + xsd_choice_required=True, + xsd_type="TUnidadeTransp", + help=( + "Informações das Unidades de Transporte " + "(Carreta/Reboque/Vagão)\nDeve ser preenchido com as informações " + "das unidades de transporte utilizadas." + ), + ) + + +class InfDocAnt(models.AbstractModel): + "Documentos anteriores" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.infdocant" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det.InfDocAnt" + + cte40_infDocAnt_det_id = fields.Many2one( + comodel_name="cte.40.det", xsd_implicit=True, ondelete="cascade" + ) + cte40_chCTe = fields.Char( + string="Chave de acesso do CT-e", xsd_required=True, xsd_type="TChDFe" + ) + + cte40_tpPrest = fields.Selection( + INFDOCANT_TPPREST, + string="indica se a prestação é total ou parcial", + xsd_required=True, + help=( + "indica se a prestação é total ou parcial em relação as notas do " + "documento anterior\nPreencher com:\n\n1 - Total\n2 - Parcial" + ), + ) + + cte40_infNFeTranspParcial = fields.One2many( + "cte.40.infnfetranspparcial", + "cte40_infNFeTranspParcial_infDocAnt_id", + string="infNFeTranspParcial", + ) + + +class InfNfeTranspParcial(models.AbstractModel): + _description = "infNFeTranspParcial" + _name = "cte.40.infnfetranspparcial" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det.InfDocAnt.InfNfeTranspParcial" + + cte40_infNFeTranspParcial_infDocAnt_id = fields.Many2one( + comodel_name="cte.40.infdocant", xsd_implicit=True, ondelete="cascade" + ) + cte40_chNFe = fields.Char( + string="Chave de acesso da NF-e", + xsd_required=True, + xsd_type="TChDFe", + help=( + "Chave de acesso da NF-e\nInformando o tpPrest com “2 – Parcial” " + "deve-se informar as chaves de acesso das NF-e que acobertam a " + "carga transportada." + ), + ) + + +class TcteSimpInfModal(models.AbstractModel): + "Informações do modal" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infmodal" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfModal" + + cte40_versaoModal = fields.Char( + string="Versão do leiaute específico", + xsd_required=True, + help="Versão do leiaute específico para o Modal", + ) + + +class TcteSimpCobr(models.AbstractModel): + "Dados da cobrança do CT-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_cobr" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Cobr" + + cte40_fat = fields.Many2one( + comodel_name="cte.40.tctesimp_fat", string="Dados da fatura" + ) + + cte40_dup = fields.One2many( + "cte.40.tctesimp_dup", + "cte40_dup_cobr_id", + string="Dados das duplicatas", + ) + + +class TcteSimpFat(models.AbstractModel): + "Dados da fatura" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_fat" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Cobr.Fat" + + cte40_nFat = fields.Char(string="Número da fatura") + + cte40_vOrig = fields.Monetary( + string="Valor original da fatura", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + ) + + cte40_vDesc = fields.Monetary( + string="Valor do desconto da fatura", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + ) + + cte40_vLiq = fields.Monetary( + string="Valor líquido da fatura", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + ) + + +class TcteSimpDup(models.AbstractModel): + "Dados das duplicatas" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_dup" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Cobr.Dup" + + cte40_dup_cobr_id = fields.Many2one( + comodel_name="cte.40.tctesimp_cobr", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_nDup = fields.Char(string="Número da duplicata") + + cte40_dVenc = fields.Date( + string="Data de vencimento da duplicata", + xsd_type="TData", + help="Data de vencimento da duplicata (AAAA-MM-DD)", + ) + + cte40_vDup = fields.Monetary( + string="Valor da duplicata", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + ) + + +class TcteSimpInfCteSub(models.AbstractModel): + "Informações do CT-e de substituição" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infctesub" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfCteSub" + + cte40_chCte = fields.Char( + string="Chave de acesso do CT-e", + xsd_required=True, + help="Chave de acesso do CT-e a ser substituído (original)", + ) + + cte40_indAlteraToma = fields.Selection( + INFCTESUB_INDALTERATOMA, + string="Indicador de CT-e Alteração de Tomador", + ) + + +class TcteSimpImp(models.AbstractModel): + "Informações relativas aos Impostos" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_imp" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Imp" + + cte40_ICMS = fields.Many2one( + comodel_name="cte.40.timp", + string="Informações relativas ao ICMS", + xsd_required=True, + xsd_type="TImp", + ) + + cte40_vTotTrib = fields.Monetary( + string="Valor Total dos Tributos", + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + cte40_infAdFisco = fields.Char( + string="Informações adicionais de interesse", + help=( + "Informações adicionais de interesse do Fisco\nNorma referenciada," + " informações complementares, etc" + ), + ) + + +class Total(models.AbstractModel): + "Valores Totais do CTe" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.total" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Total" + + cte40_vTPrest = fields.Monetary( + string="Valor Total da Prestação do Serviço", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + help=( + "Valor Total da Prestação do Serviço\nPode conter zeros quando o " + "CT-e for de complemento de ICMS" + ), + ) + + cte40_vTRec = fields.Monetary( + string="Valor total a Receber", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + +class TcteSimpAutXml(models.AbstractModel): + """Autorizados para download do XML do DF-e + Informar CNPJ ou CPF. Preencher os zeros não significativos.""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_autxml" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.AutXml" + + cte40_autXML_infCte_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infcte", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_CNPJ = fields.Char( + string="CNPJ do autorizado", + choice="autxml", + xsd_choice_required=True, + xsd_type="TCnpj", + help="CNPJ do autorizado\nInformar zeros não significativos", + ) + + cte40_CPF = fields.Char( + string="CPF do autorizado", + choice="autxml", + xsd_choice_required=True, + xsd_type="TCpf", + help="CPF do autorizado\nInformar zeros não significativos", + ) + + +class TcteSimpInfSolicNff(models.AbstractModel): + "Grupo de informações do pedido de emissão da Nota Fiscal Fácil" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infsolicnff" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfSolicNff" + + cte40_xSolic = fields.Char( + string="Solicitação do pedido de emissão da NFF", + xsd_required=True, + help=( + "Solicitação do pedido de emissão da NFF.\nSerá preenchido com a " + "totalidade de campos informados no aplicativo emissor " + "serializado." + ), + ) + + +class TcteSimpInfPaa(models.AbstractModel): + "Grupo de Informação do Provedor de Assinatura e Autorização" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infpaa" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfPaa" + + cte40_CNPJPAA = fields.Char( + string="CNPJ do Provedor de Assinatura", + xsd_required=True, + xsd_type="TCnpj", + help="CNPJ do Provedor de Assinatura e Autorização", + ) + + cte40_PAASignature = fields.Many2one( + comodel_name="cte.40.tctesimp_paasignature", + string="Assinatura RSA do Emitente", + xsd_required=True, + help="Assinatura RSA do Emitente para DFe gerados por PAA", + ) + + +class TcteSimpPaasignature(models.AbstractModel): + "Assinatura RSA do Emitente para DFe gerados por PAA" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_paasignature" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfPaa.Paasignature" + + cte40_signatureValue = fields.Char( + string="Assinatura digital padrão RSA", + xsd_required=True, + xsd_type="xs:base64Binary", + help=( + "Assinatura digital padrão RSA\nConverter o atributo Id do DFe " + "para array de bytes e assinar com a chave privada do RSA com " + "algoritmo SHA1 gerando um valor no formato base64." + ), + ) + + cte40_RSAKeyValue = fields.Many2one( + comodel_name="cte.40.trsakeyvaluetype", + string="Chave Publica no padrão XML RSA Key", + xsd_required=True, + xsd_type="TRSAKeyValueType", + ) + + +class TcteSimpInfCteSupl(models.AbstractModel): + "Informações suplementares da GTV-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infctesupl" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCteSupl" + + cte40_qrCodCTe = fields.Char( + string="Texto com o QR-Code impresso no DACTE", xsd_required=True + ) diff --git a/l10n_br_cte_spec/models/v4_0/evento_cte_tipos_basico_v4_00.py b/l10n_br_cte_spec/models/v4_0/evento_cte_tipos_basico_v4_00.py index f79fa41e25f7..c9279805e240 100644 --- a/l10n_br_cte_spec/models/v4_0/evento_cte_tipos_basico_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/evento_cte_tipos_basico_v4_00.py @@ -25,13 +25,16 @@ class Tevento(models.AbstractModel): "Tipo Evento" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tevento" _inherit = "spec.mixin.cte" _binding_type = "Tevento" cte40_infEvento = fields.Many2one( - comodel_name="cte.40.tevento_infevento", string="infEvento", xsd_required=True + comodel_name="cte.40.tevento_infevento", + string="infEvento", + xsd_required=True, ) cte40_versao = fields.Char( @@ -142,6 +145,7 @@ class TeventoInfEvento(models.AbstractModel): class DetEvento(models.AbstractModel): "Detalhamento do evento específico" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.detevento" _inherit = "spec.mixin.cte" @@ -172,6 +176,7 @@ class TeventoInfSolicNff(models.AbstractModel): class TeventoInfPaa(models.AbstractModel): "Grupo de Informação do Provedor de Assinatura e Autorização" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tevento_infpaa" _inherit = "spec.mixin.cte" @@ -194,6 +199,7 @@ class TeventoInfPaa(models.AbstractModel): class TeventoPaasignature(models.AbstractModel): "Assinatura RSA do Emitente para DFe gerados por PAA" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tevento_paasignature" _inherit = "spec.mixin.cte" @@ -220,6 +226,7 @@ class TeventoPaasignature(models.AbstractModel): class TretEvento(models.AbstractModel): "Tipo retorno do Evento" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tretevento" _inherit = "spec.mixin.cte" @@ -309,6 +316,7 @@ class TretEventoInfEvento(models.AbstractModel): class TprocEvento(models.AbstractModel): "Tipo procEvento" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprocevento" _inherit = "spec.mixin.cte" @@ -335,7 +343,9 @@ class TprocEvento(models.AbstractModel): cte40_ipTransmissor = fields.Char( string="IP do transmissor", xsd_type="TIPv4", - help=("IP do transmissor do documento fiscal para o ambiente autorizador"), + help=( + "IP do transmissor do documento fiscal para o ambiente autorizador" + ), ) cte40_nPortaCon = fields.Char( diff --git a/l10n_br_cte_spec/models/v4_0/tipos_geral_cte_v4_00.py b/l10n_br_cte_spec/models/v4_0/tipos_geral_cte_v4_00.py index 57e85909abf1..d54b6327637b 100644 --- a/l10n_br_cte_spec/models/v4_0/tipos_geral_cte_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/tipos_geral_cte_v4_00.py @@ -194,6 +194,7 @@ class TrsakeyValueType(models.AbstractModel): "Tipo que representa uma chave publica padrão RSA" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.trsakeyvaluetype" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/readme/DESCRIPTION.rst b/l10n_br_cte_spec/readme/DESCRIPTION.rst index 22e40f1403bc..7a050013ff5e 100644 --- a/l10n_br_cte_spec/readme/DESCRIPTION.rst +++ b/l10n_br_cte_spec/readme/DESCRIPTION.rst @@ -2,11 +2,7 @@ Este módulo contem a estrutura de dados do Conhecimento de Transporte Eletrôni Este módulo não faz nada sozinho, ele precisaria de um modulo `l10n_br_cte` que mapearia esses mixins nos documentos fiscais Odoo de forma semelhante a forma como o módulo `l10n_br_nfe` faz como o módulo `l10n_br_nfe_spec`. -Este módulo inclue os principais leiautes persistantes de CT-e: - -* CT-e (Conhecimento de Transporte Eletrônico) -* CT-e OS (Conhecimento de transporte eletrônico para outros serviço - +Este módulo inclui os principais layouts persistentes do CT-e (Conhecimento de Transporte Eletrônico). Geração ~~~~~~~ @@ -18,5 +14,5 @@ https://github.com/akretion/xsdata-odoo O comando usado foi:: - export XSDATA_SCHEMA=cte; export XSDATA_VERSION=30; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese" + export XSDATA_SCHEMA=cte; export XSDATA_VERSION=40; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese" xsdata generate nfelib/cte/schemas/v4_0 --package nfelib.cte.odoo.v4_0 --output=odoo diff --git a/l10n_br_cte_spec/static/description/index.html b/l10n_br_cte_spec/static/description/index.html index b3c0f5544677..5ad76673f993 100644 --- a/l10n_br_cte_spec/static/description/index.html +++ b/l10n_br_cte_spec/static/description/index.html @@ -1,4 +1,3 @@ - @@ -9,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -275,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -301,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -373,18 +373,14 @@

cte spec

Este módulo contem a estrutura de dados do Conhecimento de Transporte Eletrônico (CT-e). Este módulo não faz nada sozinho, ele precisaria de um modulo l10n_br_cte que mapearia esses mixins nos documentos fiscais Odoo de forma semelhante a forma como o módulo l10n_br_nfe faz como o módulo l10n_br_nfe_spec.

-

Este módulo inclue os principais leiautes persistantes de CT-e:

-
    -
  • CT-e (Conhecimento de Transporte Eletrônico)
  • -
  • CT-e OS (Conhecimento de transporte eletrônico para outros serviço
  • -
+

Este módulo inclui os principais layouts persistentes do CT-e (Conhecimento de Transporte Eletrônico).

Geração

O código dos mixins Odoo desse módulo é 100% gerado a partir dos últimos esquemas xsd da Fazenda usando xsdata e essa extensão dele:

https://github.com/akretion/xsdata-odoo

O comando usado foi:

-export XSDATA_SCHEMA=cte; export XSDATA_VERSION=30; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese"
+export XSDATA_SCHEMA=cte; export XSDATA_VERSION=40; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese"
 xsdata generate nfelib/cte/schemas/v4_0 --package nfelib.cte.odoo.v4_0 --output=odoo
 
@@ -430,7 +426,9 @@

Contributors

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +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.

diff --git a/l10n_br_cte_spec/tests/test_cte_import.py b/l10n_br_cte_spec/tests/test_cte_import.py index f6ec5c7acdf3..660a51f30e59 100644 --- a/l10n_br_cte_spec/tests/test_cte_import.py +++ b/l10n_br_cte_spec/tests/test_cte_import.py @@ -6,13 +6,12 @@ import nfelib import pkg_resources -from nfelib.cte.bindings.v4_0.cte_v4_00 import Cte -from xsdata.formats.dataclass.parsers import XmlParser +from nfelib.cte.bindings.v4_0.cte_v4_00 import Tcte from odoo import api from odoo.tests import TransactionCase -from ..models import spec_models +from ..models import spec_mixin tz_datetime = re.compile(r".*[-+]0[0-9]:00$") @@ -67,6 +66,9 @@ def build_attrs_fake(self, node, create_m2o=False): key = fields[key]["related"][0] comodel_name = fields[key]["relation"] comodel = self.env.get(comodel_name) + elif fields.get(key) and fields[key].get("relation"): + comodel_name = fields[key]["relation"] + comodel = self.env.get(comodel_name) else: comodel = None for name in self.env.keys(): @@ -116,10 +118,10 @@ def match_or_create_m2o_fake(self, comodel, new_value, create_m2o=False): return comodel.new(new_value).id -# spec_models.CteSpecMixin._update_cache = _update_cache -spec_models.CteSpecMixin.build_fake = build_fake -spec_models.CteSpecMixin.build_attrs_fake = build_attrs_fake -spec_models.CteSpecMixin.match_or_create_m2o_fake = match_or_create_m2o_fake +# spec_mixin.CteSpecMixin._update_cache = _update_cache +spec_mixin.CteSpecMixin.build_fake = build_fake +spec_mixin.CteSpecMixin.build_attrs_fake = build_attrs_fake +spec_mixin.CteSpecMixin.match_or_create_m2o_fake = match_or_create_m2o_fake class CTeImportTest(TransactionCase): @@ -131,8 +133,26 @@ def test_import_cte(self): "43120178408960000182570010000000041000000047-cte.xml", ) resource_path = "/".join(res_items) - nfe_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) - parser = XmlParser() - binding = parser.from_string(nfe_stream.read().decode(), Cte) - cte = self.env["cte.40.tcte"].build_fake(binding, create=False) - self.assertEqual(cte.cte40_infCte.cte40_emit.cte40_CNPJ, "78408960000182") + cte_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) + binding = Tcte.from_xml(cte_stream.read().decode()) + cte = ( + self.env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type="in", lang="pt_BR") + .build_fake(binding.infCte, create=False) + ) + self.assertEqual(cte.cte40_emit.cte40_xNome, "KERBER E CIA. LTDA.") + + self.assertEqual(cte.cte40_ide.cte40_cCT, "00000004") + self.assertEqual( + cte.cte40_Id, "CTe43120178408960000182570010000000041000000047" + ) + + self.assertEqual(cte.cte40_emit.cte40_CNPJ, "78408960000182") + self.assertEqual(cte.cte40_receb.cte40_CNPJ, "81639791000104") + + self.assertEqual(cte.cte40_exped.cte40_CNPJ, "78408960000182") + self.assertEqual(cte.cte40_dest.cte40_CNPJ, "81639791000104") + + self.assertEqual( + cte.cte40_infCTeNorm.cte40_infCarga.cte40_proPred, "Pedra Brita" + ) diff --git a/l10n_br_fiscal/demo/company_demo.xml b/l10n_br_fiscal/demo/company_demo.xml index 224d742422c1..c8d18e20b549 100644 --- a/l10n_br_fiscal/demo/company_demo.xml +++ b/l10n_br_fiscal/demo/company_demo.xml @@ -162,6 +162,14 @@ True + + 1 + Série 1 + + + True + + 1 Série 1 @@ -202,6 +210,14 @@ True + + 1 + Série 1 + + + True + + 1 Série 1 diff --git a/l10n_br_fiscal/models/document.py b/l10n_br_fiscal/models/document.py index dfe625f30ca3..dfe3af9c1755 100644 --- a/l10n_br_fiscal/models/document.py +++ b/l10n_br_fiscal/models/document.py @@ -198,6 +198,29 @@ class Document(models.Model): default=False, ) + transport_modal = fields.Selection( + selection=[ + ("01", "Rodoviário"), + ("02", "Aéreo"), + ("03", "Aquaviário"), + ("04", "Ferroviário"), + ("05", "Dutoviário"), + ("06", "Multimodal"), + ], + string="Modal de Transporte", + ) + + service_provider = fields.Selection( + selection=[ + ("0", "Remetente"), + ("1", "Expedidor"), + ("2", "Recebedor"), + ("3", "Destinatário"), + ("4", "Outros"), + ], + string="Tomador do Serviço", + ) + @api.constrains("document_key") def _check_key(self): for record in self: diff --git a/l10n_br_fiscal/models/document_mixin_fields.py b/l10n_br_fiscal/models/document_mixin_fields.py index cdcb252e949f..d523e3496984 100644 --- a/l10n_br_fiscal/models/document_mixin_fields.py +++ b/l10n_br_fiscal/models/document_mixin_fields.py @@ -471,3 +471,41 @@ def _operation_domain(self): key_random_code = fields.Char(string="Document Key Random Code") key_check_digit = fields.Char(string="Document Key Check Digit") total_weight = fields.Float(string="Total Weight") + + ## CTE + + # commitment_date = fields.Datetime("Delivery Date") + # expected_date = fields.Datetime("Expected Date") + + # Remetente + partner_sendering_id = fields.Many2one( + "res.partner", + string="Sender Address", + help="Responsible for sending the goods, usually the issuer of the NFe.", + ) + + # Expedidor + partner_shippering_id = fields.Many2one( + "res.partner", + string="Shipper Address", + help="The one responsible for delivering the cargo to the carrier when \ + the shipment is not carried out by the sender.", + ) + + # # Destinatário + # partner_shipping_id = fields.Many2one( + # "res.partner", + # string="Recipient", + # help="The one who receives the goods at the end of the transport \ + # route, can be an individual or a company.", + # ) + + # Recebedor + partner_receivering_id = fields.Many2one( + "res.partner", + string="Receiver Address", + help="Actor who receives the goods. He is considered an intermediary \ + between the issuer and the final recipient.", + ) + + partner_insurance_id = fields.Many2one("res.partner", string="Insurance Partner") diff --git a/l10n_br_fiscal/views/document_view.xml b/l10n_br_fiscal/views/document_view.xml index 48a91d4597eb..00b7a16dd2f6 100644 --- a/l10n_br_fiscal/views/document_view.xml +++ b/l10n_br_fiscal/views/document_view.xml @@ -285,6 +285,10 @@ + + + + diff --git a/setup/l10n_br_cte/odoo/addons/l10n_br_cte b/setup/l10n_br_cte/odoo/addons/l10n_br_cte new file mode 120000 index 000000000000..a76696a2ca0b --- /dev/null +++ b/setup/l10n_br_cte/odoo/addons/l10n_br_cte @@ -0,0 +1 @@ +../../../../l10n_br_cte \ No newline at end of file diff --git a/setup/l10n_br_cte/setup.py b/setup/l10n_br_cte/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/l10n_br_cte/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/spec_driven_model/models/spec_mixin.py b/spec_driven_model/models/spec_mixin.py index ee246ece556a..fd51d9f6146b 100644 --- a/spec_driven_model/models/spec_mixin.py +++ b/spec_driven_model/models/spec_mixin.py @@ -124,7 +124,8 @@ def _register_hook(self): filter( lambda x: (x.startswith(field_prefix) and "_choice" not in x), fields, - ) + ), + None, ) model_type = type( name,