From 796404321aa5abf512939a5920f231f300f1b082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Fri, 19 May 2023 17:18:31 -0300 Subject: [PATCH 01/72] [ADD] l10n_br_mdfe_spec generated by xsdata-odoo --- l10n_br_mdfe_spec/static/description/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l10n_br_mdfe_spec/static/description/index.html b/l10n_br_mdfe_spec/static/description/index.html index 1cd0f667e9d1..7054961877a9 100644 --- a/l10n_br_mdfe_spec/static/description/index.html +++ b/l10n_br_mdfe_spec/static/description/index.html @@ -9,12 +9,12 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. -See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to customize this style sheet. */ From 6b5faa5da62461380d10f65ccbcb85a69973f222 Mon Sep 17 00:00:00 2001 From: Ygor Carvalho Date: Wed, 12 Jul 2023 14:57:53 -0300 Subject: [PATCH 02/72] [ADD] l10n_br_mdfe --- l10n_br_mdfe/README.rst | 119 +++++ l10n_br_mdfe/__init__.py | 2 + l10n_br_mdfe/__manifest__.py | 35 ++ l10n_br_mdfe/hooks.py | 21 + l10n_br_mdfe/models/__init__.py | 4 + l10n_br_mdfe/models/document.py | 78 ++++ l10n_br_mdfe/models/document_related.py | 23 + l10n_br_mdfe/models/res_company.py | 16 + l10n_br_mdfe/models/res_partner.py | 21 + l10n_br_mdfe/readme/CONFIGURE.rst | 10 + l10n_br_mdfe/readme/CONTRIBUTORS.rst | 1 + l10n_br_mdfe/readme/DESCRIPTION.rst | 4 + l10n_br_mdfe/readme/USAGE.rst | 11 + l10n_br_mdfe/static/description/icon.png | Bin 0 -> 9455 bytes l10n_br_mdfe/static/description/index.html | 461 ++++++++++++++++++++ l10n_br_mdfe/views/document.xml | 17 + l10n_br_mdfe/views/document_related.xml | 17 + l10n_br_mdfe/views/res_company.xml | 17 + l10n_br_mdfe/views/res_partner.xml | 17 + setup/l10n_br_mdfe/odoo/addons/l10n_br_mdfe | 1 + setup/l10n_br_mdfe/setup.py | 6 + 21 files changed, 881 insertions(+) create mode 100644 l10n_br_mdfe/README.rst create mode 100644 l10n_br_mdfe/__init__.py create mode 100644 l10n_br_mdfe/__manifest__.py create mode 100644 l10n_br_mdfe/hooks.py create mode 100644 l10n_br_mdfe/models/__init__.py create mode 100644 l10n_br_mdfe/models/document.py create mode 100644 l10n_br_mdfe/models/document_related.py create mode 100644 l10n_br_mdfe/models/res_company.py create mode 100644 l10n_br_mdfe/models/res_partner.py create mode 100644 l10n_br_mdfe/readme/CONFIGURE.rst create mode 100644 l10n_br_mdfe/readme/CONTRIBUTORS.rst create mode 100644 l10n_br_mdfe/readme/DESCRIPTION.rst create mode 100644 l10n_br_mdfe/readme/USAGE.rst create mode 100644 l10n_br_mdfe/static/description/icon.png create mode 100644 l10n_br_mdfe/static/description/index.html create mode 100644 l10n_br_mdfe/views/document.xml create mode 100644 l10n_br_mdfe/views/document_related.xml create mode 100644 l10n_br_mdfe/views/res_company.xml create mode 100644 l10n_br_mdfe/views/res_partner.xml create mode 120000 setup/l10n_br_mdfe/odoo/addons/l10n_br_mdfe create mode 100644 setup/l10n_br_mdfe/setup.py diff --git a/l10n_br_mdfe/README.rst b/l10n_br_mdfe/README.rst new file mode 100644 index 000000000000..86cc403d4d62 --- /dev/null +++ b/l10n_br_mdfe/README.rst @@ -0,0 +1,119 @@ +==== +MDFe +==== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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_mdfe + :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_mdfe + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/124/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +[ This file must be max 2-3 paragraphs, and is required. ] + +This module extends the functionality of ... to support ... +and to allow you to ... + + +.. 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 +============= + +[ This file is optional, it should explain how to configure + the module before using it; it is aimed at advanced users. ] + +To configure this module, you need to: + +#. Go to ... + +.. figure:: https://raw.githubusercontent.com/OCA/l10n-brazil/14.0/l10n_br_mdfe/static/description/image.png + :alt: alternative description + :width: 600 px + +Usage +===== + +[ This file must be present and contains the usage instructions + for end-users. As all other rst files included in the README, + it MUST NOT contain reStructuredText sections + only body text (paragraphs, lists, tables, etc). Should you need + a more elaborate structure to explain the addon, please create a + Sphinx documentation (which may include this file as a "quick start" + section). ] + +To use this module, you need to: + +#. Go to ... + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* KMEE + +Contributors +~~~~~~~~~~~~ + +* Ygor Carvalho + +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-ygcarvalh| image:: https://github.com/ygcarvalh.png?size=40px + :target: https://github.com/ygcarvalh + :alt: ygcarvalh + +Current `maintainer `__: + +|maintainer-ygcarvalh| + +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_mdfe/__init__.py b/l10n_br_mdfe/__init__.py new file mode 100644 index 000000000000..cc6b6354ad8f --- /dev/null +++ b/l10n_br_mdfe/__init__.py @@ -0,0 +1,2 @@ +from . import models +from .hooks import post_init_hook diff --git a/l10n_br_mdfe/__manifest__.py b/l10n_br_mdfe/__manifest__.py new file mode 100644 index 000000000000..2dc7b26b73eb --- /dev/null +++ b/l10n_br_mdfe/__manifest__.py @@ -0,0 +1,35 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "MDFe", + "summary": """Brazilian Eletronic Invoice MDF-e""", + "version": "14.0.1.0.0", + "category": "Localisation", + "license": "AGPL-3", + "author": "KMEE,Odoo Community Association (OCA)", + "maintainers": ["ygcarvalh"], + "website": "https://github.com/OCA/l10n-brazil", + "development_status": "Alpha", + "depends": [ + "l10n_br_fiscal", + "l10n_br_mdfe_spec", + "spec_driven_model", + ], + "data": [ + # 'views/document_related.xml', + # 'views/res_partner.xml', + # 'views/res_company.xml', + # 'views/document.xml', + ], + "post_init_hook": "post_init_hook", + "installable": True, + "auto_install": False, + "external_dependencies": { + "python": [ + "nfelib>=2.0.0", + "erpbrasil.transmissao", + "erpbrasil.edoc", + ] + }, +} diff --git a/l10n_br_mdfe/hooks.py b/l10n_br_mdfe/hooks.py new file mode 100644 index 000000000000..ce00f97172cc --- /dev/null +++ b/l10n_br_mdfe/hooks.py @@ -0,0 +1,21 @@ +# Copyright (C) 2019-2020 - Raphael Valyi Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from odoo import SUPERUSER_ID, api + +from odoo.addons.spec_driven_model import hooks + + +def post_init_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + hooks.register_hook( + env, + "l10n_br_mdfe", + "odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_tipos_basico_v3_00", + ) + + hooks.post_init_hook( + cr, + registry, + "l10n_br_mdfe", + "odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_tipos_basico_v3_00", + ) diff --git a/l10n_br_mdfe/models/__init__.py b/l10n_br_mdfe/models/__init__.py new file mode 100644 index 000000000000..2e8e1cd4b724 --- /dev/null +++ b/l10n_br_mdfe/models/__init__.py @@ -0,0 +1,4 @@ +from . import document +from . import res_company +from . import res_partner +from . import document_related diff --git a/l10n_br_mdfe/models/document.py b/l10n_br_mdfe/models/document.py new file mode 100644 index 000000000000..51dd422747aa --- /dev/null +++ b/l10n_br_mdfe/models/document.py @@ -0,0 +1,78 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import re + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + + +def filtered_processador_edoc_mdfe(record): + if record.processador_edoc == "oca" and record.document_type_id.code in ["57"]: + return True + return False + + +class MDFe(spec_models.StackedModel): + + _name = "l10n_br_fiscal.document" + _inherit = ["l10n_br_fiscal.document", "mdfe.30.tmdfe_infmdfe"] + _stacked = "mdfe.30.tmdfe_infmdfe" + _field_prefix = "mdfe30_" + _schame_name = "mdfe" + _schema_version = "3.0.0" + _odoo_module = "l10n_br_mdfe" + _spec_module = "odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_tipos_basico_v3_00" + _spec_tab_name = "MDFe" + _mdfe_search_keys = ["mdfe30_Id"] + + INFMDFE_TREE = """ + > + > + - res.company + - + - l10n_br_fiscal.document.related + > + """ + + ########################## + # MDF-e spec related fields + ########################## + + ########################## + # MDF-e tag: infMDFe + ########################## + + mdfe30_versao = fields.Char(related="document_version") + + mdfe30_Id = fields.Char( + compute="_compute_mdfe40_id_tag", + inverse="_inverse_mdfe40_id_tag", + ) + + ########################## + # MDF-e tag: id + # Methods + ########################## + + @api.depends("document_type_id", "document_key") + def _compute_mdfe40_id_tag(self): + """Set schema data which are not just related fields""" + + for record in self.filtered(filtered_processador_edoc_mdfe): + if ( + record.document_type_id + and record.document_type.prefix + and record.document_key + ): + record.mdfe30_Id = "{}{}".format( + record.document_type.prefix, record.document_key + ) + else: + record.mdfe30_Id = False + + def _inverse_mdfe40_id_tag(self): + for record in self: + if record.cte40_Id: + record.document_key = re.findall(r"\d+", str(record.mdfe30_Id))[0] diff --git a/l10n_br_mdfe/models/document_related.py b/l10n_br_mdfe/models/document_related.py new file mode 100644 index 000000000000..b840dbf339ec --- /dev/null +++ b/l10n_br_mdfe/models/document_related.py @@ -0,0 +1,23 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.spec_driven_model.models import spec_models + + +class MDFeRelated(spec_models.StackedModel): + + _name = "l10n_br_fiscal.document.related" + _inherit = [ + "l10n_br_fiscal.document.related", + "mdfe.30.tmdfe_infnfe", + "mdfe.30.infcte", + ] + _stacked = "mdfe.30.tmdfe_infnfe" + _field_prefix = "cte30_" + _schema_name = "mdfe" + _schema_version = "3.0.0" + _odoo_module = "l10n_br_mdfe" + _spec_module = "odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_tipos_basico_v3_00" + _spec_tab_name = "MDFe" + + # TODO diff --git a/l10n_br_mdfe/models/res_company.py b/l10n_br_mdfe/models/res_company.py new file mode 100644 index 000000000000..6ebed5021687 --- /dev/null +++ b/l10n_br_mdfe/models/res_company.py @@ -0,0 +1,16 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.spec_driven_model.models import spec_models + + +class ResCompany(spec_models.SpecModel): + + _name = "res.company" + _inherit = [ + "res.company", + "mdfe.30.emi", + ] + _mdfe_search_keys = ["mdfe30_CNPJ", "mdfe30_xNome", "mdfe_xFant"] + + # TODO diff --git a/l10n_br_mdfe/models/res_partner.py b/l10n_br_mdfe/models/res_partner.py new file mode 100644 index 000000000000..ac77102f122d --- /dev/null +++ b/l10n_br_mdfe/models/res_partner.py @@ -0,0 +1,21 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.spec_driven_model.models import spec_models + + +class ResPartner(spec_models.SpecModel): + + _name = "res.partner" + _inherit = [ + "res.partner", + "mdfe.30.tendereco", + "mdfe.30.tlocal", + "mdfe.30.tendeemi", + "mdfe.30.dest", + "mdfe.30.tresptec", + "mdfe.30.autxml", + ] + _mdfe_search_keys = ["mdfe30_CNPJ", "mdfe30_CPF", "mdfe_xNome"] + + # TODO diff --git a/l10n_br_mdfe/readme/CONFIGURE.rst b/l10n_br_mdfe/readme/CONFIGURE.rst new file mode 100644 index 000000000000..754e51aeff53 --- /dev/null +++ b/l10n_br_mdfe/readme/CONFIGURE.rst @@ -0,0 +1,10 @@ +[ This file is optional, it should explain how to configure + the module before using it; it is aimed at advanced users. ] + +To configure this module, you need to: + +#. Go to ... + +.. figure:: ../static/description/image.png + :alt: alternative description + :width: 600 px diff --git a/l10n_br_mdfe/readme/CONTRIBUTORS.rst b/l10n_br_mdfe/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..957041454b69 --- /dev/null +++ b/l10n_br_mdfe/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Ygor Carvalho diff --git a/l10n_br_mdfe/readme/DESCRIPTION.rst b/l10n_br_mdfe/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..43cf54b776e0 --- /dev/null +++ b/l10n_br_mdfe/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +[ This file must be max 2-3 paragraphs, and is required. ] + +This module extends the functionality of ... to support ... +and to allow you to ... diff --git a/l10n_br_mdfe/readme/USAGE.rst b/l10n_br_mdfe/readme/USAGE.rst new file mode 100644 index 000000000000..f4629c3d548a --- /dev/null +++ b/l10n_br_mdfe/readme/USAGE.rst @@ -0,0 +1,11 @@ +[ This file must be present and contains the usage instructions + for end-users. As all other rst files included in the README, + it MUST NOT contain reStructuredText sections + only body text (paragraphs, lists, tables, etc). Should you need + a more elaborate structure to explain the addon, please create a + Sphinx documentation (which may include this file as a "quick start" + section). ] + +To use this module, you need to: + +#. Go to ... diff --git a/l10n_br_mdfe/static/description/icon.png b/l10n_br_mdfe/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 zcmW++2RxMjAAjx~&dlBk9S+%}OXg)AGE&Cb*&}d0jUxM@u(PQx^-s)697TX`ehR4?GS^qbkof1cslKgkU)h65qZ9Oc=ml_0temigYLJfnz{IDzUf>bGs4N!v3=Z3jMq&A#7%rM5eQ#dc?k~! zVpnB`o+K7|Al`Q_U;eD$B zfJtP*jH`siUq~{KE)`jP2|#TUEFGRryE2`i0**z#*^6~AI|YzIWy$Cu#CSLW3q=GA z6`?GZymC;dCPk~rBS%eCb`5OLr;RUZ;D`}um=H)BfVIq%7VhiMr)_#G0N#zrNH|__ zc+blN2UAB0=617@>_u;MPHN;P;N#YoE=)R#i$k_`UAA>WWCcEVMh~L_ zj--gtp&|K1#58Yz*AHCTMziU1Jzt_jG0I@qAOHsk$2}yTmVkBp_eHuY$A9)>P6o~I z%aQ?!(GqeQ-Y+b0I(m9pwgi(IIZZzsbMv+9w{PFtd_<_(LA~0H(xz{=FhLB@(1&qHA5EJw1>>=%q2f&^X>IQ{!GJ4e9U z&KlB)z(84HmNgm2hg2C0>WM{E(DdPr+EeU_N@57;PC2&DmGFW_9kP&%?X4}+xWi)( z;)z%wI5>D4a*5XwD)P--sPkoY(a~WBw;E~AW`Yue4kFa^LM3X`8x|}ZUeMnqr}>kH zG%WWW>3ml$Yez?i%)2pbKPI7?5o?hydokgQyZsNEr{a|mLdt;X2TX(#B1j35xPnPW z*bMSSOauW>o;*=kO8ojw91VX!qoOQb)zHJ!odWB}d+*K?#sY_jqPdg{Sm2HdYzdEx zOGVPhVRTGPtv0o}RfVP;Nd(|CB)I;*t&QO8h zFfekr30S!-LHmV_Su-W+rEwYXJ^;6&3|L$mMC8*bQptyOo9;>Qb9Q9`ySe3%V$A*9 zeKEe+b0{#KWGp$F+tga)0RtI)nhMa-K@JS}2krK~n8vJ=Ngm?R!9G<~RyuU0d?nz# z-5EK$o(!F?hmX*2Yt6+coY`6jGbb7tF#6nHA zuKk=GGJ;ZwON1iAfG$E#Y7MnZVmrY|j0eVI(DN_MNFJmyZ|;w4tf@=CCDZ#5N_0K= z$;R~bbk?}TpfDjfB&aiQ$VA}s?P}xPERJG{kxk5~R`iRS(SK5d+Xs9swCozZISbnS zk!)I0>t=A<-^z(cmSFz3=jZ23u13X><0b)P)^1T_))Kr`e!-pb#q&J*Q`p+B6la%C zuVl&0duN<;uOsB3%T9Fp8t{ED108<+W(nOZd?gDnfNBC3>M8WE61$So|P zVvqH0SNtDTcsUdzaMDpT=Ty0pDHHNL@Z0w$Y`XO z2M-_r1S+GaH%pz#Uy0*w$Vdl=X=rQXEzO}d6J^R6zjM1u&c9vYLvLp?W7w(?np9x1 zE_0JSAJCPB%i7p*Wvg)pn5T`8k3-uR?*NT|J`eS#_#54p>!p(mLDvmc-3o0mX*mp_ zN*AeS<>#^-{S%W<*mz^!X$w_2dHWpcJ6^j64qFBft-o}o_Vx80o0>}Du;>kLts;$8 zC`7q$QI(dKYG`Wa8#wl@V4jVWBRGQ@1dr-hstpQL)Tl+aqVpGpbSfN>5i&QMXfiZ> zaA?T1VGe?rpQ@;+pkrVdd{klI&jVS@I5_iz!=UMpTsa~mBga?1r}aRBm1WS;TT*s0f0lY=JBl66Upy)-k4J}lh=P^8(SXk~0xW=T9v*B|gzIhN z>qsO7dFd~mgxAy4V?&)=5ieYq?zi?ZEoj)&2o)RLy=@hbCRcfT5jigwtQGE{L*8<@Yd{zg;CsL5mvzfDY}P-wos_6PfprFVaeqNE%h zKZhLtcQld;ZD+>=nqN~>GvROfueSzJD&BE*}XfU|H&(FssBqY=hPCt`d zH?@s2>I(|;fcW&YM6#V#!kUIP8$Nkdh0A(bEVj``-AAyYgwY~jB zT|I7Bf@%;7aL7Wf4dZ%VqF$eiaC38OV6oy3Z#TER2G+fOCd9Iaoy6aLYbPTN{XRPz z;U!V|vBf%H!}52L2gH_+j;`bTcQRXB+y9onc^wLm5wi3-Be}U>k_u>2Eg$=k!(l@I zcCg+flakT2Nej3i0yn+g+}%NYb?ta;R?(g5SnwsQ49U8Wng8d|{B+lyRcEDvR3+`O{zfmrmvFrL6acVP%yG98X zo&+VBg@px@i)%o?dG(`T;n*$S5*rnyiR#=wW}}GsAcfyQpE|>a{=$Hjg=-*_K;UtD z#z-)AXwSRY?OPefw^iI+ z)AXz#PfEjlwTes|_{sB?4(O@fg0AJ^g8gP}ex9Ucf*@_^J(s_5jJV}c)s$`Myn|Kd z$6>}#q^n{4vN@+Os$m7KV+`}c%4)4pv@06af4-x5#wj!KKb%caK{A&Y#Rfs z-po?Dcb1({W=6FKIUirH&(yg=*6aLCekcKwyfK^JN5{wcA3nhO(o}SK#!CINhI`-I z1)6&n7O&ZmyFMuNwvEic#IiOAwNkR=u5it{B9n2sAJV5pNhar=j5`*N!Na;c7g!l$ z3aYBqUkqqTJ=Re-;)s!EOeij=7SQZ3Hq}ZRds%IM*PtM$wV z@;rlc*NRK7i3y5BETSKuumEN`Xu_8GP1Ri=OKQ$@I^ko8>H6)4rjiG5{VBM>B|%`&&s^)jS|-_95&yc=GqjNo{zFkw%%HHhS~e=s zD#sfS+-?*t|J!+ozP6KvtOl!R)@@-z24}`9{QaVLD^9VCSR2b`b!KC#o;Ki<+wXB6 zx3&O0LOWcg4&rv4QG0)4yb}7BFSEg~=IR5#ZRj8kg}dS7_V&^%#Do==#`u zpy6{ox?jWuR(;pg+f@mT>#HGWHAJRRDDDv~@(IDw&R>9643kK#HN`!1vBJHnC+RM&yIh8{gG2q zA%e*U3|N0XSRa~oX-3EAneep)@{h2vvd3Xvy$7og(sayr@95+e6~Xvi1tUqnIxoIH zVWo*OwYElb#uyW{Imam6f2rGbjR!Y3`#gPqkv57dB6K^wRGxc9B(t|aYDGS=m$&S!NmCtrMMaUg(c zc2qC=2Z`EEFMW-me5B)24AqF*bV5Dr-M5ig(l-WPS%CgaPzs6p_gnCIvTJ=Y<6!gT zVt@AfYCzjjsMEGi=rDQHo0yc;HqoRNnNFeWZgcm?f;cp(6CNylj36DoL(?TS7eU#+ z7&mfr#y))+CJOXQKUMZ7QIdS9@#-}7y2K1{8)cCt0~-X0O!O?Qx#E4Og+;A2SjalQ zs7r?qn0H044=sDN$SRG$arw~n=+T_DNdSrarmu)V6@|?1-ZB#hRn`uilTGPJ@fqEy zGt(f0B+^JDP&f=r{#Y_wi#AVDf-y!RIXU^0jXsFpf>=Ji*TeqSY!H~AMbJdCGLhC) zn7Rx+sXw6uYj;WRYrLd^5IZq@6JI1C^YkgnedZEYy<&4(z%Q$5yv#Boo{AH8n$a zhb4Y3PWdr269&?V%uI$xMcUrMzl=;w<_nm*qr=c3Rl@i5wWB;e-`t7D&c-mcQl7x! zZWB`UGcw=Y2=}~wzrfLx=uet<;m3~=8I~ZRuzvMQUQdr+yTV|ATf1Uuomr__nDf=X zZ3WYJtHp_ri(}SQAPjv+Y+0=fH4krOP@S&=zZ-t1jW1o@}z;xk8 z(Nz1co&El^HK^NrhVHa-_;&88vTU>_J33=%{if;BEY*J#1n59=07jrGQ#IP>@u#3A z;!q+E1Rj3ZJ+!4bq9F8PXJ@yMgZL;>&gYA0%_Kbi8?S=XGM~dnQZQ!yBSgcZhY96H zrWnU;k)qy`rX&&xlDyA%(a1Hhi5CWkmg(`Gb%m(HKi-7Z!LKGRP_B8@`7&hdDy5n= z`OIxqxiVfX@OX1p(mQu>0Ai*v_cTMiw4qRt3~NBvr9oBy0)r>w3p~V0SCm=An6@3n)>@z!|o-$HvDK z|3D2ZMJkLE5loMKl6R^ez@Zz%S$&mbeoqH5`Bb){Ei21q&VP)hWS2tjShfFtGE+$z zzCR$P#uktu+#!w)cX!lWN1XU%K-r=s{|j?)Akf@q#3b#{6cZCuJ~gCxuMXRmI$nGtnH+-h z+GEi!*X=AP<|fG`1>MBdTb?28JYc=fGvAi2I<$B(rs$;eoJCyR6_bc~p!XR@O-+sD z=eH`-ye})I5ic1eL~TDmtfJ|8`0VJ*Yr=hNCd)G1p2MMz4C3^Mj?7;!w|Ly%JqmuW zlIEW^Ft%z?*|fpXda>Jr^1noFZEwFgVV%|*XhH@acv8rdGxeEX{M$(vG{Zw+x(ei@ zmfXb22}8-?Fi`vo-YVrTH*C?a8%M=Hv9MqVH7H^J$KsD?>!SFZ;ZsvnHr_gn=7acz z#W?0eCdVhVMWN12VV^$>WlQ?f;P^{(&pYTops|btm6aj>_Uz+hqpGwB)vWp0Cf5y< zft8-je~nn?W11plq}N)4A{l8I7$!ks_x$PXW-2XaRFswX_BnF{R#6YIwMhAgd5F9X zGmwdadS6(a^fjHtXg8=l?Rc0Sm%hk6E9!5cLVloEy4eh(=FwgP`)~I^5~pBEWo+F6 zSf2ncyMurJN91#cJTy_u8Y}@%!bq1RkGC~-bV@SXRd4F{R-*V`bS+6;W5vZ(&+I<9$;-V|eNfLa5n-6% z2(}&uGRF;p92eS*sE*oR$@pexaqr*meB)VhmIg@h{uzkk$9~qh#cHhw#>O%)b@+(| z^IQgqzuj~Sk(J;swEM-3TrJAPCq9k^^^`q{IItKBRXYe}e0Tdr=Huf7da3$l4PdpwWDop%^}n;dD#K4s#DYA8SHZ z&1!riV4W4R7R#C))JH1~axJ)RYnM$$lIR%6fIVA@zV{XVyx}C+a-Dt8Y9M)^KU0+H zR4IUb2CJ{Hg>CuaXtD50jB(_Tcx=Z$^WYu2u5kubqmwp%drJ6 z?Fo40g!Qd<-l=TQxqHEOuPX0;^z7iX?Ke^a%XT<13TA^5`4Xcw6D@Ur&VT&CUe0d} z1GjOVF1^L@>O)l@?bD~$wzgf(nxX1OGD8fEV?TdJcZc2KoUe|oP1#=$$7ee|xbY)A zDZq+cuTpc(fFdj^=!;{k03C69lMQ(|>uhRfRu%+!k&YOi-3|1QKB z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/l10n_br_mdfe/static/description/index.html b/l10n_br_mdfe/static/description/index.html new file mode 100644 index 000000000000..267913f36a41 --- /dev/null +++ b/l10n_br_mdfe/static/description/index.html @@ -0,0 +1,461 @@ + + + + + + +MDFe + + + +
+

MDFe

+ + +

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

+

[ This file must be max 2-3 paragraphs, and is required. ]

+

This module extends the functionality of … to support … +and to allow you to …

+
+

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

+
+
[ This file is optional, it should explain how to configure
+
the module before using it; it is aimed at advanced users. ]
+
+

To configure this module, you need to:

+
    +
  1. Go to …
  2. +
+
+alternative description +
+
+
+

Usage

+
+
[ This file must be present and contains the usage instructions
+
for end-users. As all other rst files included in the README, +it MUST NOT contain reStructuredText sections +only body text (paragraphs, lists, tables, etc). Should you need +a more elaborate structure to explain the addon, please create a +Sphinx documentation (which may include this file as a “quick start” +section). ]
+
+

To use this module, you need to:

+
    +
  1. Go to …
  2. +
+
+
+

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 smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • KMEE
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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

+

Current maintainer:

+

ygcarvalh

+

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_mdfe/views/document.xml b/l10n_br_mdfe/views/document.xml new file mode 100644 index 000000000000..9cde23c5511b --- /dev/null +++ b/l10n_br_mdfe/views/document.xml @@ -0,0 +1,17 @@ + + + + + + document.form (in l10n_br_mdfe) + document + + + + + + + + + diff --git a/l10n_br_mdfe/views/document_related.xml b/l10n_br_mdfe/views/document_related.xml new file mode 100644 index 000000000000..d5f060fd15cd --- /dev/null +++ b/l10n_br_mdfe/views/document_related.xml @@ -0,0 +1,17 @@ + + + + + + document.related.form (in l10n_br_mdfe) + document.related + + + + + + + + + diff --git a/l10n_br_mdfe/views/res_company.xml b/l10n_br_mdfe/views/res_company.xml new file mode 100644 index 000000000000..a9f0587d68df --- /dev/null +++ b/l10n_br_mdfe/views/res_company.xml @@ -0,0 +1,17 @@ + + + + + + res.company.form (in l10n_br_mdfe) + res.company + + + + + + + + + diff --git a/l10n_br_mdfe/views/res_partner.xml b/l10n_br_mdfe/views/res_partner.xml new file mode 100644 index 000000000000..3cf7e85d5634 --- /dev/null +++ b/l10n_br_mdfe/views/res_partner.xml @@ -0,0 +1,17 @@ + + + + + + res.partner.form (in l10n_br_mdfe) + res.partner + + + + + + + + + diff --git a/setup/l10n_br_mdfe/odoo/addons/l10n_br_mdfe b/setup/l10n_br_mdfe/odoo/addons/l10n_br_mdfe new file mode 120000 index 000000000000..4b3c90b7dcb2 --- /dev/null +++ b/setup/l10n_br_mdfe/odoo/addons/l10n_br_mdfe @@ -0,0 +1 @@ +../../../../l10n_br_mdfe \ No newline at end of file diff --git a/setup/l10n_br_mdfe/setup.py b/setup/l10n_br_mdfe/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/l10n_br_mdfe/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 475bc2cace5a60367a6f9735cd3ff856a00a1b37 Mon Sep 17 00:00:00 2001 From: Ygor Carvalho Date: Wed, 12 Jul 2023 15:50:28 -0300 Subject: [PATCH 03/72] [FIX] l10n_br_mdfe: set correct code for document type --- l10n_br_mdfe/models/document.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_br_mdfe/models/document.py b/l10n_br_mdfe/models/document.py index 51dd422747aa..f9327a256379 100644 --- a/l10n_br_mdfe/models/document.py +++ b/l10n_br_mdfe/models/document.py @@ -9,7 +9,7 @@ def filtered_processador_edoc_mdfe(record): - if record.processador_edoc == "oca" and record.document_type_id.code in ["57"]: + if record.processador_edoc == "oca" and record.document_type_id.code in ["58"]: return True return False From 5967ba1166beafa0c86d5db375378c65aa1c0701 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Wed, 6 Sep 2023 10:27:23 -0300 Subject: [PATCH 04/72] [ADD] MDFe validations --- l10n_br_fiscal/constants/fiscal.py | 1 + l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/l10n_br_fiscal/constants/fiscal.py b/l10n_br_fiscal/constants/fiscal.py index e936a7371c12..ad1bf6335308 100644 --- a/l10n_br_fiscal/constants/fiscal.py +++ b/l10n_br_fiscal/constants/fiscal.py @@ -328,6 +328,7 @@ MODELO_FISCAL_NFE = "55" MODELO_FISCAL_NFCE = "65" MODELO_FISCAL_NFSE = "SE" +MODELO_FISCAL_MDFE = "58" MODELO_FISCAL_CFE = "59" MODELO_FISCAL_CUPOM_FISCAL_ECF = "2D" MODELO_FISCAL_CTE = "57" diff --git a/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv b/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv index 97630d38ce40..13f7d5252256 100644 --- a/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv +++ b/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv @@ -23,7 +23,6 @@ "document_22","22","Nota Fiscal de Serviço de Telecomunicação","False",,,"icms" "document_23","23","GNRE","False",,,"icms" "document_24","24","Autorização de Carregamento e Transporte","False",,,"icms" -"document_25","25","Manifesto de Carga","False","MDFe","mdf-e","icms" "document_26","26","Conhecimento de Transporte Multimodal de Cargas","False",,,"icms" "document_27","27","Nota Fiscal de Transporte Ferroviário de Cargas","False",,,"icms" "document_28","28","Nota Fiscal/Conta de Fornecimento de Gás Canalizado","False",,,"icms" @@ -31,6 +30,7 @@ "document_30","30","Bilhete/Recibo do Passageiro","False",,,"icms" "document_55","55","Nota Fiscal Eletrônica","True","NFe","nf-e","icms" "document_57","57","Conhecimento de Transporte Eletrônico – CT-e","True","CTe","ct-e","icms" +"document_58","58","Manifesto de Carga","False","MDFe","mdf-e","icms" "document_59","59","Cupom Fiscal Eletrônico - CF-e","True","CFe","cf-e","icms" "document_60","60","Cupom Fiscal Eletrônico CF-e-ECF","True",,,"icms" "document_65","65","Nota Fiscal Eletrônica ao Consumidor Final – NFC-e","True","NFe","nfc-e","icms" From b71cbd14d6727b59a9333d534fb843d52ce0a270 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Wed, 6 Sep 2023 10:28:07 -0300 Subject: [PATCH 05/72] [ADD] MDFe fields parsing --- l10n_br_mdfe/__manifest__.py | 8 +- l10n_br_mdfe/constants/mdfe.py | 47 +++++ l10n_br_mdfe/data/ir_config_parameter.xml | 9 + l10n_br_mdfe/models/__init__.py | 3 +- l10n_br_mdfe/models/document.py | 188 ++++++++++++++++- l10n_br_mdfe/models/document_related.py | 8 +- l10n_br_mdfe/models/res_company.py | 48 ++++- l10n_br_mdfe/models/res_config_settings.py | 31 +++ l10n_br_mdfe/models/res_partner.py | 222 ++++++++++++++++++++- l10n_br_mdfe/views/mdfe_action.xml | 21 ++ l10n_br_mdfe/views/mdfe_menu.xml | 13 ++ l10n_br_mdfe/views/res_company.xml | 15 +- 12 files changed, 586 insertions(+), 27 deletions(-) create mode 100644 l10n_br_mdfe/constants/mdfe.py create mode 100644 l10n_br_mdfe/data/ir_config_parameter.xml create mode 100644 l10n_br_mdfe/models/res_config_settings.py create mode 100644 l10n_br_mdfe/views/mdfe_action.xml create mode 100644 l10n_br_mdfe/views/mdfe_menu.xml diff --git a/l10n_br_mdfe/__manifest__.py b/l10n_br_mdfe/__manifest__.py index 2dc7b26b73eb..d178522e64fc 100644 --- a/l10n_br_mdfe/__manifest__.py +++ b/l10n_br_mdfe/__manifest__.py @@ -17,10 +17,10 @@ "spec_driven_model", ], "data": [ - # 'views/document_related.xml', - # 'views/res_partner.xml', - # 'views/res_company.xml', - # 'views/document.xml', + "data/ir_config_parameter.xml", + "views/mdfe_action.xml", + "views/mdfe_menu.xml", + "views/res_company.xml", ], "post_init_hook": "post_init_hook", "installable": True, diff --git a/l10n_br_mdfe/constants/mdfe.py b/l10n_br_mdfe/constants/mdfe.py new file mode 100644 index 000000000000..c6bda813c601 --- /dev/null +++ b/l10n_br_mdfe/constants/mdfe.py @@ -0,0 +1,47 @@ +# Copyright (C) 2023 Felipe Zago - KMEE +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +MDFE_VERSIONS = [("3.00", "3.00")] + +MDFE_VERSION_DEFAULT = "3.00" + +MDFE_ENVIRONMENTS = [("1", "Produção"), ("2", "Homologação")] + +MDFE_ENVIRONMENT_DEFAULT = "2" + +MDFE_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"), +] + +MDFE_EMIT_TYPE_DEFAULT = "1" + +MDFE_TRANSP_TYPE = [ + ("1", "1 - Empresa de Transporte de Cargas – ETC"), + ("2", "2 - Transportador Autônomo de Cargas – TAC"), + ("3", "3 - Cooperativa de Transporte de Cargas – CTC"), +] + +MDFE_TRANSP_TYPE_DEFAULT = "1" + +MDFE_MODALS = [ + ("1", "1 - Rodoviário"), + ("2", "2 - Aéreo"), + ("3", "3 - Aquaviário"), + ("4", "4 - Ferroviário"), +] + +MDFE_MODAL_DEFAULT = "1" + +MDFE_TRANSMISSIONS = [ + ("1", "Emissão Normal"), + ("2", "Contingência Off-Line"), + ("3", "Regime Especial NFF"), +] + +MDFE_TRANSMISSION_DEFAULT = "1" + +MDFE_EMISSION_PROCESSES = [("0", "Emissão de MDFe com aplicativo do contribuinte")] + +MDFE_EMISSION_PROCESS_DEFAULT = "0" diff --git a/l10n_br_mdfe/data/ir_config_parameter.xml b/l10n_br_mdfe/data/ir_config_parameter.xml new file mode 100644 index 000000000000..28a75474e0da --- /dev/null +++ b/l10n_br_mdfe/data/ir_config_parameter.xml @@ -0,0 +1,9 @@ + + + + + l10n_br_mdfe.version.name + Odoo Brasil OCA v14 + + + diff --git a/l10n_br_mdfe/models/__init__.py b/l10n_br_mdfe/models/__init__.py index 2e8e1cd4b724..2c2add6bfd2f 100644 --- a/l10n_br_mdfe/models/__init__.py +++ b/l10n_br_mdfe/models/__init__.py @@ -1,4 +1,5 @@ from . import document +from . import document_related from . import res_company from . import res_partner -from . import document_related +from . import res_config_settings diff --git a/l10n_br_mdfe/models/document.py b/l10n_br_mdfe/models/document.py index f9327a256379..01a018a86bbc 100644 --- a/l10n_br_mdfe/models/document.py +++ b/l10n_br_mdfe/models/document.py @@ -3,15 +3,34 @@ import re -from odoo import api, fields +from erpbrasil.assinatura import certificado as cert +from erpbrasil.transmissao import TransmissaoSOAP +from nfelib.nfe.ws.edoc_legacy import MDFeAdapter as edoc_mdfe +from requests import Session +from odoo import _, api, fields +from odoo.exceptions import UserError + +from odoo.addons.l10n_br_fiscal.constants.fiscal import MODELO_FISCAL_MDFE from odoo.addons.spec_driven_model.models import spec_models +from ..constants.mdfe import ( + MDFE_EMISSION_PROCESS_DEFAULT, + MDFE_EMISSION_PROCESSES, + MDFE_EMIT_TYPES, + MDFE_MODAL_DEFAULT, + MDFE_MODALS, + MDFE_TRANSMISSIONS, + MDFE_TRANSP_TYPE, + MDFE_VERSIONS, +) + def filtered_processador_edoc_mdfe(record): - if record.processador_edoc == "oca" and record.document_type_id.code in ["58"]: - return True - return False + return ( + record.processador_edoc == "oca" + and record.document_type_id.code == MODELO_FISCAL_MDFE + ) class MDFe(spec_models.StackedModel): @@ -47,17 +66,17 @@ class MDFe(spec_models.StackedModel): mdfe30_versao = fields.Char(related="document_version") mdfe30_Id = fields.Char( - compute="_compute_mdfe40_id_tag", - inverse="_inverse_mdfe40_id_tag", + compute="_compute_mdfe30_id_tag", + inverse="_inverse_mdfe30_id_tag", ) ########################## - # MDF-e tag: id + # MDF-e tag: infMDFe # Methods ########################## @api.depends("document_type_id", "document_key") - def _compute_mdfe40_id_tag(self): + def _compute_mdfe30_id_tag(self): """Set schema data which are not just related fields""" for record in self.filtered(filtered_processador_edoc_mdfe): @@ -72,7 +91,156 @@ def _compute_mdfe40_id_tag(self): else: record.mdfe30_Id = False - def _inverse_mdfe40_id_tag(self): + def _inverse_mdfe30_id_tag(self): for record in self: - if record.cte40_Id: + if record.mdfe30_Id: record.document_key = re.findall(r"\d+", str(record.mdfe30_Id))[0] + + ########################## + # MDF-e tag: ide + ########################## + + mdfe30_cUF = fields.Char( + related="company_id.partner_id.state_id.ibge_code", + ) + + mdfe30_tpAmb = fields.Selection(related="mdfe_environment") + + mdfe_environment = fields.Selection( + selection=MDFE_VERSIONS, + string="MDFe Environment", + copy=False, + default=lambda self: self.env.company.mdfe_environment, + ) + + mdfe30_tpEmit = fields.Selection(related="mdfe_emit_type") + + mdfe_emit_type = fields.Selection( + selection=MDFE_EMIT_TYPES, + string="MDFe Emit Type", + copy=False, + default=lambda self: self.env.company.mdfe_emit_type, + ) + + mdfe30_tpTransp = fields.Selection(related="mdfe_transp_type") + + mdfe_transp_type = fields.Selection( + selection=MDFE_TRANSP_TYPE, + string="MDFe Transp Type", + copy=False, + default=lambda self: self.env.company.mdfe_transp_type, + ) + + mdfe30_mod = fields.Char(related="document_type_id.code") + + mdfe30_serie = fields.Char(related="document_serie") + + mdfe30_nMDF = fields.Char(related="document_number") + + mdfe30_dhEmi = fields.Datetime(related="document_date") + + mdfe30_modal = fields.Selection( + selection=MDFE_MODALS, string="Transport Modal", default=MDFE_MODAL_DEFAULT + ) + + mdfe30_tpEmis = fields.Selection(related="nfe_transmission") + + mdfe_transmission = fields.Selection( + selection=MDFE_TRANSMISSIONS, + string="MDFe Transmission", + copy=False, + default=lambda self: self.env.company.mdfe_transmission, + ) + + mdfe30_procEmi = fields.Selection( + selection=MDFE_EMISSION_PROCESSES, + string="MDFe Emission Process", + default=MDFE_EMISSION_PROCESS_DEFAULT, + ) + + mdfe30_verProc = fields.Char( + copy=False, + default=lambda s: s.env["ir.config_parameter"] + .sudo() + .get_param("l10n_br_mdfe.version.name", default="Odoo Brasil OCA v14"), + ) + + mdfe30_UFIni = fields.Selection(related="mdfe_initial_state_id.ibge_code") + + mdfe30_UFFim = fields.Selection(related="mdfe_final_state_id.ibge_code") + + mdfe_initial_state_id = fields.Many2one( + comodel_name="res.country.state", + string="MDFe Initial State", + domain=[("country_id.code", "=", "BR")], + ) + + mdfe_final_state_id = fields.Many2one( + comodel_name="res.country.state", + string="MDFe Final State", + domain=[("country_id.code", "=", "BR")], + ) + + ########################## + # MDF-e tag: infMunCarrega + ########################## + + # TODO: compute? + mdfe30_infMunCarrega = fields.One2many(related="mdfe_loading_city_ids") + + mdfe_loading_city_ids = fields.One2many( + comodel_name="res.city", string="MDFe Loading Cities" + ) + + ########################## + # MDF-e tag: infPercurso + ########################## + + # TODO: compute? + mdfe30_infPercurso = fields.One2many(related="mdfe_route_state_ids") + + mdfe_route_state_ids = fields.One2many( + comodel_name="res.country.state", + string="MDFe Route States", + domain=[("country_id.code", "=", "BR")], + ) + + ########################## + # MDF-e tag: emit + ########################## + + nfe40_emit = fields.Many2one(comodel_name="res.company", related="company_id") + + ########################## + # MDF-e tag: infRespTec + ########################## + + mdfe30_infRespTec = fields.Many2one( + comodel_name="res.partner", + related="company_id.technical_support_id", + ) + + def _processador(self): + certificate = False + if self.company_id.sudo().certificate_nfe_id: + certificate = self.company_id.sudo().certificate_nfe_id + elif self.company_id.sudo().certificate_ecnpj_id: + certificate = self.company_id.sudo().certificate_ecnpj_id + + if not certificate: + raise UserError(_("Certificado não encontrado")) + + certificado = cert.Certificado( + arquivo=certificate.file, + senha=certificate.password, + ) + session = Session() + session.verify = False + params = { + "transmissao": TransmissaoSOAP(certificado, session), + "uf": self.company_id.state_id.ibge_code, + "versao": self.nfe_version, + "ambiente": self.nfe_environment, + } + + return edoc_mdfe(**params) diff --git a/l10n_br_mdfe/models/document_related.py b/l10n_br_mdfe/models/document_related.py index b840dbf339ec..daa5680408dc 100644 --- a/l10n_br_mdfe/models/document_related.py +++ b/l10n_br_mdfe/models/document_related.py @@ -7,13 +7,9 @@ class MDFeRelated(spec_models.StackedModel): _name = "l10n_br_fiscal.document.related" - _inherit = [ - "l10n_br_fiscal.document.related", - "mdfe.30.tmdfe_infnfe", - "mdfe.30.infcte", - ] + _inherit = ["l10n_br_fiscal.document.related", "mdfe.30.tmdfe_infnfe"] _stacked = "mdfe.30.tmdfe_infnfe" - _field_prefix = "cte30_" + _field_prefix = "mdfe30_" _schema_name = "mdfe" _schema_version = "3.0.0" _odoo_module = "l10n_br_mdfe" diff --git a/l10n_br_mdfe/models/res_company.py b/l10n_br_mdfe/models/res_company.py index 6ebed5021687..eff531052c60 100644 --- a/l10n_br_mdfe/models/res_company.py +++ b/l10n_br_mdfe/models/res_company.py @@ -1,16 +1,62 @@ # 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 +from ..constants.mdfe import ( + MDFE_EMIT_TYPE_DEFAULT, + MDFE_EMIT_TYPES, + MDFE_ENVIRONMENT_DEFAULT, + MDFE_ENVIRONMENTS, + MDFE_TRANSMISSION_DEFAULT, + MDFE_TRANSMISSIONS, + MDFE_TRANSP_TYPE, + MDFE_TRANSP_TYPE_DEFAULT, + MDFE_VERSION_DEFAULT, + MDFE_VERSIONS, +) + class ResCompany(spec_models.SpecModel): _name = "res.company" _inherit = [ "res.company", - "mdfe.30.emi", + "mdfe.30.emit", ] _mdfe_search_keys = ["mdfe30_CNPJ", "mdfe30_xNome", "mdfe_xFant"] + mdfe_version = fields.Selection( + selection=MDFE_VERSIONS, + string="MDFe Version", + default=MDFE_VERSION_DEFAULT, + ) + + mdfe_environment = fields.Selection( + selection=MDFE_ENVIRONMENTS, + string="MDFe Environment", + default=MDFE_ENVIRONMENT_DEFAULT, + ) + + mdfe_emit_type = fields.Selection( + selection=MDFE_EMIT_TYPES, + string="MDFe Emit Type", + default=MDFE_EMIT_TYPE_DEFAULT, + ) + + mdfe_transp_type = fields.Selection( + selection=MDFE_TRANSP_TYPE, + string="MDFe Transp Type", + default=MDFE_TRANSP_TYPE_DEFAULT, + ) + + mdfe_transmission = fields.Selection( + selection=MDFE_TRANSMISSIONS, + string="MDFe Transmission", + copy=False, + default=MDFE_TRANSMISSION_DEFAULT, + ) + # TODO diff --git a/l10n_br_mdfe/models/res_config_settings.py b/l10n_br_mdfe/models/res_config_settings.py new file mode 100644 index 000000000000..c53461962393 --- /dev/null +++ b/l10n_br_mdfe/models/res_config_settings.py @@ -0,0 +1,31 @@ +# Copyright (C) 2023 KMEE - Felipe Zago +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + mdfe_version = fields.Selection( + string="MDF-e Version", + related="company_id.mdfe_version", + readonly=False, + ) + + mdfe_environment = fields.Selection( + string="MDF-e Environment", + related="company_id.mdfe_environment", + readonly=False, + ) + + mdfe_transmission = fields.Selection( + string="MDF-e Transmission", + related="company_id.mdfe_transmission", + readonly=False, + ) + + mdfe_version_name = fields.Char( + string="MDF-e Proc Version", + config_parameter="l10n_br_mdfe.version.name", + ) diff --git a/l10n_br_mdfe/models/res_partner.py b/l10n_br_mdfe/models/res_partner.py index ac77102f122d..400a8c0656a0 100644 --- a/l10n_br_mdfe/models/res_partner.py +++ b/l10n_br_mdfe/models/res_partner.py @@ -1,6 +1,11 @@ # Copyright 2023 KMEE # 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, punctuation_rm + +from odoo import api, fields + from odoo.addons.spec_driven_model.models import spec_models @@ -18,4 +23,219 @@ class ResPartner(spec_models.SpecModel): ] _mdfe_search_keys = ["mdfe30_CNPJ", "mdfe30_CPF", "mdfe_xNome"] - # TODO + @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 + + mdfe30_CNPJ = fields.Char( + compute="_compute_mdfe_data", inverse="_inverse_mdfe30_CNPJ", store=True + ) + + mdfe30_CPF = fields.Char( + compute="_compute_mdfe_data", inverse="_inverse_mdfe30_CPF", store=True + ) + + mdfe30_xLgr = fields.Char( + readonly=True, + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_nro = fields.Char( + readonly=True, + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_xCpl = fields.Char( + readonly=True, + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_xBairro = fields.Char( + readonly=True, + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_cMun = fields.Char( + readonly=True, + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_xMun = fields.Char( + readonly=True, + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_UF = fields.Char( + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_choice6 = fields.Selection( + selection=[("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], + string="CNPJ/CPF do Emitente", + compute="_compute_mdfe_data", + ) + + mdfe30_CEP = fields.Char( + compute="_compute_mdfe_data", inverse="_inverse_mdfe30_CEP", compute_sudo=True + ) + + mdfe30_cPais = fields.Char( + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_xPais = fields.Char( + compute="_compute_mdfe30_ender", + inverse="_inverse_mdfe30_ender", + compute_sudo=True, + ) + + mdfe30_fone = fields.Char( + compute="_compute_mdfe_data", inverse="_inverse_mdfe30_fone", compute_sudo=True + ) + + mdfe30_xNome = fields.Char(related="legal_name") + + mdfe30_xFant = fields.Char(related="name", string="Nome Fantasia") + + mdfe30_IE = fields.Char( + compute="_compute_mdfe_data", + inverse="_inverse_mdfe30_IE", + compute_sudo=True, + ) + + mdfe30_email = fields.Char(related="email") + + mdfe30_xContato = fields.Char(related="legal_name") + + mdfe30_choice8 = fields.Selection( + selection=[("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], + string="CNPJ/CPF do Parceiro Autorizado", + compute="_compute_mdfe_data", + ) + + @api.depends("company_type", "inscr_est", "cnpj_cpf", "country_id") + def _compute_mdfe_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.is_company: + rec.mdfe30_choice6 = "mdfe30_CNPJ" + rec.mdfe30_choice8 = "mdfe30_CNPJ" + rec.mdfe30_CNPJ = cnpj_cpf + rec.mdfe30_CPF = None + else: + rec.mdfe30_choice6 = "mdfe30_CPF" + rec.mdfe30_choice8 = "mdfe30_CPF" + rec.mdfe30_CPF = cnpj_cpf + rec.mdfe30_CNPJ = None + else: + rec.mdfe30_choice6 = False + rec.mdfe30_choice8 = False + rec.mdfe30_CNPJ = "" + rec.mdfe30_CPF = "" + + if rec.inscr_est: + rec.mdfe30_IE = punctuation_rm(rec.inscr_est) + else: + rec.mdfe30_IE = None + + rec.mdfe30_CEP = punctuation_rm(rec.zip) + rec.mdfe30_fone = punctuation_rm(rec.phone or "").replace(" ", "") + + def _inverse_mdfe30_CNPJ(self): + for rec in self: + if rec.mdfe30_CNPJ: + rec.is_company = True + rec.mdfe30_choice6 = "mdfe30_CPF" + rec.mdfe30_choice8 = "mdfe30_CPF" + rec.cnpj_cpf = cnpj_cpf.formata(str(rec.mdfe30_CNPJ)) + + def _inverse_mdfe30_CPF(self): + for rec in self: + if rec.mdfe30_CPF: + rec.is_company = False + rec.mdfe30_choice6 = "mdfe30_CNPJ" + rec.mdfe30_choice8 = "mdfe30_CNPJ" + rec.cnpj_cpf = cnpj_cpf.formata(str(rec.mdfe30_CPF)) + + def _inverse_mdfe30_IE(self): + for rec in self: + if rec.mdfe30_IE: + rec.inscr_est = str(rec.mdfe30_IE) + + def _inverse_mdfe30_CEP(self): + for rec in self: + if rec.mdfe30_CEP: + country_code = rec.country_id.code if rec.country_id else "BR" + rec.zip = format_zipcode(rec.mdfe30_CEP, country_code) + + def _inverse_mdfe30_fone(self): + for rec in self: + if rec.mdfe30_fone: + rec.phone = rec.mdfe30_fone + + @api.depends( + "street_name", + "street_number", + "street2", + "district", + "city_id", + "state_id", + "country_id", + ) + def _compute_mdfe30_ender(self): + for rec in self: + rec.mdfe30_xLgr = rec.street_name + rec.mdfe30_nro = rec.street_number + rec.mdfe30_xCpl = rec.street2 + rec.mdfe30_xBairro = rec.district + rec.mdfe30_cMun = rec.city_id.ibge_code + rec.mdfe30_xMun = rec.city_id.name + rec.mdfe30_UF = rec.state_id.code + rec.mdfe30_cPais = rec.country_id.bc_code + rec.mdfe30_xPais = rec.country_id.name + + def _inverse_mdfe30_ender(self): + for rec in self: + if rec.mdfe30_cMun and rec.mdfe30_cPais and rec.mdfe30_UF: + city_id = self.env["res.city"].search( + [("ibge_code", "=", rec.mdfe30_cMun)] + ) + country_id = self.env["res.country"].search( + [("bc_code", "=", rec.mdfe30_cPais)] + ) + state_id = self.env["res.country.state"].search( + [("code", "=", rec.mdfe30_UF), ("country_id", "=", country_id.id)] + ) + + rec.street_name = rec.mdfe30_xLgr + rec.street_number = rec.mdfe30_nro + rec.street2 = rec.mdfe30_xCpl + rec.district = rec.mdfe30_xBairro + rec.city_id = city_id + rec.country_id = country_id + rec.state_id = state_id diff --git a/l10n_br_mdfe/views/mdfe_action.xml b/l10n_br_mdfe/views/mdfe_action.xml new file mode 100644 index 000000000000..4e942942bdef --- /dev/null +++ b/l10n_br_mdfe/views/mdfe_action.xml @@ -0,0 +1,21 @@ + + + + + + MDF-e + ir.actions.act_window + l10n_br_fiscal.document + tree,form + + +

+ Add a new MDF-e +

+
+
+ +
diff --git a/l10n_br_mdfe/views/mdfe_menu.xml b/l10n_br_mdfe/views/mdfe_menu.xml new file mode 100644 index 000000000000..a7f4ef64c442 --- /dev/null +++ b/l10n_br_mdfe/views/mdfe_menu.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/l10n_br_mdfe/views/res_company.xml b/l10n_br_mdfe/views/res_company.xml index a9f0587d68df..96261006a9d8 100644 --- a/l10n_br_mdfe/views/res_company.xml +++ b/l10n_br_mdfe/views/res_company.xml @@ -6,12 +6,19 @@ res.company.form (in l10n_br_mdfe) res.company - + - + + + + + + + + + + - - From 48c531b3fe71b43243c90b33707b6aedb26b57c4 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Wed, 6 Sep 2023 10:31:43 -0300 Subject: [PATCH 06/72] [FIX] add inherit to document related --- l10n_br_mdfe/models/document_related.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/l10n_br_mdfe/models/document_related.py b/l10n_br_mdfe/models/document_related.py index daa5680408dc..1fa5f9158de6 100644 --- a/l10n_br_mdfe/models/document_related.py +++ b/l10n_br_mdfe/models/document_related.py @@ -7,7 +7,11 @@ class MDFeRelated(spec_models.StackedModel): _name = "l10n_br_fiscal.document.related" - _inherit = ["l10n_br_fiscal.document.related", "mdfe.30.tmdfe_infnfe"] + _inherit = [ + "l10n_br_fiscal.document.related", + "mdfe.30.tmdfe_infnfe", + "mdfe.30.infcte", + ] _stacked = "mdfe.30.tmdfe_infnfe" _field_prefix = "mdfe30_" _schema_name = "mdfe" From 5508177bffa5f54a45376b52dda001069a6ef1f3 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Fri, 8 Sep 2023 18:09:21 -0300 Subject: [PATCH 07/72] [ADD] MDFe views and fields --- .../models/document_fiscal_mixin_fields.py | 4 + l10n_br_fiscal/models/document_workflow.py | 3 + l10n_br_mdfe/__manifest__.py | 1 + l10n_br_mdfe/models/__init__.py | 2 +- l10n_br_mdfe/models/document.py | 122 ++++++++++++++---- l10n_br_mdfe/models/res_company.py | 30 ++++- l10n_br_mdfe/views/document.xml | 54 +++++++- l10n_br_mdfe/views/mdfe_action.xml | 18 +++ l10n_br_mdfe/views/mdfe_menu.xml | 2 +- l10n_br_mdfe/views/res_company.xml | 4 +- 10 files changed, 203 insertions(+), 37 deletions(-) diff --git a/l10n_br_fiscal/models/document_fiscal_mixin_fields.py b/l10n_br_fiscal/models/document_fiscal_mixin_fields.py index 4543bd666c12..6eda2214ab40 100644 --- a/l10n_br_fiscal/models/document_fiscal_mixin_fields.py +++ b/l10n_br_fiscal/models/document_fiscal_mixin_fields.py @@ -436,3 +436,7 @@ def _operation_domain(self): ) force_compute_delivery_costs_by_total = fields.Boolean(default=False) + + key_random_code = fields.Char(string="Document Key Random Code") + + key_check_digit = fields.Char(string="Document Key Check Digit") diff --git a/l10n_br_fiscal/models/document_workflow.py b/l10n_br_fiscal/models/document_workflow.py index b06a1e7bdcb1..18734f53e0fc 100644 --- a/l10n_br_fiscal/models/document_workflow.py +++ b/l10n_br_fiscal/models/document_workflow.py @@ -267,6 +267,9 @@ def _generate_key(self): numero_serie=record.document_serie or "", validar=False, ) + record.key_random_code = chave_edoc.codigo_aleatorio + record.key_check_digit = chave_edoc.digito_verificador + # TODO: Implementar campos no Odoo # record.key_number = chave_edoc.campos # record.key_formated = ' '.joint(chave_edoc.partes()) diff --git a/l10n_br_mdfe/__manifest__.py b/l10n_br_mdfe/__manifest__.py index d178522e64fc..7bfaafd379b6 100644 --- a/l10n_br_mdfe/__manifest__.py +++ b/l10n_br_mdfe/__manifest__.py @@ -18,6 +18,7 @@ ], "data": [ "data/ir_config_parameter.xml", + "views/document.xml", "views/mdfe_action.xml", "views/mdfe_menu.xml", "views/res_company.xml", diff --git a/l10n_br_mdfe/models/__init__.py b/l10n_br_mdfe/models/__init__.py index 2c2add6bfd2f..382a68d66fec 100644 --- a/l10n_br_mdfe/models/__init__.py +++ b/l10n_br_mdfe/models/__init__.py @@ -1,5 +1,5 @@ +from . import res_company from . import document from . import document_related -from . import res_company from . import res_partner from . import res_config_settings diff --git a/l10n_br_mdfe/models/document.py b/l10n_br_mdfe/models/document.py index 01a018a86bbc..75c2c4282267 100644 --- a/l10n_br_mdfe/models/document.py +++ b/l10n_br_mdfe/models/document.py @@ -18,11 +18,11 @@ MDFE_EMISSION_PROCESS_DEFAULT, MDFE_EMISSION_PROCESSES, MDFE_EMIT_TYPES, + MDFE_ENVIRONMENTS, MDFE_MODAL_DEFAULT, MDFE_MODALS, MDFE_TRANSMISSIONS, MDFE_TRANSP_TYPE, - MDFE_VERSIONS, ) @@ -100,15 +100,13 @@ def _inverse_mdfe30_id_tag(self): # MDF-e tag: ide ########################## - mdfe30_cUF = fields.Char( - related="company_id.partner_id.state_id.ibge_code", - ) + mdfe30_cUF = fields.Selection(compute="_compute_uf") mdfe30_tpAmb = fields.Selection(related="mdfe_environment") mdfe_environment = fields.Selection( - selection=MDFE_VERSIONS, - string="MDFe Environment", + selection=MDFE_ENVIRONMENTS, + string="Environment", copy=False, default=lambda self: self.env.company.mdfe_environment, ) @@ -117,7 +115,7 @@ def _inverse_mdfe30_id_tag(self): mdfe_emit_type = fields.Selection( selection=MDFE_EMIT_TYPES, - string="MDFe Emit Type", + string="Emit Type", copy=False, default=lambda self: self.env.company.mdfe_emit_type, ) @@ -126,7 +124,7 @@ def _inverse_mdfe30_id_tag(self): mdfe_transp_type = fields.Selection( selection=MDFE_TRANSP_TYPE, - string="MDFe Transp Type", + string="Transp Type", copy=False, default=lambda self: self.env.company.mdfe_transp_type, ) @@ -139,22 +137,24 @@ def _inverse_mdfe30_id_tag(self): mdfe30_dhEmi = fields.Datetime(related="document_date") - mdfe30_modal = fields.Selection( + mdfe30_modal = fields.Selection(related="mdfe_modal") + + mdfe_modal = fields.Selection( selection=MDFE_MODALS, string="Transport Modal", default=MDFE_MODAL_DEFAULT ) - mdfe30_tpEmis = fields.Selection(related="nfe_transmission") + mdfe30_tpEmis = fields.Selection(related="mdfe_transmission") mdfe_transmission = fields.Selection( selection=MDFE_TRANSMISSIONS, - string="MDFe Transmission", + string="Transmission", copy=False, default=lambda self: self.env.company.mdfe_transmission, ) mdfe30_procEmi = fields.Selection( selection=MDFE_EMISSION_PROCESSES, - string="MDFe Emission Process", + string="Emission Process", default=MDFE_EMISSION_PROCESS_DEFAULT, ) @@ -165,51 +165,110 @@ def _inverse_mdfe30_id_tag(self): .get_param("l10n_br_mdfe.version.name", default="Odoo Brasil OCA v14"), ) - mdfe30_UFIni = fields.Selection(related="mdfe_initial_state_id.ibge_code") + mdfe30_UFIni = fields.Selection(compute="_compute_initial_final_state") - mdfe30_UFFim = fields.Selection(related="mdfe_final_state_id.ibge_code") + mdfe30_UFFim = fields.Selection(compute="_compute_initial_final_state") mdfe_initial_state_id = fields.Many2one( comodel_name="res.country.state", - string="MDFe Initial State", + string="Initial State", domain=[("country_id.code", "=", "BR")], ) mdfe_final_state_id = fields.Many2one( comodel_name="res.country.state", - string="MDFe Final State", + string="Final State", domain=[("country_id.code", "=", "BR")], ) + mdfe30_cMDF = fields.Char(related="key_random_code") + + mdfe30_cDV = fields.Char(related="key_check_digit") + + ########################## + # MDF-e tag: ide + # Methods + ########################## + + @api.depends("company_id") + def _compute_uf(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_cUF = record.company_id.partner_id.state_id.ibge_code + + @api.depends("mdfe_initial_state_id", "mdfe_final_state_id") + def _compute_initial_final_state(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_UFIni = record.mdfe_initial_state_id.ibge_code + record.mdfe30_UFFim = record.mdfe_final_state_id.ibge_code + ########################## # MDF-e tag: infMunCarrega ########################## - # TODO: compute? - mdfe30_infMunCarrega = fields.One2many(related="mdfe_loading_city_ids") + # TODO: is this right? + mdfe30_infMunCarrega = fields.One2many(compute="_compute_inf_carrega") - mdfe_loading_city_ids = fields.One2many( - comodel_name="res.city", string="MDFe Loading Cities" + mdfe_loading_city_ids = fields.Many2many( + comodel_name="res.city", string="Loading Cities" ) + ########################## + # MDF-e tag: infMunCarrega + # Methods + ########################## + + @api.depends("mdfe_loading_city_ids") + def _compute_inf_carrega(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_infMunCarrega = [ + ( + 0, + 0, + { + "mdfe30_cMunCarrega": city.ibge_code, + "mdfe30_xMunCarrega": city.name, + }, + ) + for city in record.mdfe_loading_city_ids + ] + ########################## # MDF-e tag: infPercurso ########################## - # TODO: compute? - mdfe30_infPercurso = fields.One2many(related="mdfe_route_state_ids") + # TODO: is this right? + mdfe30_infPercurso = fields.One2many(compute="_compute_inf_percurso") - mdfe_route_state_ids = fields.One2many( + mdfe_route_state_ids = fields.Many2many( comodel_name="res.country.state", - string="MDFe Route States", + string="Route States", domain=[("country_id.code", "=", "BR")], ) + ########################## + # MDF-e tag: infPercurso + # Methods + ########################## + + @api.depends("mdfe_route_state_ids") + def _compute_inf_percurso(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_infPercurso = [ + ( + 0, + 0, + { + "mdfe30_UFPer": city.ibge_code, + }, + ) + for city in record.mdfe_route_state_ids + ] + ########################## # MDF-e tag: emit ########################## - nfe40_emit = fields.Many2one(comodel_name="res.company", related="company_id") + mdfe30_emit = fields.Many2one(comodel_name="res.company", related="company_id") ########################## # MDF-e tag: infRespTec @@ -220,6 +279,19 @@ def _inverse_mdfe30_id_tag(self): related="company_id.technical_support_id", ) + def _build_attr(self, node, fields, vals, path, attr): + key = "mdfe30_%s" % (attr[0],) # TODO schema wise + value = getattr(node, attr[0]) + + if key == "mdfe30_mod": + 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 _processador(self): certificate = False if self.company_id.sudo().certificate_nfe_id: diff --git a/l10n_br_mdfe/models/res_company.py b/l10n_br_mdfe/models/res_company.py index eff531052c60..c948a008d195 100644 --- a/l10n_br_mdfe/models/res_company.py +++ b/l10n_br_mdfe/models/res_company.py @@ -59,4 +59,32 @@ class ResCompany(spec_models.SpecModel): default=MDFE_TRANSMISSION_DEFAULT, ) - # TODO + mdfe30_enderEmit = fields.Many2one( + comodel_name="res.partner", + related="partner_id", + ) + + mdfe30_CNPJ = fields.Char(related="partner_id.mdfe30_CNPJ") + + mdfe30_CPF = fields.Char(related="partner_id.mdfe30_CPF") + + mdfe30_xNome = fields.Char(related="partner_id.legal_name") + + mdfe30_xFant = fields.Char(related="partner_id.name") + + mdfe30_IE = fields.Char(related="partner_id.mdfe30_IE") + + mdfe30_fone = fields.Char(related="partner_id.mdfe30_fone") + + mdfe30_choice6 = fields.Selection( + [("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], + string="CNPJ ou CPF?", + compute="_compute_mdfe_data", + ) + + def _compute_mdfe_data(self): + for rec in self: + if rec.partner_id.is_company: + rec.mdfe30_choice6 = "mdfe30_CNPJ" + else: + rec.mdfe30_choice6 = "mdfe30_CPF" diff --git a/l10n_br_mdfe/views/document.xml b/l10n_br_mdfe/views/document.xml index 9cde23c5511b..63fbe76943e9 100644 --- a/l10n_br_mdfe/views/document.xml +++ b/l10n_br_mdfe/views/document.xml @@ -3,15 +3,55 @@ License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - - document.form (in l10n_br_mdfe) - document - + + mdfe.document.form.view (in l10n_br_mdfe) + l10n_br_fiscal.document + + primary - - - + + 1 + + + + 1 + + + + 1 + + + + 1 + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + diff --git a/l10n_br_mdfe/views/mdfe_action.xml b/l10n_br_mdfe/views/mdfe_action.xml index 4e942942bdef..184d9c6e50c9 100644 --- a/l10n_br_mdfe/views/mdfe_action.xml +++ b/l10n_br_mdfe/views/mdfe_action.xml @@ -7,10 +7,28 @@ ir.actions.act_window l10n_br_fiscal.document tree,form + +

Add a new MDF-e diff --git a/l10n_br_mdfe/views/mdfe_menu.xml b/l10n_br_mdfe/views/mdfe_menu.xml index a7f4ef64c442..7eb08deee1db 100644 --- a/l10n_br_mdfe/views/mdfe_menu.xml +++ b/l10n_br_mdfe/views/mdfe_menu.xml @@ -5,7 +5,7 @@ id="mdfe_document_menu" action="mdfe_document_action" name="MDF-e" - groups="l10n_br_nfe.group_user" + groups="l10n_br_fiscal.group_user" parent="l10n_br_fiscal.document_menu" sequence="50" /> diff --git a/l10n_br_mdfe/views/res_company.xml b/l10n_br_mdfe/views/res_company.xml index 96261006a9d8..4b12f3f6ca00 100644 --- a/l10n_br_mdfe/views/res_company.xml +++ b/l10n_br_mdfe/views/res_company.xml @@ -12,8 +12,8 @@ - - + + From 1b3ce3f1d90a1b4259508e46edc39f33848453f8 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Mon, 11 Sep 2023 17:58:03 -0300 Subject: [PATCH 08/72] [ADD] method to prepare processor params --- l10n_br_fiscal/models/document.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/l10n_br_fiscal/models/document.py b/l10n_br_fiscal/models/document.py index d656680389ec..e5e4015c9444 100644 --- a/l10n_br_fiscal/models/document.py +++ b/l10n_br_fiscal/models/document.py @@ -4,10 +4,13 @@ from ast import literal_eval +from erpbrasil.assinatura import certificado as cert from erpbrasil.base.fiscal.edoc import ChaveEdoc +from erpbrasil.transmissao import TransmissaoSOAP +from requests import Session from odoo import _, api, fields, models -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError, ValidationError from ..constants.fiscal import ( DOCUMENT_ISSUER_COMPANY, @@ -570,3 +573,27 @@ def action_send_email(self): "target": "new", "context": ctx, } + + def _prepare_processor_params(self): + certificate = False + if self.company_id.sudo().certificate_nfe_id: + certificate = self.company_id.sudo().certificate_nfe_id + elif self.company_id.sudo().certificate_ecnpj_id: + certificate = self.company_id.sudo().certificate_ecnpj_id + + if not certificate: + raise UserError(_("Certificado não encontrado")) + + certificado = cert.Certificado( + arquivo=certificate.file, + senha=certificate.password, + ) + session = Session() + session.verify = False + + return { + "transmissao": TransmissaoSOAP(certificado, session), + "uf": self.company_id.state_id.ibge_code, + "versao": self.nfe_version, + "ambiente": self.nfe_environment, + } From d95787bd27a3dfc699dfe9dbe84387b89ba6c8b7 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Mon, 11 Sep 2023 17:59:48 -0300 Subject: [PATCH 09/72] [ADD] processor default params to NFe --- l10n_br_nfe/models/document.py | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/l10n_br_nfe/models/document.py b/l10n_br_nfe/models/document.py index e49752901aaa..3ccfdb4ac195 100644 --- a/l10n_br_nfe/models/document.py +++ b/l10n_br_nfe/models/document.py @@ -9,10 +9,8 @@ from datetime import datetime from unicodedata import normalize -from erpbrasil.assinatura import certificado as cert from erpbrasil.base.fiscal.edoc import ChaveEdoc from erpbrasil.edoc.pdf import base -from erpbrasil.transmissao import TransmissaoSOAP from lxml import etree from nfelib.nfe.bindings.v4_0.nfe_v4_00 import Nfe from nfelib.nfe.ws.edoc_legacy import NFCeAdapter as edoc_nfce, NFeAdapter as edoc_nfe @@ -805,28 +803,7 @@ def _serialize(self, edocs): return edocs def _processador(self): - certificate = False - if self.company_id.sudo().certificate_nfe_id: - certificate = self.company_id.sudo().certificate_nfe_id - elif self.company_id.sudo().certificate_ecnpj_id: - certificate = self.company_id.sudo().certificate_ecnpj_id - - if not certificate: - raise UserError(_("Certificado não encontrado")) - self._check_nfe_environment() - - certificado = cert.Certificado( - arquivo=certificate.file, - senha=certificate.password, - ) - session = Session() - session.verify = False - params = { - "transmissao": TransmissaoSOAP(certificado, session), - "uf": self.company_id.state_id.ibge_code, - "versao": self.nfe_version, - "ambiente": self.nfe_environment, - } + params = self._prepare_processor_params() if self.document_type == MODELO_FISCAL_NFCE: params.update( From 4276d04f72f690b88e3606e1ee1ddb444904a304 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Mon, 11 Sep 2023 18:02:06 -0300 Subject: [PATCH 10/72] [ADD] MDFe modal implementations --- l10n_br_mdfe/__manifest__.py | 5 + l10n_br_mdfe/constants/mdfe.py | 15 +- l10n_br_mdfe/constants/modal.py | 353 ++++++++++++ l10n_br_mdfe/data/l10n_br_mdfe.modal.csv | 5 + l10n_br_mdfe/models/__init__.py | 5 + l10n_br_mdfe/models/document.py | 525 ++++++++++++++--- l10n_br_mdfe/models/document_info.py | 94 ++++ l10n_br_mdfe/models/document_related.py | 7 +- l10n_br_mdfe/models/modal.py | 28 + l10n_br_mdfe/models/modal_aquaviario.py | 148 +++++ l10n_br_mdfe/models/modal_ferroviario.py | 59 ++ l10n_br_mdfe/models/modal_rodoviario.py | 358 ++++++++++++ l10n_br_mdfe/models/res_partner.py | 83 ++- l10n_br_mdfe/security/ir.model.access.csv | 30 + l10n_br_mdfe/views/document.xml | 222 +++++++- l10n_br_mdfe/views/document_related.xml | 17 - l10n_br_mdfe/views/mdfe_action.xml | 10 +- l10n_br_mdfe/views/modal/modal_aquaviario.xml | 65 +++ .../views/modal/modal_ferroviario.xml | 40 ++ l10n_br_mdfe/views/modal/modal_rodoviario.xml | 527 ++++++++++++++++++ l10n_br_mdfe_spec/__manifest__.py | 2 +- l10n_br_nfe/models/document.py | 1 - 22 files changed, 2493 insertions(+), 106 deletions(-) create mode 100644 l10n_br_mdfe/constants/modal.py create mode 100644 l10n_br_mdfe/data/l10n_br_mdfe.modal.csv create mode 100644 l10n_br_mdfe/models/document_info.py create mode 100644 l10n_br_mdfe/models/modal.py create mode 100644 l10n_br_mdfe/models/modal_aquaviario.py create mode 100644 l10n_br_mdfe/models/modal_ferroviario.py create mode 100644 l10n_br_mdfe/models/modal_rodoviario.py create mode 100644 l10n_br_mdfe/security/ir.model.access.csv delete mode 100644 l10n_br_mdfe/views/document_related.xml create mode 100644 l10n_br_mdfe/views/modal/modal_aquaviario.xml create mode 100644 l10n_br_mdfe/views/modal/modal_ferroviario.xml create mode 100644 l10n_br_mdfe/views/modal/modal_rodoviario.xml diff --git a/l10n_br_mdfe/__manifest__.py b/l10n_br_mdfe/__manifest__.py index 7bfaafd379b6..f672451ed4be 100644 --- a/l10n_br_mdfe/__manifest__.py +++ b/l10n_br_mdfe/__manifest__.py @@ -17,11 +17,16 @@ "spec_driven_model", ], "data": [ + "security/ir.model.access.csv", "data/ir_config_parameter.xml", + "data/l10n_br_mdfe.modal.csv", "views/document.xml", "views/mdfe_action.xml", "views/mdfe_menu.xml", "views/res_company.xml", + "views/modal/modal_aquaviario.xml", + "views/modal/modal_rodoviario.xml", + "views/modal/modal_ferroviario.xml", ], "post_init_hook": "post_init_hook", "installable": True, diff --git a/l10n_br_mdfe/constants/mdfe.py b/l10n_br_mdfe/constants/mdfe.py index c6bda813c601..3a6f2b769a7a 100644 --- a/l10n_br_mdfe/constants/mdfe.py +++ b/l10n_br_mdfe/constants/mdfe.py @@ -18,22 +18,13 @@ MDFE_EMIT_TYPE_DEFAULT = "1" MDFE_TRANSP_TYPE = [ - ("1", "1 - Empresa de Transporte de Cargas – ETC"), - ("2", "2 - Transportador Autônomo de Cargas – TAC"), - ("3", "3 - Cooperativa de Transporte de Cargas – CTC"), + ("1", "Empresa de Transporte de Cargas – ETC"), + ("2", "Transportador Autônomo de Cargas – TAC"), + ("3", "Cooperativa de Transporte de Cargas – CTC"), ] MDFE_TRANSP_TYPE_DEFAULT = "1" -MDFE_MODALS = [ - ("1", "1 - Rodoviário"), - ("2", "2 - Aéreo"), - ("3", "3 - Aquaviário"), - ("4", "4 - Ferroviário"), -] - -MDFE_MODAL_DEFAULT = "1" - MDFE_TRANSMISSIONS = [ ("1", "Emissão Normal"), ("2", "Contingência Off-Line"), diff --git a/l10n_br_mdfe/constants/modal.py b/l10n_br_mdfe/constants/modal.py new file mode 100644 index 000000000000..28d3baa4ded7 --- /dev/null +++ b/l10n_br_mdfe/constants/modal.py @@ -0,0 +1,353 @@ +MDFE_MODALS = [ + ("1", "Rodoviário"), + ("2", "Aéreo"), + ("3", "Aquaviário"), + ("4", "Ferroviário"), +] + +MDFE_MODAL_DEFAULT = "1" + +MDFE_MODAL_VERSION_DEFAULT = "3.00" + +MDFE_MODAL_DEFAULT_AIRCRAFT = "OACI" + +MDFE_MODAL_SHIP_TYPES = [ + ("01", "ALVARENGA"), + ("02", "BARCAÇA"), + ("03", "BARCAÇA PROPULSADA"), + ("04", "CARGUEIRO"), + ("05", "CATAMARÃ MISTO"), + ("06", "CATAMARÃ PASSAGEIROS"), + ("07", "CHATA CARGUEIRA"), + ("08", "CHATA GRANELEIRA"), + ("09", "CHATA MISTA"), + ("10", "CHATA TANQUE"), + ("11", "EMPURRADOR"), + ("12", "ESTIMULAÇÃO"), + ("13", "FERRY BOAT"), + ("14", "FLOTEL"), + ("15", "FLUTUANTE"), + ("16", "CABREA/GUINDASTE"), + ("17", "LANCHA MISTA"), + ("18", "LANCHA PASSAGEIROS"), + ("19", "LANCHA PRÁTICO"), + ("20", "MANUSEIO DE ESPIAS"), + ("21", "PESQUISA"), + ("22", "REBOCADOR"), + ("23", "SUPRIMENTO"), + ("24", "TRANSBORDADOR"), + ("25", "CARGA PESADA"), + ("26", "FRIGORÍFICO"), + ("27", "GLP"), + ("28", "GRANELEIRO"), + ("29", "MINERO-PETROLEIRO"), + ("30", "MULTI-PROPOSITO"), + ("31", "NAVIO CISTERNA"), + ("32", "PASSAGEIROS"), + ("33", "PETROLEIRO"), + ("34", "PORTA CONTEINER"), + ("35", "TANQUE QUIMICO"), + ("36", "RO-RO"), + ("37", "DRAGA"), + ("38", "OUTROS"), + ("39", "BALSA"), + ("40", "DSV(APOIO E MERGULHO)"), + ("41", "SWATH"), + ("42", "ROV"), + ("43", "AHTS(REBOQUE E MANUSEIO DE ÂNCORAS)"), + ("44", "UT 4.000"), + ("45", "UT 750"), + ("46", "RSV"), + ("47", "TANQUE GLP"), + ("48", "PATROL VESSEL"), + ("50", "TANQUE GNL"), + ("51", "LANÇAMENTO DE LINHAS"), + ("52", "BARCO A MOTOR"), + ("53", "BALSA MOTORIZADA"), + ("54", "MPSV"), + ("55", "PESQUEIRO"), + ("56", "PSV"), +] + +MDFE_MODAL_HARBORS = [ + ("BRADR", "ANGRA DOS REIS"), + ("BRAFU", "AFUÁ"), + ("BRAJU", "PORTO BARRA DOS COQUEIROS - ARACAJU - SE"), + ("BRALT", "ALENQUER"), + ("BRAMM", "ALMEIRIM"), + ("BRAMW", "ALUMAR"), + ("BRANT", "ANTONINA"), + ("BRARB", "ARATU"), + ("BRARE", "AREIA BRANCA (TERMISA)"), + ("BRATB", "ABAETETUBA"), + ("BRATM", "ALTAMIRA"), + ("BRBAR", "BARRA DOS COQUEIROS"), + ("BRBEL", "BELEM"), + ("BRBPS", "PORTO SEGURO"), + ("BRBVB", "BOA VISTA"), + ("BRBVE", "BREVES"), + ("BRBVM", "BELO MONTE"), + ("BRBZC", "BUZIOS"), + ("BRCAF", "CARAUARI"), + ("BRCAM", "BACIA PETROLÍFERA DE CAMPOS"), + ("BRCAW", "CAMPOS"), + ("BRCCX", "CÁCERES"), + ("BRCDA", "CODAJÁS"), + ("BRCDA", "CODAJÁS"), + ("BRCDO", "CABEDELO"), + ("BRCIZ", "COARI"), + ("BRCMG", "CORUMBÁ/LADÁRIO"), + ("BRCNV", "CANAVIEIRA"), + ("BRCQD", "CHARQUEADAS"), + ("BRCRQ", "CARAVELAS"), + ("BRCZS", "CRUZEIRO DO SUL - AC"), + ("BRERN", "EIRUNEPÉ"), + ("BRESP", "BACIA PETROLÍFERO DO ESPIRITO SANTO"), + ("BRETA", "ESTRELA"), + ("BRFEJ", "FEIJÓ"), + ("BRFEN", "SANTO ANTONIO - FERNANDO DE NORONHA"), + ("BRFLN", "FLORIANOPOLIS"), + ("BRFNO", "FORNO (ARRAIAL DO CABO)"), + ("BRFOA", "FONTE BOA"), + ("BRFOR", "FORTALEZA (MUCURIPE)"), + ("BRFOT", "BACIA PETROLÍFERA DE FORTALEZA"), + ("BRGIB", "GUAÍBA -RS"), + ("BRGJM", "GUAJARÁ-MIRIM (RO)"), + ("BRHMA", "HUMAITÁ"), + ("BRIBB", "IMBITUBA"), + ("PORTO", "DE EMBARQUE / DESTINO"), + ("BRIBE", "ILHABELA"), + ("BRIGI", "ITAGUAI ( EX SEPETIBA)"), + ("BRIGU", "FOZ DO IGUAÇU"), + ("BRIMM", "ITAMARATI"), + ("BRIOA", "ITAPOA"), + ("BRIOS", "ILHEUS"), + ("BRIPG", "IPIRANGA"), + ("BRIQI", "ITAQUI"), + ("BRITA", "ITACOATIARA"), + ("BRITB", "ITAITUBA"), + ("BRITJ", "ITAJAI"), + ("BRLBR", "LÁBREA"), + ("BRLDR", "LADÁRIO - MS"), + ("BRLIN", "LINDÓIA"), + ("BRLJI", "LARANJAL DO JARI"), + ("BRMAO", "MANAUS"), + ("BRMBZ", "MAUÉS"), + ("BRMCP", "SANTANA/MACAPÁ"), + ("BRMCU", "MACAU"), + ("BRMCZ", "MACEIÓ"), + ("BRMEA", "MACAÉ"), + ("BRMEA", "MACAÉ"), + ("BRMGU", "MUNGUBA"), + ("BRMHO", "SÃO LUÍS (MARANHÃO)"), + ("BRMNX", "MANICORÉ"), + ("BRMPR", "MANACAPURU"), + ("BRMRS", "MORRETES"), + ("BRMTE", "MONTE ALEGRE"), + ("BRNAT", "NATAL"), + ("BRNTR", "NITERÓI"), + ("BRNVP", "NOVO ARIPUANA"), + ("BRNVT", "NAVEGANTES"), + ("BROBI", "ÓBIDOS"), + ("BRORX", "ORIXIMINÁ"), + ("BROUT", "OUTEIRO"), + ("BRPAT", "PARATY"), + ("BRPBO", "PORTO BELO"), + ("BRPBX", "PORTO ALEGRE - PA"), + ("BRPEC", "PECEM"), + ("BRPEO", "PRESIDENTE EPITÁCIO-SP"), + ("BRPET", "PELOTAS"), + ("BRPIN", "PARINTINS"), + ("BRPIN", "PARINTINS"), + ("BRPJZ", "SANTANA"), + ("BRPKC", "PORTOCEL"), + ("BRPMA", "PONTA DA MADEIRA"), + ("BRPMH", "PORTO MURTINHO"), + ("BRPMR", "PALMEIRAS"), + ("BRPNA", "PANORAMA"), + ("BRPNG", "PARANAGUA"), + ("BRPOA", "PORTO ALEGRE - RS"), + ("BRPOU", "PONTA DO UBU"), + ("BRPPB", "PRESIDENTE PRUDENTE"), + ("BRPRM", "PRAIA MOLE"), + ("BRPTQ", "PORTO DE MOZ"), + ("BRPVH", "PORTO VELHO - RO"), + ("BRQAV", "BENJAMIN CONSTANT"), + ("BRQCK", "CABO FRIO"), + ("BRQNS", "CANOAS"), + ("BRRBB", "BORBA"), + ("BRRBR", "RIO BRANCO (RO)"), + ("BRRCH", "BARRA DO RIACHO - PORTOCEL"), + ("BRREC", "RECIFE"), + ("BRREL", "TERMINAL DE REGÊNCIA"), + ("BRRIA", "SANTA MARIA"), + ("BRRIG", "RIO GRANDE"), + ("BRRIO", "RIO DE JANEIRO"), + ("BRSAS", "BACIA PETROLIFERA DE SANTOS"), + ("BRSBE", "SAO BENEDITO"), + ("BRSFK", "SOURE"), + ("BRSFK", "SOURE"), + ("BRSFS", "SAO FRANCISCO DO SUL"), + ("BRSIV", "SILVES"), + ("BRSJL", "SAO GABRIEL DA CACHOEIRA"), + ("BRSLV", "SALVA TERRA"), + ("BRSNH", "SANTA HELENA"), + ("BRSOS", "SÃO SIMÃO-GO"), + ("BRSSA", "SALVADOR"), + ("BRSSO", "SÃO SEBASTIAO"), + ("BRSSZ", "SANTOS"), + ("BRSTM", "SANTAREM"), + ("BRSUA", "SUAPE"), + ("BRSWK", "SÃO PAULO DO OLIVENC"), + ("BRSYC", "SANTA CLARA"), + ("BRTAP", "TAPES"), + ("BRTBE", "SANTO ANTONIO DO ICA"), + ("BRTBT", "TABATINGA"), + ("BRTBT", "TABATINGA"), + ("BRTFF", "TEFÉ"), + ("BRTFO", "TRIUNFO"), + ("BRTHE", "TERESINA"), + ("BRTLG", "TRÊS LAGOAS"), + ("BRTMT", "TROMBETAS"), + ("BRTRM", "TRAMANDAÍ"), + ("BRTRQ", "TARAUACA"), + ("BRTUB", "PONTA DO TUBARÃO"), + ("BRTUR", "TUCURUÍ"), + ("BRUBT", "UBATUBA"), + ("BRURT", "URUCURITUBA"), + ("BRVDC", "VILA DO CONDE"), + ("BRVIX", "VITÓRIA"), + ("BRVVE", "CAPUABA"), + ("BRXX1", "PONTO DE ABASTECIMENTO - BANKER NAVIOS"), + ("BRZMD", "SENA MADUREIRA AC"), + ("BRZZZ", "MADRE DE DEUS"), + ("BR006", "MANOEL URBANO"), + ("BR007", "ALVARÃES"), + ("BR008", "AMATURA"), + ("BR009", "ANAJÁS"), + ("BR010", "ANAMÃ"), + ("BR011", "ANORÍ"), + ("BR012", "ATALAIA DO NORTE"), + ("BR013", "AUTAZES"), + ("BR014", "BAGRE"), + ("BR015", "BARCARENA"), + ("BR016", "BARCELOS"), + ("BR017", "BARREIRINHA"), + ("BR021", "BOCA DO ACRE"), + ("BR025", "BREVES"), + ("BR026", "CAAPIRANGA"), + ("BR027", "CACHOEIRA DO SUL"), + ("BR028", "CANUTAMA"), + ("BR030", "CAREIRO"), + ("BR031", "CHAVES"), + ("BR032", "GUAÍRA"), + ("BR035", "CUCUÍ"), + ("BR036", "CURRALINHO"), + ("BR038", "ENVIRA"), + ("BR040", "BOCA DO PURUS"), + ("BR042", "GURUPÁ"), + ("BR050", "JAPURÁ"), + ("BR051", "JARI"), + ("BR052", "JURUTI"), + ("BR053", "JURUÁ"), + ("BR054", "JUTAI"), + ("BR057", "MANACAPURU"), + ("BR058", "MANAQUIRI"), + ("BR060", "MARAÃ"), + ("BR064", "FONTE BOA"), + ("BR065", "MUANÁ"), + ("BR067", "NHAMUNDA"), + ("BR068", "NOVA OLINDA DO NORTE"), + ("BR069", "NOVO AIRÃO"), + ("BR071", "OEIRAS DO PARA"), + ("BR074", "PAUINÍ"), + ("BR075", "PORTEL"), + ("BR077", "PRAINHA"), + ("BR079", "SANTA CRUZ DO ARARI"), + ("BR081", "SANTA ISABEL DO RIO NEGRO"), + ("BR083", "SANTO ANTONIO DO IÇÁ"), + ("BR088", "SAO SEBASTIA0 DA BOA VISTA"), + ("BR095", "TAPAUÁ"), + ("BR097", "BAIÃO"), + ("BR098", "TERRA SANTA"), + ("BR099", "TONANTINS"), + ("BR101", "URUCARÁ"), + ("BR103", "VITORIA DO PARÁ"), + ("BR108", "CARNAMBEIRA"), + ("BR131", "SEPETIBA (ILHA DE JAGUANUM)"), + ("BR134", "SÃO FRANCISCO"), + ("BR147", "SANTA CLARA (TERMINAL)"), + ("BR149", "TAQUARI"), + ("BR151", "CÁCERES - MT"), + ("BR170", "CACHOEIRA DO ARARI"), + ("BR174", "CORUMBÁ"), + ("BR184", "PORTO MURTINHO - MS"), + ("BR185", "PORTO SAO FRANCISCO"), + ("BR192", "BERURI"), + ("BR193", "BOA VISTA DO RAMOS"), + ("BR195", "MARECHAL TAUMATURGO"), + ("BR196", "IAURETÊ"), + ("BR197", "IPIXUNA"), + ("BR202", "PONTA DE PEDRA"), + ("BR205", "PORTO MIGUEL DE OLIVEIRA"), + ("BR221", "CAREIRO DA VÁRZEA"), + ("BR224", "SANTA ROSA DO PURUS"), + ("BR225", "BELÉM DO SOLIMÕES"), + ("BR227", "GUAJARÁ"), + ("BR229", "SAO SEBATIAO UATUMA"), + ("BR235", "TERMINAL NORTE CAPIXABA"), + ("BR236", "APUI"), + ("BR238", "SANTA CRUZ DO SUL"), + ("BR239", "FPSO PIRANEMA"), + ("BR240", "SANTA RITA DO PURUS - AC"), + ("BR243", "JAGUARIAIVA"), + ("BR245", "URUCURITUBA"), + ("BR246", "FPSO CIDADE DE MACAÉ"), + ("BR252", "TERGUÁ (TERMINAL DE GUAMARÉ)"), + ("BR254", "LAGOA PARDA"), + ("BR260", "MATARIPE"), + ("BR267", "POÇO DE CARAVELA NORTE"), + ("BR280", "TURIAÇU"), + ("BR298", "CARACARAÍ (RR)"), + ("BR300", "TERMINAL AQUAVIARIO BARRA DO RIACHO-TRANSPETRO"), + ("BR301", "TERMINAL STA. MARIA DA SERRA"), + ("BR302", "NOVA SANTA RITA"), + ("BR306", "AMAPÁ"), + ("BR321", "CARAPEBA"), + ("BR339", "ALIANÇA"), + ("BR342", "SENADOR JOSE PORFÍRIO"), + ("BR371", "BRASILÉIA - AC"), + ("BR376", "PORTO ACRE - AC"), + ("BR386", "PORTO IRANDUBA"), + ("BR400", "PORTO DO AÇU"), + ("BR409", "MOURA"), + ("BR410", "UARINI"), + ("BR414", "FARO"), + ("BR444", "MELGAÇO"), + ("BR454", "ALTER DO CHÃO"), + ("BR456", "SAO SEBASTIÃO"), + ("BR462", "CAVIANA"), + ("BR500", "ESTIRAO DO EQUADOR"), + ("BR551", "SBM 04"), + ("BR553", "JUMA"), + ("BR602", "NITERÓI"), + ("BR696", "GREGÓRIO CURVO - MS"), + ("BR719", "MIRAMAR"), + ("BR723", "NATAL"), + ("BR728", "NOVO REMANSO"), + ("BR756", "FPSO P-54"), + ("BR768", "RIO LAMBARI - MG"), + ("BR778", "SÃO PAULO DE OLIVENÇA"), + ("BR793", "SANTANA"), + ("BR801", "CURUNA UNA"), + ("BR802", "SAO SEBASTIAO"), + ("BR821", "VILA DO CARMO"), + ("BR855", "JACARÉ GRANDE"), + ("BR900", "BOCA DA VALERIA"), + ("BR907", "URUCU"), + ("BR930", "TERMINAL MANAUS DA ENERGIA"), + ("BR938", "PORTO WALTER"), + ("BR940", "VITORIA DO XINGU"), + ("BR985", "PORTO DO RIO IGUACU TERMINAL E COMERCIO LTDA"), + ("BR997", "TERMINAL BELM"), +] diff --git a/l10n_br_mdfe/data/l10n_br_mdfe.modal.csv b/l10n_br_mdfe/data/l10n_br_mdfe.modal.csv new file mode 100644 index 000000000000..0eec84b570de --- /dev/null +++ b/l10n_br_mdfe/data/l10n_br_mdfe.modal.csv @@ -0,0 +1,5 @@ +"id","modal_type","name" +"modal_1","1","Rodoviário" +"modal_2","2","Aéreo" +"modal_3","3","Aquaviário" +"modal_4","4","Ferroviário" diff --git a/l10n_br_mdfe/models/__init__.py b/l10n_br_mdfe/models/__init__.py index 382a68d66fec..0867bb980fdb 100644 --- a/l10n_br_mdfe/models/__init__.py +++ b/l10n_br_mdfe/models/__init__.py @@ -3,3 +3,8 @@ from . import document_related from . import res_partner from . import res_config_settings +from . import modal +from . import modal_aquaviario +from . import modal_ferroviario +from . import modal_rodoviario +from . import document_info diff --git a/l10n_br_mdfe/models/document.py b/l10n_br_mdfe/models/document.py index 75c2c4282267..b70abef368ab 100644 --- a/l10n_br_mdfe/models/document.py +++ b/l10n_br_mdfe/models/document.py @@ -2,16 +2,31 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import re +from unicodedata import normalize -from erpbrasil.assinatura import certificado as cert -from erpbrasil.transmissao import TransmissaoSOAP +from nfelib.mdfe.bindings.v3_0.mdfe_modal_aereo_v3_00 import Aereo +from nfelib.mdfe.bindings.v3_0.mdfe_modal_aquaviario_v3_00 import Aquav +from nfelib.mdfe.bindings.v3_0.mdfe_modal_ferroviario_v3_00 import Ferrov +from nfelib.mdfe.bindings.v3_0.mdfe_modal_rodoviario_v3_00 import Rodo +from nfelib.mdfe.bindings.v3_0.mdfe_v3_00 import Mdfe from nfelib.nfe.ws.edoc_legacy import MDFeAdapter as edoc_mdfe -from requests import Session -from odoo import _, api, fields -from odoo.exceptions import UserError +from odoo import api, fields -from odoo.addons.l10n_br_fiscal.constants.fiscal import MODELO_FISCAL_MDFE +from odoo.addons.l10n_br_fiscal.constants.fiscal import ( + EVENT_ENV_HML, + EVENT_ENV_PROD, + MODELO_FISCAL_MDFE, + PROCESSADOR_OCA, +) +from odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_modal_aquaviario_v3_00 import ( + AQUAV_TPNAV, +) +from odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_modal_rodoviario_v3_00 import ( + VALEPED_CATEGCOMBVEIC, + VEICTRACAO_TPCAR, + VEICTRACAO_TPROD, +) from odoo.addons.spec_driven_model.models import spec_models from ..constants.mdfe import ( @@ -19,16 +34,21 @@ MDFE_EMISSION_PROCESSES, MDFE_EMIT_TYPES, MDFE_ENVIRONMENTS, - MDFE_MODAL_DEFAULT, - MDFE_MODALS, MDFE_TRANSMISSIONS, MDFE_TRANSP_TYPE, ) +from ..constants.modal import ( + MDFE_MODAL_DEFAULT, + MDFE_MODAL_DEFAULT_AIRCRAFT, + MDFE_MODAL_HARBORS, + MDFE_MODAL_SHIP_TYPES, + MDFE_MODALS, +) def filtered_processador_edoc_mdfe(record): return ( - record.processador_edoc == "oca" + record.processador_edoc == PROCESSADOR_OCA and record.document_type_id.code == MODELO_FISCAL_MDFE ) @@ -46,6 +66,9 @@ class MDFe(spec_models.StackedModel): _spec_tab_name = "MDFe" _mdfe_search_keys = ["mdfe30_Id"] + # all m2o at this level will be stacked even if not required: + _force_stack_paths = "infmdfe.infAdic" + INFMDFE_TREE = """ > > @@ -185,6 +208,20 @@ def _inverse_mdfe30_id_tag(self): mdfe30_cDV = fields.Char(related="key_check_digit") + mdfe30_infMunCarrega = fields.One2many(compute="_compute_inf_carrega") + + mdfe_loading_city_ids = fields.Many2many( + comodel_name="res.city", string="Loading Cities" + ) + + mdfe30_infPercurso = fields.One2many(compute="_compute_inf_percurso") + + mdfe_route_state_ids = fields.Many2many( + comodel_name="res.country.state", + string="Route States", + domain=[("country_id.code", "=", "BR")], + ) + ########################## # MDF-e tag: ide # Methods @@ -198,24 +235,8 @@ def _compute_uf(self): @api.depends("mdfe_initial_state_id", "mdfe_final_state_id") def _compute_initial_final_state(self): for record in self.filtered(filtered_processador_edoc_mdfe): - record.mdfe30_UFIni = record.mdfe_initial_state_id.ibge_code - record.mdfe30_UFFim = record.mdfe_final_state_id.ibge_code - - ########################## - # MDF-e tag: infMunCarrega - ########################## - - # TODO: is this right? - mdfe30_infMunCarrega = fields.One2many(compute="_compute_inf_carrega") - - mdfe_loading_city_ids = fields.Many2many( - comodel_name="res.city", string="Loading Cities" - ) - - ########################## - # MDF-e tag: infMunCarrega - # Methods - ########################## + record.mdfe30_UFIni = record.mdfe_initial_state_id.code + record.mdfe30_UFFim = record.mdfe_final_state_id.code @api.depends("mdfe_loading_city_ids") def _compute_inf_carrega(self): @@ -232,44 +253,324 @@ def _compute_inf_carrega(self): for city in record.mdfe_loading_city_ids ] + @api.depends("mdfe_route_state_ids") + def _compute_inf_percurso(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_infPercurso = [ + ( + 0, + 0, + { + "mdfe30_UFPer": state.code, + }, + ) + for state in record.mdfe_route_state_ids + ] + ########################## - # MDF-e tag: infPercurso + # MDF-e tag: emit ########################## - # TODO: is this right? - mdfe30_infPercurso = fields.One2many(compute="_compute_inf_percurso") + mdfe30_emit = fields.Many2one(comodel_name="res.company", related="company_id") - mdfe_route_state_ids = fields.Many2many( + ########################## + # MDF-e tag: infModal + ########################## + + mdfe30_infModal = fields.Many2one( + comodel_name="l10n_br_mdfe.modal", + string="Modal Info", + compute="_compute_modal_inf", + ) + + mdfe30_versaoModal = fields.Char(related="mdfe30_infModal.mdfe30_versaoModal") + + # Campos do Modal Aéreo + airplane_nationality = fields.Char() + + airplane_registration = fields.Char() + + flight_number = fields.Char() + + flight_date = fields.Date() + + boarding_airfield = fields.Char(default=MDFE_MODAL_DEFAULT_AIRCRAFT) + + landing_airfield = fields.Char(default=MDFE_MODAL_DEFAULT_AIRCRAFT) + + # Campos do Modal Aquaviário + ship_irin = fields.Char() + + ship_type = fields.Selection(selection=MDFE_MODAL_SHIP_TYPES) + + ship_code = fields.Char() + + ship_name = fields.Char() + + ship_travel_number = fields.Char() + + ship_boarding_point = fields.Selection(selection=MDFE_MODAL_HARBORS) + + ship_landing_point = fields.Selection(selection=MDFE_MODAL_HARBORS) + + transshipment_port = fields.Char() + + ship_navigation_type = fields.Selection(selection=AQUAV_TPNAV) + + ship_loading_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.carregamento", + inverse_name="document_id", + ) + + ship_unloading_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.descarregamento", + inverse_name="document_id", + ) + + ship_convoy_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.comboio", inverse_name="document_id" + ) + + ship_empty_load_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.carga.vazia", + inverse_name="document_id", + ) + + ship_empty_transport_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.transporte.vazio", + inverse_name="document_id", + ) + + # Campos do Modal Ferroviário + train_prefix = fields.Char(string="Train Prefix") + + train_release_time = fields.Datetime(string="Train Release Time") + + train_origin = fields.Char(string="Train Origin") + + train_destiny = fields.Char(string="Train Destiny") + + train_wagon_quantity = fields.Char(string="Train Wagon Quantity") + + train_wagon_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.ferroviario.vagao", inverse_name="document_id" + ) + + # Campos do Modal Rodoviário + rodo_scheduling_code = fields.Char(string="Scheduling Code") + + rodo_ciot_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.ciot", inverse_name="document_id" + ) + + rodo_toll_device_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.vale_pedagio.dispositivo", + inverse_name="document_id", + ) + + rod_toll_vehicle_categ = fields.Selection(selection=VALEPED_CATEGCOMBVEIC) + + rodo_contractor_ids = fields.Many2many(comodel_name="res.partner") + + rodo_RNTRC = fields.Char() + + rodo_payment_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.pagamento", + inverse_name="document_id", + ) + + rodo_vehicle_proprietary_id = fields.Many2one(comodel_name="res.partner") + + rodo_vehicle_conductor_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.veiculo.condutor", + inverse_name="document_id", + ) + + rodo_vehicle_code = fields.Char() + + rodo_vehicle_RENAVAM = fields.Char() + + rodo_vehicle_plate = fields.Char() + + rodo_vehicle_tare_weight = fields.Char() + + rodo_vehicle_kg_capacity = fields.Char() + + rodo_vehicle_m3_capacity = fields.Char() + + rodo_vehicle_tire_type = fields.Selection(selection=VEICTRACAO_TPROD) + + rodo_vehicle_type = fields.Selection(selection=VEICTRACAO_TPCAR) + + rodo_vehicle_state_id = fields.Many2one( comodel_name="res.country.state", - string="Route States", + string="Vehicle State", domain=[("country_id.code", "=", "BR")], ) + rodo_tow_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.reboque", inverse_name="document_id" + ) + + rodo_seal_ids = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.lacre", inverse_name="document_id" + ) + ########################## - # MDF-e tag: infPercurso + # MDF-e tag: infModal # Methods ########################## - @api.depends("mdfe_route_state_ids") - def _compute_inf_percurso(self): + @api.depends("mdfe_modal") + def _compute_modal_inf(self): for record in self.filtered(filtered_processador_edoc_mdfe): - record.mdfe30_infPercurso = [ + record.mdfe30_infModal = self.env.ref( + "l10n_br_mdfe.modal_%s" % record.mdfe_modal + ) + + def _export_fields_mdfe_30_infmodal(self, xsd_fields, class_obj, export_dict): + if self.mdfe_modal == "1": + export_dict["any_element"] = self._export_modal_rodoviario_fields() + elif self.mdfe_modal == "2": + export_dict["any_element"] = self._export_modal_aereo_fields() + elif self.mdfe_modal == "3": + export_dict["any_element"] = self._export_modal_aquaviario_fields() + elif self.mdfe_modal == "4": + export_dict["any_element"] = self._export_modal_ferroviario_fields() + + def _export_modal_aereo_fields(self): + return Aereo( + nac=self.airplane_nationality, + matr=self.airplane_registration, + nVoo=self.flight_number, + dVoo=fields.Date.to_string(self.flight_date), + cAerEmb=self.boarding_airfield, + cAerDes=self.landing_airfield, + ) + + def _export_modal_aquaviario_fields(self): + return Aquav( + irin=self.ship_irin, + tpEmb=self.ship_type, + cEmbar=self.ship_code, + xEmbar=self.ship_name, + cPrtEmb=self.ship_boarding_point, + cPrtDest=self.ship_landing_point, + prtTrans=self.transshipment_port, + tpNav=self.ship_navigation_type, + infTermCarreg=[ + Aquav.InfTermCarreg(**loading.export_fields()) + for loading in self.ship_loading_ids + ], + infTermDescarreg=[ + Aquav.InfTermDescarreg(**unloading.export_fields()) + for unloading in self.ship_unloading_ids + ], + infEmbComb=[ + Aquav.InfEmbComb(**convoy.export_fields()) + for convoy in self.ship_convoy_ids + ], + infUnidCargaVazia=[ + Aquav.infUnidCargaVazia(**load.export_fields()) + for load in self.ship_empty_load_ids + ], + infUnidTranspVazia=[ + Aquav.infUnidTranspVazia(**transp.export_fields()) + for transp in self.ship_empty_transport_ids + ], + ) + + def _export_modal_ferroviario_fields(self): + return Ferrov( + trem=Ferrov.Trem( + xPref=self.train_prefix, + dhTrem=fields.Datetime.to_string(self.train_release_time), + xOri=self.train_origin, + xDest=self.train_destiny, + qVag=self.train_wagon_quantity, + ), + vag=[Ferrov.Vag(**vag.export_fields()) for vag in self.train_wagon_ids], + ) + + def _export_modal_rodoviario_fields(self): + return Rodo( + codAgPorto=self.rodo_scheduling_code, + infANTT=Rodo.infANTT( + RNTRC=self.rodo_RNTRC, + infCIOT=self.rodo_ciot_ids.export_fields(), + valePed=Rodo.InfAntt.ValePed( + disp=[ + Rodo.InfAntt.ValePed.Disp(**dev.export_fields()) + for dev in self.rodo_toll_device_ids + ], + categCombVeic=self.rod_toll_vehicle_categ, + ), + infContratante=[ + Rodo.InfAntt.infContratante(**contr.export_contractor_fields()) + for contr in self.rodo_contractor_ids + ], + infPag=[ + Rodo.InfAntt.infPag(**pay.export_contractor_fields()) + for pay in self.rodo_payment_ids + ], + ), + lacRodo=[ + Rodo.infANTT.lacRodo(**seal.export_fields()) + for seal in self.rodo_seal_ids + ], + veicReboque=[ + Rodo.veicReboque(**tow.export_fields()) for tow in self.rodo_tow_ids + ], + veicTracao=Rodo.veicTracao( + cInt=self.rodo_vehicle_code, + placa=self.rodo_vehicle_plate, + RENAVAM=self.rodo_vehicle_RENAVAM, + tara=self.rodo_vehicle_tare_weight, + capKG=self.rodo_vehicle_kg_capacity, + capM3=self.rodo_vehicle_m3_capacity, + tpRod=self.rodo_vehicle_tire_type, + tpCar=self.rodo_vehicle_type, + UF=self.rodo_vehicle_state_id.code, + condutor=[ + Rodo.veicTracao.condutor(**cond.export_fields()) + for cond in self.rodo_vehicle_conductor_ids + ], + prop=[ + Rodo.veicTracao.prop(**prop.export_proprietary_fields()) + for prop in self.rodo_vehicle_proprietary_id + ], + ), + ) + + ########################## + # MDF-e tag: infDoc + ########################## + + mdfe30_infDoc = fields.One2many( + comodel_name="l10n_br_mdfe.document.info", compute="_compute_doc_inf" + ) + + unloading_city_ids = fields.One2many( + comodel_name="l10n_br_mdfe.municipio.descarga", inverse_name="document_id" + ) + + ########################## + # MDF-e tag: infDoc + # Methods + ########################## + + @api.depends("unloading_city_ids") + def _compute_doc_inf(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_infDoc = [(5, 0, 0)] + record.mdfe30_infDoc = [ ( 0, 0, - { - "mdfe30_UFPer": city.ibge_code, - }, + {"mdfe30_infMunDescarga": [(6, 0, record.unloading_city_ids.ids)]}, ) - for city in record.mdfe_route_state_ids ] - ########################## - # MDF-e tag: emit - ########################## - - mdfe30_emit = fields.Many2one(comodel_name="res.company", related="company_id") - ########################## # MDF-e tag: infRespTec ########################## @@ -279,6 +580,73 @@ def _compute_inf_percurso(self): related="company_id.technical_support_id", ) + ########################## + # NF-e tag: infAdic + ########################## + + mdfe30_infAdFisco = fields.Char(compute="_compute_mdfe30_additional_data") + + mdfe30_infCpl = fields.Char(compute="_compute_mdfe30_additional_data") + + ########################## + # MDF-e tag: infAdic + # Methods + ########################## + + @api.depends("fiscal_additional_data") + def _compute_mdfe30_additional_data(self): + for record in self: + record.mdfe30_infCpl = False + record.mdfe30_infAdFisco = False + + if record.fiscal_additional_data: + record.mdfe30_infAdFisco = ( + normalize("NFKD", record.fiscal_additional_data) + .encode("ASCII", "ignore") + .decode("ASCII") + .replace("\n", "") + .replace("\r", "") + ) + if record.customer_additional_data: + record.mdfe30_infCpl = ( + normalize("NFKD", record.customer_additional_data) + .encode("ASCII", "ignore") + .decode("ASCII") + .replace("\n", "") + .replace("\r", "") + ) + + ########################## + # MDF-e tag: autXML + ########################## + + def _default_mdfe30_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 + + mdfe30_autXML = fields.One2many(default=_default_mdfe30_autxml) + + ################################ + # Framework Spec model's methods + ################################ + + def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + if xsd_field == "mdfe30_tpAmb": + self.env.context = dict(self.env.context) + self.env.context.update({"tpAmb": self[xsd_field]}) + return super()._export_field(xsd_field, class_obj, member_spec, export_value) + + def _export_many2one(self, field_name, xsd_required, class_obj=None): + if field_name == "mdfe30_infModal": + return self._build_generateds(class_obj._fields[field_name].comodel_name) + + return super()._export_many2one(field_name, xsd_required, class_obj) + def _build_attr(self, node, fields, vals, path, attr): key = "mdfe30_%s" % (attr[0],) # TODO schema wise value = getattr(node, attr[0]) @@ -292,27 +660,48 @@ def _build_attr(self, node, fields, vals, path, attr): return super()._build_attr(node, fields, vals, path, attr) - def _processador(self): - certificate = False - if self.company_id.sudo().certificate_nfe_id: - certificate = self.company_id.sudo().certificate_nfe_id - elif self.company_id.sudo().certificate_ecnpj_id: - certificate = self.company_id.sudo().certificate_ecnpj_id - - if not certificate: - raise UserError(_("Certificado não encontrado")) - - certificado = cert.Certificado( - arquivo=certificate.file, - senha=certificate.password, - ) - session = Session() - session.verify = False - params = { - "transmissao": TransmissaoSOAP(certificado, session), - "uf": self.company_id.state_id.ibge_code, - "versao": self.nfe_version, - "ambiente": self.nfe_environment, - } + ################################ + # Business Model Methods + ################################ + + def _serialize(self, edocs): + edocs = super()._serialize(edocs) + for record in self.with_context(lang="pt_BR").filtered( + filtered_processador_edoc_mdfe + ): + inf_mdfe = record.export_ds()[0] + mdfe = Mdfe(infMDFe=inf_mdfe, infMDFeSupl=None, signature=None) + edocs.append(mdfe) + return edocs + def _processador(self): + params = self._prepare_processor_params() return edoc_mdfe(**params) + + def _document_export(self, pretty_print=True): + result = super()._document_export() + for record in self.filtered(filtered_processador_edoc_mdfe): + edoc = record.serialize()[0] + processador = record._processador() + xml_file = processador.render_edoc_xsdata(edoc, pretty_print=pretty_print)[ + 0 + ] + event_id = self.event_ids.create_event_save_xml( + company_id=self.company_id, + environment=( + EVENT_ENV_PROD if self.nfe_environment == "1" else EVENT_ENV_HML + ), + event_type="0", + xml_file=xml_file, + document_id=self, + ) + record.authorization_event_id = event_id + xml_assinado = processador.assina_raiz(edoc, edoc.infNFe.Id) + self._valida_xml(xml_assinado) + return result + + def _valida_xml(self, xml_file): + self.ensure_one() + erros = Mdfe.schema_validation(xml_file) + erros = "\n".join(erros) + self.write({"xml_error_message": erros or False}) diff --git a/l10n_br_mdfe/models/document_info.py b/l10n_br_mdfe/models/document_info.py new file mode 100644 index 000000000000..e31c411ba17a --- /dev/null +++ b/l10n_br_mdfe/models/document_info.py @@ -0,0 +1,94 @@ +# Copyright 2023 KMEE +# 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 MDFeDocumentInfo(spec_models.SpecModel): + _name = "l10n_br_mdfe.document.info" + _inherit = "mdfe.30.tmdfe_infdoc" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_infMunDescarga = fields.One2many( + comodel_name="l10n_br_mdfe.municipio.descarga" + ) + + +class MDFeMunicipioDescarga(spec_models.SpecModel): + _name = "l10n_br_mdfe.municipio.descarga" + _inherit = "mdfe.30.infmundescarga" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_cMunDescarga = fields.Char(compute="_compute_city_data") + + mdfe30_xMunDescarga = fields.Char(compute="_compute_city_data") + + mdfe30_infCTe = fields.One2many(compute="_compute_document_data") + + mdfe30_infNFe = fields.One2many(compute="_compute_document_data") + + country_id = fields.Many2one( + comodel_name="res.country.state", + default=lambda self: self.env.ref("base.br"), + ) + + state_id = fields.Many2one( + comodel_name="res.country.state", + string="State", + domain="[('country_id', '=', country_id)]", + required=True, + ) + + city_id = fields.Many2one( + string="City", + comodel_name="res.city", + domain="[('state_id', '=', state_id)]", + required=True, + ) + + document_type = fields.Selection( + selection=[ + ("nfe", "NF-e"), + ("cte", "CT-e"), + ], + string="Document Type", + default="nfe", + ) + + nfe_ids = fields.Many2many( + comodel_name="l10n_br_fiscal.document.related", + relation="mdfe_related_nfe_carregamento_rel", + ) + + cte_ids = fields.Many2many( + comodel_name="l10n_br_fiscal.document.related", + relation="mdfe_related_cte_carregamento_rel", + ) + + @api.depends("city_id") + def _compute_city_data(self): + for record in self: + record.mdfe30_cMunDescarga = record.city_id.ibge_code + record.mdfe30_xMunDescarga = record.city_id.name + + @api.depends("document_type", "nfe_ids", "cte_ids") + def _compute_document_data(self): + for record in self: + record.mdfe30_infCTe = [(5, 0, 0)] + record.mdfe30_infNFe = [(5, 0, 0)] + + if not record.document_type: + continue + + if record.document_type == "nfe": + record.mdfe30_infNFe = [ + (0, 0, {"mdfe30_chNFe": nfe.mdfe30_chNFe}) for nfe in record.nfe_ids + ] + else: + record.mdfe30_infCTe = [ + (0, 0, {"mdfe30_chCTe": cte.mdfe30_chCTe}) for cte in record.cte_ids + ] diff --git a/l10n_br_mdfe/models/document_related.py b/l10n_br_mdfe/models/document_related.py index 1fa5f9158de6..97dd8c0939fc 100644 --- a/l10n_br_mdfe/models/document_related.py +++ b/l10n_br_mdfe/models/document_related.py @@ -1,6 +1,8 @@ # 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 @@ -19,5 +21,8 @@ class MDFeRelated(spec_models.StackedModel): _odoo_module = "l10n_br_mdfe" _spec_module = "odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_tipos_basico_v3_00" _spec_tab_name = "MDFe" + _stack_skip = ("mdfe30_infNFe_infMunDescarga_id", "mdfe30_infCTe_infMunDescarga_id") + + mdfe30_chNFe = fields.Char(related="document_id.document_key") - # TODO + mdfe30_chCTe = fields.Char(related="document_id.document_key") diff --git a/l10n_br_mdfe/models/modal.py b/l10n_br_mdfe/models/modal.py new file mode 100644 index 000000000000..a1cd7aef847e --- /dev/null +++ b/l10n_br_mdfe/models/modal.py @@ -0,0 +1,28 @@ +# 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 + +from ..constants.modal import ( + MDFE_MODAL_DEFAULT, + MDFE_MODAL_VERSION_DEFAULT, + MDFE_MODALS, +) + + +class MDFeModal(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal" + _inherit = "mdfe.30.infmodal" + _mdfe_search_keys = ["mdfe30_versaoModal"] + + mdfe30_versaoModal = fields.Char( + string="Modal Version", default=MDFE_MODAL_VERSION_DEFAULT + ) + + name = fields.Char(string="Modal Name") + + modal_type = fields.Selection( + selection=MDFE_MODALS, string="Modal Type", default=MDFE_MODAL_DEFAULT + ) diff --git a/l10n_br_mdfe/models/modal_aquaviario.py b/l10n_br_mdfe/models/modal_aquaviario.py new file mode 100644 index 000000000000..9dee796ccc6b --- /dev/null +++ b/l10n_br_mdfe/models/modal_aquaviario.py @@ -0,0 +1,148 @@ +# Copyright 2023 KMEE +# 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.modal import MDFE_MODAL_HARBORS + + +class MDFeModalAquaviario(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.aquaviario" + _inherit = "mdfe.30.aquav" + _mdfe_search_keys = ["mdfe30_irin", "mdfe30_cEmbar", "mdfe30_nViag"] + + mdfe30_infTermCarreg = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.carregamento" + ) + + mdfe30_infTermDescarreg = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.descarregamento" + ) + + mdfe30_infEmbComb = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.comboio" + ) + + mdfe30_infUnidCargaVazia = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.carga.vazia" + ) + + mdfe30_infUnidTranspVazia = fields.One2many( + comodel_name="l10n_br_mdfe.modal.aquaviario.transporte.vazio" + ) + + +class MDFeModalAquaviarioCarregamento(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.aquaviario.carregamento" + _inherit = "mdfe.30.inftermcarreg" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + loading_harbor = fields.Selection( + selection=MDFE_MODAL_HARBORS, string="Loading Harbor", required=True + ) + + mdfe30_cTermCarreg = fields.Char(compute="_compute_loading_harbor") + + mdfe30_xTermCarreg = fields.Char(compute="_compute_loading_harbor") + + @api.depends("loading_harbor") + def _compute_loading_harbor(self): + for record in self: + record.mdfe30_cTermCarreg = record.loading_harbor + record.mdfe30_xTermCarreg = dict( + self._fields["loading_harbor"].selection + ).get(record.loading_harbor) + + @api.model + def export_fields(self): + return { + "cTermCarreg": self.mdfe30_cTermCarreg, + "xTermCarreg": self.mdfe30_xTermCarreg, + } + + +class MDFeModalAquaviarioDescarregamento(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.aquaviario.descarregamento" + _inherit = "mdfe.30.inftermdescarreg" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + unloading_harbor = fields.Selection( + selection=MDFE_MODAL_HARBORS, string="Unloading Harbor", required=True + ) + + mdfe30_cTermDescarreg = fields.Char(compute="_compute_unloading_harbor") + + mdfe30_xTermDescarreg = fields.Char(compute="_compute_unloading_harbor") + + @api.depends("unloading_harbor") + def _compute_unloading_harbor(self): + for record in self: + record.mdfe30_cTermDescarreg = record.unloading_harbor + record.mdfe30_xTermDescarreg = dict( + self._fields["unloading_harbor"].selection + ).get(record.unloading_harbor) + + @api.model + def export_fields(self): + return { + "cTermDescarreg": self.mdfe30_cTermDescarreg, + "xTermDescarreg": self.mdfe30_xTermDescarreg, + } + + +class MDFeModalAquaviarioComboio(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.aquaviario.comboio" + _inherit = "mdfe.30.infembcomb" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_cEmbComb = fields.Char(required=True) + + mdfe30_xBalsa = fields.Char(required=True) + + @api.model + def export_fields(self): + return { + "cEmbComb": self.mdfe30_cEmbComb, + "xBalsa": self.mdfe30_xBalsa, + } + + +class MDFeModalAquaviarioCargaVazia(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.aquaviario.carga.vazia" + _inherit = "mdfe.30.infunidcargavazia" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_idUnidCargaVazia = fields.Char(required=True) + + mdfe30_tpUnidCargaVazia = fields.Selection(required=True) + + @api.model + def export_fields(self): + return { + "idUnidCargaVazia": self.mdfe30_idUnidCargaVazia, + "tpUnidCargaVazia": self.mdfe30_tpUnidCargaVazia, + } + + +class MDFeModalAquaviarioTranporteVazio(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.aquaviario.transporte.vazio" + _inherit = "mdfe.30.infunidtranspvazia" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_idUnidTranspVazia = fields.Char(required=True) + + mdfe30_tpUnidTranspVazia = fields.Selection(required=True) + + @api.model + def export_fields(self): + return { + "idUnidTranspVazia": self.mdfe30_idUnidTranspVazia, + "tpUnidTranspVazia": self.mdfe30_tpUnidTranspVazia, + } diff --git a/l10n_br_mdfe/models/modal_ferroviario.py b/l10n_br_mdfe/models/modal_ferroviario.py new file mode 100644 index 000000000000..d2a5c3a9beb9 --- /dev/null +++ b/l10n_br_mdfe/models/modal_ferroviario.py @@ -0,0 +1,59 @@ +# Copyright 2023 KMEE +# 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 MDFeModalFerroviario(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.ferroviario" + _inherit = "mdfe.30.ferrov" + + mdfe30_trem = fields.Many2one( + comodel_name="l10n_br_mdfe.modal.ferroviario.trem", required=True + ) + + mdfe30_vag = fields.One2many(comodel_name="l10n_br_mdfe.modal.ferroviario.vagao") + + +class MDFeModalFerroviarioTrem(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.ferroviario.trem" + _inherit = "mdfe.30.trem" + + mdfe30_xPref = fields.Char(required=True) + + mdfe30_xOri = fields.Char(required=True) + + mdfe30_xDest = fields.Char(required=True) + + mdfe30_qVag = fields.Char(required=True) + + +class MDFeModalFerroviarioVagao(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.ferroviario.vagao" + _inherit = "mdfe.30.vag" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_pesoBC = fields.Float(required=True) + + mdfe30_pesoR = fields.Float(required=True) + + mdfe30_serie = fields.Char(required=True) + + mdfe30_nVag = fields.Char(required=True) + + mdfe30_TU = fields.Char(required=True) + + @api.model + def export_fields(self): + return { + "pesoBC": self.mdfe30_pesoBC, + "pesoR": self.mdfe30_pesoR, + "tpVag": self.mdfe30_tpVag, + "serie": self.mdfe30_serie, + "nVag": self.mdfe30_nVag, + "nSeq": self.mdfe30_nSeq, + "TU": self.mdfe30_TU, + } diff --git a/l10n_br_mdfe/models/modal_rodoviario.py b/l10n_br_mdfe/models/modal_rodoviario.py new file mode 100644 index 000000000000..510687a2963e --- /dev/null +++ b/l10n_br_mdfe/models/modal_rodoviario.py @@ -0,0 +1,358 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from erpbrasil.base.misc import punctuation_rm + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class MDFeModalRodoviario(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario" + _inherit = "mdfe.30.rodo" + _mdfe_search_keys = ["mdfe30_codAgPorto"] + + mdfe30_infANTT = fields.Many2one(comodel_name="l10n_br_mdfe.modal.rodoviario.antt") + + mdfe30_veicTracao = fields.Many2one( + comodel_name="l10n_br_mdfe.modal.rodoviario.veiculo.tracao", required=True + ) + + mdfe30_veicReboque = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.reboque" + ) + + mdfe30_lacRodo = fields.One2many(comodel_name="l10n_br_mdfe.modal.rodoviario.lacre") + + +class MDFeModalRodoviarioANTT(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.antt" + _inherit = "mdfe.30.infantt" + _mdfe_search_keys = ["mdfe30_RNTRC"] + + mdfe30_infCIOT = fields.One2many(comodel_name="l10n_br_mdfe.modal.rodoviario.ciot") + + mdfe30_valePed = fields.Many2one( + comodel_name="l10n_br_mdfe.modal.rodoviario.vale_pedagio" + ) + + mdfe30_infContratante = fields.One2many(comodel_name="res.partner") + + mdfe30_infPag = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.pagamento" + ) + + +class MDFeModalRodoviarioCIOT(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.ciot" + _inherit = "mdfe.30.infciot" + _mdfe_search_keys = ["mdfe30_CIOT"] + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_CIOT = fields.Char(required=True) + + is_company = fields.Boolean(string="É empresa?", required=True) + + mdfe30_CNPJ = fields.Char(string="CNPJ do responsável") + + mdfe30_CPF = fields.Char(string="CPF do responsável") + + mdfe30_choice1 = fields.Selection( + selection=[("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], + string="CNPJ/CPF do responsável", + compute="_compute_ciot_choice", + ) + + @api.depends("is_company") + def _compute_ciot_choice(self): + for record in self: + record.mdfe30_choice1 = "mdfe30_CNPJ" if record.is_company else "mdfe30_CPF" + + @api.model + def export_fields(self): + cnpj_cpf_data = {} + if self.is_company: + cnpj_cpf_data["CNPJ"]: self.mdfe30_CNPJ + else: + cnpj_cpf_data["CPF"]: self.mdfe30_CPF + + return {"CIOT": self.mdfe30_CIOT, **cnpj_cpf_data} + + +class MDFeModalRodoviarioValePedagio(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.vale_pedagio" + _inherit = "mdfe.30.valeped" + + mdfe30_disp = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.vale_pedagio.dispositivo" + ) + + +class MDFeModalRodoviarioValePedagioDispositivo(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.vale_pedagio.dispositivo" + _inherit = "mdfe.30.disp" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_CNPJForn = fields.Char(required=True) + + mdfe30_vValePed = fields.Monetary(required=True) + + @api.model + def export_fields(self): + return { + "CNPJForn": self.mdfe30_CNPJForn, + "CNPJPg": self.mdfe30_CNPJPg, + "CPFPg": self.mdfe30_CPFPg, + "nCompra": self.mdfe30_nCompra, + "vValePed": self.mdfe30_vValePed, + "tpValePed": self.mdfe30_tpValePed, + } + + +class MDFeModalRodoviarioPagamento(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.pagamento" + _inherit = "mdfe.30.rodo_infpag" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + partner_id = fields.Many2one( + comodel_name="res.partner", string="Responsável pelo pagamento", required=True + ) + + mdfe30_CNPJ = fields.Char(related="partner_id.mdfe30_CNPJ") + + mdfe30_CPF = fields.Char(related="partner_id.mdfe30_CPF") + + mdfe30_xNome = fields.Char(related="partner_id.mdfe30_xNome") + + mdfe30_choice1 = fields.Selection( + selection=[ + ("mdfe30_CNPJ", "CNPJ"), + ("mdfe30_CPF", "CPF"), + ("mdfe40_idEstrangeiro", "idEstrangeiro"), + ], + string="CNPJ/CPF/Id Estrangeiro do responsável pelo pagamento", + compute="_compute_mdfe_data", + ) + + mdfe30_comp = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.pagamento.frete" + ) + + mdfe30_infPrazo = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.pagamento.prazo" + ) + + mdfe30_vContrato = fields.Monetary(required=True) + + mdfe30_indPag = fields.Selection(required=True) + + mdfe30_infBanc = fields.Many2one( + comodel_name="l10n_br_mdfe.modal.rodoviario.pagamento.banco", required=True + ) + + @api.depends("partner_id") + def _compute_mdfe_data(self): + for rec in self: + cnpj_cpf = punctuation_rm(rec.partner_id.cnpj_cpf) + if cnpj_cpf: + if rec.partner_id.country_id.code != "BR": + rec.mdfe30_choice1 = "mdfe40_idEstrangeiro" + elif rec.partner_id.is_company: + rec.mdfe30_choice1 = "mdfe30_CNPJ" + else: + rec.mdfe30_choice1 = "mdfe30_CPF" + else: + rec.mdfe30_choice1 = False + + @api.model + def export_fields(self): + cnpj_cpf_data = {} + if self.mdfe30_choice1 == "mdfe30_idEstrangeiro": + cnpj_cpf_data["idEstrangeiro"] = self.mdfe30_idEstrangeiro + elif self.mdfe30_choice1 == "mdfe30_CNPJ": + cnpj_cpf_data["CNPJ"]: self.mdfe30_CNPJ + else: + cnpj_cpf_data["CPF"]: self.mdfe30_CPF + + return { + "xNome": self.mdfe30_xNome, + "comp": self.mdfe30_comp.export_fields(), + "vContrato": self.mdfe30_vContrato, + "indAltoDesemp": self.mdfe30_indAltoDesemp, + "indPag": self.mdfe30_indPag, + "vAdiant": self.mdfe30_vAdiant, + "infPrazo": self.mdfe30_infPrazo.export_fields(), + "infBanc": self.mdfe30_infBanc.export_fields(), + **cnpj_cpf_data, + } + + +class MDFeModalRodoviarioPagamentoBanco(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.pagamento.banco" + _inherit = "mdfe.30.rodo_infbanc" + + payment_type = fields.Selection( + selection=[ + ("bank", "Banco"), + ("pix", "PIX"), + ], + string="Meio de Pagamento", + default="bank", + ) + + mdfe30_choice1 = fields.Selection( + selection=[ + ("mdfe30_codBanco", "Banco"), + ("mdfe30_codAgencia", "Agencia"), + ("mdfe30_CNPJIPEF", "CNPJ"), + ("mdfe30_PIX", "PIX"), + ], + string="Método de Pagamento", + compute="_compute_payment_type", + ) + + @api.depends("payment_type") + def _compute_payment_type(self): + for record in self: + if record.payment_type == "bank": + record.mdfe30_choice1 = "mdfe30_codBanco" + elif record.payment_type == "pix": + record.mdfe30_choice1 = "mdfe30_PIX" + else: + record.mdfe30_choice1 = False + + @api.model + def export_fields(self): + bank_data = {} + if self.mdfe30_choice1 == "mdfe30_codBanco": + bank_data["codBanco"] = self.mdfe30_codBanco + bank_data["codAgencia"] = self.mdfe30_codAgencia + elif self.mdfe30_choice1 == "mdfe30_PIX": + bank_data["PIX"]: self.mdfe30_PIX + + return bank_data + + +class MDFeModalRodoviarioPagamentoFrete(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.pagamento.frete" + _inherit = "mdfe.30.rodo_comp" + + mdfe30_tpComp = fields.Selection(required=True) + + mdfe30_vComp = fields.Monetary(required=True) + + @api.model + def export_fields(self): + return { + "tpComp": self.mdfe30_tpComp, + "vComp": self.mdfe30_vComp, + "xComp": self.mdfe30_xComp, + } + + +class MDFeModalRodoviarioPagamentoPrazo(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.pagamento.prazo" + _inherit = "mdfe.30.rodo_infprazo" + + mdfe30_nParcela = fields.Char(required=True) + + mdfe30_dVenc = fields.Date(required=True) + + mdfe30_vParcela = fields.Monetary(required=True) + + @api.model + def export_fields(self): + return { + "nParcela": self.mdfe30_nParcela, + "dVenc": self.mdfe30_dVenc, + "vParcela": self.mdfe30_vParcela, + } + + +class MDFeModalRodoviarioVeiculoTracao(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.veiculo.tracao" + _inherit = "mdfe.30.veictracao" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_prop = fields.Many2one(comodel_name="res.partner") + + mdfe30_condutor = fields.One2many( + comodel_name="l10n_br_mdfe.modal.rodoviario.veiculo.condutor" + ) + + mdfe30_placa = fields.Char(required=True) + + mdfe30_tara = fields.Char(required=True) + + mdfe30_capKG = fields.Char(required=True) + + mdfe30_tpRod = fields.Selection(required=True) + + mdfe30_tpCar = fields.Selection(required=True) + + +class MDFeModalRodoviarioVeiculoCondutor(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.veiculo.condutor" + _inherit = "mdfe.30.rodo_condutor" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_xNome = fields.Char(required=True) + + mdfe30_CPF = fields.Char(required=True) + + @api.model + def export_fields(self): + return { + "xNome": self.mdfe30_xNome, + "CPF": self.mdfe30_CPF, + } + + +class MDFeModalRodoviarioReboque(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.reboque" + _inherit = "mdfe.30.veicreboque" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_prop = fields.Many2one(comodel_name="res.partner") + + mdfe30_placa = fields.Char(required=True) + + mdfe30_tara = fields.Char(required=True) + + mdfe30_capKG = fields.Char(required=True) + + mdfe30_tpCar = fields.Selection(required=True) + + @api.model + def export_fields(self): + return { + "cInt": self.mdfe30_cInt, + "placa": self.mdfe30_placa, + "RENAVAM": self.mdfe30_RENAVAM, + "tara": self.mdfe30_tara, + "capKG": self.mdfe30_capKG, + "capM3": self.mdfe30_capM3, + "prop": self.mdfe30_prop, + "tpCar": self.mdfe30_tpCar, + "UF": self.mdfe30_UF, + } + + +class MDFeModalRodoviarioLacre(spec_models.SpecModel): + _name = "l10n_br_mdfe.modal.rodoviario.lacre" + _inherit = "mdfe.30.lacrodo" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_nLacre = fields.Char(required=True) + + @api.model + def export_fields(self): + return {"nLacre": self.mdfe30_nLacre} diff --git a/l10n_br_mdfe/models/res_partner.py b/l10n_br_mdfe/models/res_partner.py index 400a8c0656a0..ae5b2a3972b8 100644 --- a/l10n_br_mdfe/models/res_partner.py +++ b/l10n_br_mdfe/models/res_partner.py @@ -20,6 +20,9 @@ class ResPartner(spec_models.SpecModel): "mdfe.30.dest", "mdfe.30.tresptec", "mdfe.30.autxml", + "mdfe.30.veicreboque_prop", + "mdfe.30.veictracao_prop", + "mdfe.30.infcontratante", ] _mdfe_search_keys = ["mdfe30_CNPJ", "mdfe30_CPF", "mdfe_xNome"] @@ -97,7 +100,7 @@ def _prepare_import_dict( ) mdfe30_CEP = fields.Char( - compute="_compute_mdfe_data", inverse="_inverse_mdfe30_CEP", compute_sudo=True + compute="_compute_mdfe_data", inverse="_inverse_mdfe30_CEP" ) mdfe30_cPais = fields.Char( @@ -113,7 +116,7 @@ def _prepare_import_dict( ) mdfe30_fone = fields.Char( - compute="_compute_mdfe_data", inverse="_inverse_mdfe30_fone", compute_sudo=True + compute="_compute_mdfe_data", inverse="_inverse_mdfe30_fone" ) mdfe30_xNome = fields.Char(related="legal_name") @@ -123,7 +126,6 @@ def _prepare_import_dict( mdfe30_IE = fields.Char( compute="_compute_mdfe_data", inverse="_inverse_mdfe30_IE", - compute_sudo=True, ) mdfe30_email = fields.Char(related="email") @@ -136,25 +138,63 @@ def _prepare_import_dict( compute="_compute_mdfe_data", ) + mdfe30_choice9 = fields.Selection( + selection=[("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], + string="CNPJ/CPF do Proprietário do Reboque", + compute="_compute_mdfe_data", + ) + + mdfe30_choice10 = fields.Selection( + selection=[ + ("mdfe30_CNPJ", "CNPJ"), + ("mdfe30_CPF", "CPF"), + ("mdfe30_idEstrangeiro", "idEstrangeiro"), + ], + string="CNPJ/CPF/Id Estrangeiro do Contratante", + compute="_compute_mdfe_data", + ) + + mdfe30_choice11 = fields.Selection( + selection=[("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], + string="CNPJ/CPF/Id Estrangeiro do Contratante", + compute="_compute_mdfe_data", + ) + + mdfe30_tpProp = fields.Selection(required=True) + + mdfe30_RNTRC = fields.Char(required=True) + @api.depends("company_type", "inscr_est", "cnpj_cpf", "country_id") def _compute_mdfe_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.is_company: + if rec.country_id.code != "BR": + rec.mdfe30_choice10 = "mdfe30_idEstrangeiro" + rec.mdfe30_CNPJ = cnpj_cpf + elif rec.is_company: rec.mdfe30_choice6 = "mdfe30_CNPJ" rec.mdfe30_choice8 = "mdfe30_CNPJ" + rec.mdfe30_choice9 = "mdfe30_CNPJ" + rec.mdfe30_choice10 = "mdfe30_CNPJ" + rec.mdfe30_choice11 = "mdfe30_CNPJ" rec.mdfe30_CNPJ = cnpj_cpf rec.mdfe30_CPF = None else: rec.mdfe30_choice6 = "mdfe30_CPF" rec.mdfe30_choice8 = "mdfe30_CPF" + rec.mdfe30_choice9 = "mdfe30_CPF" + rec.mdfe30_choice10 = "mdfe30_CPF" + rec.mdfe30_choice11 = "mdfe30_CPF" rec.mdfe30_CPF = cnpj_cpf rec.mdfe30_CNPJ = None else: rec.mdfe30_choice6 = False rec.mdfe30_choice8 = False + rec.mdfe30_choice9 = False + rec.mdfe30_choice10 = False + rec.mdfe30_choice11 = False rec.mdfe30_CNPJ = "" rec.mdfe30_CPF = "" @@ -172,6 +212,9 @@ def _inverse_mdfe30_CNPJ(self): rec.is_company = True rec.mdfe30_choice6 = "mdfe30_CPF" rec.mdfe30_choice8 = "mdfe30_CPF" + rec.mdfe30_choice9 = "mdfe30_CPF" + rec.mdfe30_choice10 = "mdfe30_CPF" + rec.mdfe30_choice11 = "mdfe30_CPF" rec.cnpj_cpf = cnpj_cpf.formata(str(rec.mdfe30_CNPJ)) def _inverse_mdfe30_CPF(self): @@ -180,6 +223,9 @@ def _inverse_mdfe30_CPF(self): rec.is_company = False rec.mdfe30_choice6 = "mdfe30_CNPJ" rec.mdfe30_choice8 = "mdfe30_CNPJ" + rec.mdfe30_choice9 = "mdfe30_CNPJ" + rec.mdfe30_choice10 = "mdfe30_CNPJ" + rec.mdfe30_choice11 = "mdfe30_CNPJ" rec.cnpj_cpf = cnpj_cpf.formata(str(rec.mdfe30_CPF)) def _inverse_mdfe30_IE(self): @@ -239,3 +285,32 @@ def _inverse_mdfe30_ender(self): rec.city_id = city_id rec.country_id = country_id rec.state_id = state_id + + @api.model + def export_contractor_fields(self): + cnpj_cpf_data = {} + if self.choice10 == "mdfe30_idEstrangeiro": + cnpj_cpf_data["idEstrangeiro"] = self.mdfe30_idEstrangeiro + elif self.choice10 == "mdfe30_CNPJ": + cnpj_cpf_data["CNPJ"]: self.mdfe30_CNPJ + else: + cnpj_cpf_data["CPF"]: self.mdfe30_CPF + + return {"xNome": self.name, **cnpj_cpf_data} + + @api.model + def export_proprietary_fields(self): + cnpj_cpf_data = {} + if self.choice10 == "mdfe30_CNPJ": + cnpj_cpf_data["CNPJ"]: self.mdfe30_CNPJ + else: + cnpj_cpf_data["CPF"]: self.mdfe30_CPF + + return { + "xNome": self.mdfe30_xNome, + "tpProp": self.mdfe30_tpProp, + "RNTRC": self.mdfe30_RNTRC, + "IE": self.mdfe30_IE, + "UF": self.mdfe30_UF, + **cnpj_cpf_data, + } diff --git a/l10n_br_mdfe/security/ir.model.access.csv b/l10n_br_mdfe/security/ir.model.access.csv new file mode 100644 index 000000000000..54f5d5dd62eb --- /dev/null +++ b/l10n_br_mdfe/security/ir.model.access.csv @@ -0,0 +1,30 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +l10n_br_mdfe_modal_user,l10n_br_mdfe_modal_user,model_l10n_br_mdfe_modal,base.group_user,1,1,1,1 + +l10n_br_mdfe_modal_rodoviario_user,l10n_br_mdfe_modal_rodoviario_user,model_l10n_br_mdfe_modal_rodoviario,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_antt_user,l10n_br_mdfe_modal_rodoviario_antt_user,model_l10n_br_mdfe_modal_rodoviario_antt,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_ciot_user,l10n_br_mdfe_modal_rodoviario_ciot_user,model_l10n_br_mdfe_modal_rodoviario_ciot,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_vale_pedagio_user,l10n_br_mdfe_modal_rodoviario_vale_pedagio_user,model_l10n_br_mdfe_modal_rodoviario_vale_pedagio,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_vale_pedagio_dispositivo_user,l10n_br_mdfe_modal_rodoviario_vale_pedagio_dispositivo_user,model_l10n_br_mdfe_modal_rodoviario_vale_pedagio_dispositivo,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_pagamento_user,l10n_br_mdfe_modal_rodoviario_pagamento_user,model_l10n_br_mdfe_modal_rodoviario_pagamento,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_pagamento_frete_user,l10n_br_mdfe_modal_rodoviario_pagamento_frete_user,model_l10n_br_mdfe_modal_rodoviario_pagamento_frete,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_pagamento_prazo_user,l10n_br_mdfe_modal_rodoviario_pagamento_prazo_user,model_l10n_br_mdfe_modal_rodoviario_pagamento_prazo,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_pagamento_banco_user,l10n_br_mdfe_modal_rodoviario_pagamento_banco_user,model_l10n_br_mdfe_modal_rodoviario_pagamento_banco,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_veiculo_tracao_user,l10n_br_mdfe_modal_rodoviario_veiculo_tracao_user,model_l10n_br_mdfe_modal_rodoviario_veiculo_tracao,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_veiculo_condutor_user,l10n_br_mdfe_modal_rodoviario_veiculo_condutor_user,model_l10n_br_mdfe_modal_rodoviario_veiculo_condutor,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_reboque_user,l10n_br_mdfe_modal_rodoviario_reboque_user,model_l10n_br_mdfe_modal_rodoviario_reboque,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_rodoviario_lacre_user,l10n_br_mdfe_modal_rodoviario_lacre_user,model_l10n_br_mdfe_modal_rodoviario_lacre,base.group_user,1,1,1,1 + +l10n_br_mdfe_modal_ferroviario_user,l10n_br_mdfe_modal_ferroviario_user,model_l10n_br_mdfe_modal_ferroviario,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_ferroviario_user_trem,l10n_br_mdfe_modal_ferroviario_user_trem,model_l10n_br_mdfe_modal_ferroviario_trem,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_ferroviario_user_vagao,l10n_br_mdfe_modal_ferroviario_user_vagao,model_l10n_br_mdfe_modal_ferroviario_vagao,base.group_user,1,1,1,1 + +l10n_br_mdfe_modal_aquaviario_user,l10n_br_mdfe_modal_aquaviario_user,model_l10n_br_mdfe_modal_aquaviario,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_aquaviario_carregamento_user,l10n_br_mdfe_modal_aquaviario_carregamento_user,model_l10n_br_mdfe_modal_aquaviario_carregamento,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_aquaviario_descarregamento_user,l10n_br_mdfe_modal_aquaviario_descarregamento_user,model_l10n_br_mdfe_modal_aquaviario_descarregamento,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_aquaviario_comboio_user,l10n_br_mdfe_modal_aquaviario_comboio_user,model_l10n_br_mdfe_modal_aquaviario_comboio,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_aquaviario_carga_vazia_user,l10n_br_mdfe_modal_aquaviario_carga_vazia_user,model_l10n_br_mdfe_modal_aquaviario_carga_vazia,base.group_user,1,1,1,1 +l10n_br_mdfe_modal_aquaviario_transporte_vazio_user,l10n_br_mdfe_modal_aquaviario_transporte_vazio_user,model_l10n_br_mdfe_modal_aquaviario_transporte_vazio,base.group_user,1,1,1,1 + +l10n_br_mdfe_document_info_user,l10n_br_mdfe_document_info_user,model_l10n_br_mdfe_document_info,base.group_user,1,1,1,1 +l10n_br_mdfe_municipio_descarga_user,l10n_br_mdfe_municipio_descarga_user,model_l10n_br_mdfe_municipio_descarga,base.group_user,1,1,1,1 diff --git a/l10n_br_mdfe/views/document.xml b/l10n_br_mdfe/views/document.xml index 63fbe76943e9..ccb314675f77 100644 --- a/l10n_br_mdfe/views/document.xml +++ b/l10n_br_mdfe/views/document.xml @@ -9,6 +9,10 @@ primary + + 1 + + 1 @@ -36,7 +40,6 @@ - @@ -51,6 +54,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + +
+
+ +
diff --git a/l10n_br_mdfe/views/document_related.xml b/l10n_br_mdfe/views/document_related.xml deleted file mode 100644 index d5f060fd15cd..000000000000 --- a/l10n_br_mdfe/views/document_related.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - document.related.form (in l10n_br_mdfe) - document.related - - - - - - - - - diff --git a/l10n_br_mdfe/views/mdfe_action.xml b/l10n_br_mdfe/views/mdfe_action.xml index 184d9c6e50c9..c80449be2a35 100644 --- a/l10n_br_mdfe/views/mdfe_action.xml +++ b/l10n_br_mdfe/views/mdfe_action.xml @@ -11,6 +11,14 @@ name="view_ids" eval="[ (5,0,0), + ( + 0, + 0, + { + 'view_mode': 'tree', + 'view_id': ref('l10n_br_fiscal.document_tree') + } + ), ( 0, 0, @@ -18,7 +26,7 @@ 'view_mode': 'form', 'view_id': ref('l10n_br_mdfe.mdfe_document_form_view') } - ) + ), ]" /> + + + + modal.aquaviario.carregamento.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.aquaviario.carregamento + + + + + + + + + modal.aquaviario.descarregamento.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.aquaviario.descarregamento + + + + + + + + + modal.aquaviario.comboio.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.aquaviario.comboio + + + + + + + + + + modal.aquaviario.carga.vazia.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.aquaviario.carga.vazia + + + + + + + + + + modal.aquaviario.transporte.vazio.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.aquaviario.transporte.vazio + + + + + + + + diff --git a/l10n_br_mdfe/views/modal/modal_ferroviario.xml b/l10n_br_mdfe/views/modal/modal_ferroviario.xml new file mode 100644 index 000000000000..cf5a1c671009 --- /dev/null +++ b/l10n_br_mdfe/views/modal/modal_ferroviario.xml @@ -0,0 +1,40 @@ + + + + + modal.ferroviario.vagao.tree.view + l10n_br_mdfe.modal.ferroviario.vagao + + + + + + + + + + + modal.ferroviario.vagao.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.ferroviario.vagao + +
+ + + + + + + + + + + + + + + +
+
+
+
diff --git a/l10n_br_mdfe/views/modal/modal_rodoviario.xml b/l10n_br_mdfe/views/modal/modal_rodoviario.xml new file mode 100644 index 000000000000..5b3a2cfa47e5 --- /dev/null +++ b/l10n_br_mdfe/views/modal/modal_rodoviario.xml @@ -0,0 +1,527 @@ + + + + + modal.rodoviario.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario + +
+ + + + + + + + + + + + + + + + + +
+
+
+ + + modal.rodoviario.reboque.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.reboque + +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + modal.rodoviario.reboque.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.reboque + + + + + + + + + + + res.partner.reboque.prop.form.view + res.partner + +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + modal.rodoviario.lacre.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.lacre + + + + + + + + + modal.rodoviario.ANTT.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.antt + +
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ + + modal.rodoviario.ciot.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.ciot + +
+ + + + + + + + +
+
+
+ + + modal.rodoviario.ciot.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.ciot + + + + + + + + + modal.rodoviario.vale_pedagio.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.vale_pedagio + +
+ + + + + + + + + + + +
+
+
+ + + modal.rodoviario.vale_pedagio.dispositivo.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.vale_pedagio.dispositivo + +
+ + + + + + + + + + + + + + +
+
+
+ + + modal.rodoviario.vale_pedagio.dispositivo.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.vale_pedagio.dispositivo + + + + + + + + + + + res.partner.contratante.form.view (in l10n_br_mdfe) + res.partner + +
+ + + + + + + +
+
+
+ + + res.partner.contratante.tree.view (in l10n_br_mdfe) + res.partner + + + + + + + + + + modal.rodoviario.pagamento.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.pagamento + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + modal.rodoviario.pagamento.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.pagamento + + + + + + + + + + + modal.rodoviario.pagamento.banco.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.pagamento.banco + +
+ + + + + + + + +
+
+
+ + + modal.rodoviario.pagamento.banco.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.pagamento.banco + + + + + + + + + modal.rodoviario.pagamento.prazo.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.pagamento.prazo + +
+ + + + + + + +
+
+
+ + + modal.rodoviario.pagamento.prazo.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.pagamento.prazo + + + + + + + + + + + modal.rodoviario.pagamento.frete.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.pagamento.frete + +
+ + + + + + + +
+
+
+ + + modal.rodoviario.pagamento.frete.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.pagamento.frete + + + + + + + + + + + modal.rodoviario.veiculo.tracao.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.veiculo.tracao + +
+ + + + + + + + + + + + + + + + + + + +
+
+
+ + + modal.rodoviario.veiculo.condutor.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.veiculo.condutor + + + + + + + + + + modal.rodoviario.veiculo.condutor.form.view (in l10n_br_mdfe) + l10n_br_mdfe.modal.rodoviario.veiculo.condutor + +
+ + + + + + +
+
+
+
diff --git a/l10n_br_mdfe_spec/__manifest__.py b/l10n_br_mdfe_spec/__manifest__.py index f5b1b34abe89..0de1a01feb21 100644 --- a/l10n_br_mdfe_spec/__manifest__.py +++ b/l10n_br_mdfe_spec/__manifest__.py @@ -4,7 +4,7 @@ "author": "Akretion, Odoo Community Association (OCA)", "license": "LGPL-3", "category": "Accounting", - "summary": "CT-e spec", + "summary": "MDF-e spec", "depends": ["base"], "installable": True, "application": False, diff --git a/l10n_br_nfe/models/document.py b/l10n_br_nfe/models/document.py index 3ccfdb4ac195..e6a6df67528e 100644 --- a/l10n_br_nfe/models/document.py +++ b/l10n_br_nfe/models/document.py @@ -14,7 +14,6 @@ from lxml import etree from nfelib.nfe.bindings.v4_0.nfe_v4_00 import Nfe from nfelib.nfe.ws.edoc_legacy import NFCeAdapter as edoc_nfce, NFeAdapter as edoc_nfe -from requests import Session from odoo import _, api, fields from odoo.exceptions import UserError, ValidationError From 92ce6565384b5a75497ca86ee44bb0e5cce2e075 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Fri, 15 Sep 2023 19:40:41 -0300 Subject: [PATCH 11/72] [FIX] l10n_br_fiscal: reset changes on processor --- .../data/l10n_br_fiscal.document.type.csv | 2 +- l10n_br_fiscal/models/document.py | 29 +------------------ l10n_br_fiscal/models/document_workflow.py | 2 ++ 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv b/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv index 13f7d5252256..8a49f10d0f29 100644 --- a/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv +++ b/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv @@ -30,7 +30,7 @@ "document_30","30","Bilhete/Recibo do Passageiro","False",,,"icms" "document_55","55","Nota Fiscal Eletrônica","True","NFe","nf-e","icms" "document_57","57","Conhecimento de Transporte Eletrônico – CT-e","True","CTe","ct-e","icms" -"document_58","58","Manifesto de Carga","False","MDFe","mdf-e","icms" +"document_58","58","Manifesto Eletrônico de Documentos Fiscais","True","MDFe","mdf-e","icms" "document_59","59","Cupom Fiscal Eletrônico - CF-e","True","CFe","cf-e","icms" "document_60","60","Cupom Fiscal Eletrônico CF-e-ECF","True",,,"icms" "document_65","65","Nota Fiscal Eletrônica ao Consumidor Final – NFC-e","True","NFe","nfc-e","icms" diff --git a/l10n_br_fiscal/models/document.py b/l10n_br_fiscal/models/document.py index e5e4015c9444..d656680389ec 100644 --- a/l10n_br_fiscal/models/document.py +++ b/l10n_br_fiscal/models/document.py @@ -4,13 +4,10 @@ from ast import literal_eval -from erpbrasil.assinatura import certificado as cert from erpbrasil.base.fiscal.edoc import ChaveEdoc -from erpbrasil.transmissao import TransmissaoSOAP -from requests import Session from odoo import _, api, fields, models -from odoo.exceptions import UserError, ValidationError +from odoo.exceptions import ValidationError from ..constants.fiscal import ( DOCUMENT_ISSUER_COMPANY, @@ -573,27 +570,3 @@ def action_send_email(self): "target": "new", "context": ctx, } - - def _prepare_processor_params(self): - certificate = False - if self.company_id.sudo().certificate_nfe_id: - certificate = self.company_id.sudo().certificate_nfe_id - elif self.company_id.sudo().certificate_ecnpj_id: - certificate = self.company_id.sudo().certificate_ecnpj_id - - if not certificate: - raise UserError(_("Certificado não encontrado")) - - certificado = cert.Certificado( - arquivo=certificate.file, - senha=certificate.password, - ) - session = Session() - session.verify = False - - return { - "transmissao": TransmissaoSOAP(certificado, session), - "uf": self.company_id.state_id.ibge_code, - "versao": self.nfe_version, - "ambiente": self.nfe_environment, - } diff --git a/l10n_br_fiscal/models/document_workflow.py b/l10n_br_fiscal/models/document_workflow.py index 18734f53e0fc..f3e93d94d7c9 100644 --- a/l10n_br_fiscal/models/document_workflow.py +++ b/l10n_br_fiscal/models/document_workflow.py @@ -9,6 +9,7 @@ from ..constants.fiscal import ( DOCUMENT_ISSUER_COMPANY, MODELO_FISCAL_CTE, + MODELO_FISCAL_MDFE, MODELO_FISCAL_NFCE, MODELO_FISCAL_NFE, MODELO_FISCAL_NFSE, @@ -251,6 +252,7 @@ def _generate_key(self): MODELO_FISCAL_NFE, MODELO_FISCAL_NFCE, MODELO_FISCAL_CTE, + MODELO_FISCAL_MDFE, ): date = fields.Datetime.context_timestamp(record, record.document_date) chave_edoc = ChaveEdoc( From 92e7a45f596ef34f455daee071cd23d5cbb9854e Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Fri, 15 Sep 2023 19:41:00 -0300 Subject: [PATCH 12/72] [FIX] l10n_br_nfe: reset changes on processor --- l10n_br_nfe/models/document.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/l10n_br_nfe/models/document.py b/l10n_br_nfe/models/document.py index e6a6df67528e..dd7f97148de3 100644 --- a/l10n_br_nfe/models/document.py +++ b/l10n_br_nfe/models/document.py @@ -9,11 +9,14 @@ from datetime import datetime from unicodedata import normalize +from erpbrasil.assinatura import certificado as cert from erpbrasil.base.fiscal.edoc import ChaveEdoc from erpbrasil.edoc.pdf import base +from erpbrasil.transmissao import TransmissaoSOAP from lxml import etree from nfelib.nfe.bindings.v4_0.nfe_v4_00 import Nfe from nfelib.nfe.ws.edoc_legacy import NFCeAdapter as edoc_nfce, NFeAdapter as edoc_nfe +from requests import Session from odoo import _, api, fields from odoo.exceptions import UserError, ValidationError @@ -802,7 +805,28 @@ def _serialize(self, edocs): return edocs def _processador(self): - params = self._prepare_processor_params() + certificate = False + if self.company_id.sudo().certificate_nfe_id: + certificate = self.company_id.sudo().certificate_nfe_id + elif self.company_id.sudo().certificate_ecnpj_id: + certificate = self.company_id.sudo().certificate_ecnpj_id + + if not certificate: + raise UserError(_("Certificado não encontrado")) + + certificado = cert.Certificado( + arquivo=certificate.file, + senha=certificate.password, + ) + session = Session() + session.verify = False + + params = { + "transmissao": TransmissaoSOAP(certificado, session), + "uf": self.company_id.state_id.ibge_code, + "versao": self.nfe_version, + "ambiente": self.nfe_environment, + } if self.document_type == MODELO_FISCAL_NFCE: params.update( From 5f6d7c58fac8815b4f0971d92f4a150c23c10ebe Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Fri, 15 Sep 2023 19:46:07 -0300 Subject: [PATCH 13/72] [ADD] MDFe modals mapping --- l10n_br_mdfe/__manifest__.py | 7 +- l10n_br_mdfe/models/__init__.py | 3 + l10n_br_mdfe/models/document.py | 319 +++++++++++++----- l10n_br_mdfe/models/document_info.py | 16 +- l10n_br_mdfe/models/document_related.py | 15 +- l10n_br_mdfe/models/modal_aquaviario.py | 77 +++-- l10n_br_mdfe/models/modal_ferroviario.py | 33 +- l10n_br_mdfe/models/modal_rodoviario.py | 205 +++++++---- l10n_br_mdfe/models/product_product.py | 22 ++ l10n_br_mdfe/models/res_partner.py | 76 +++-- l10n_br_mdfe/models/seguro_carga.py | 23 ++ l10n_br_mdfe/models/transporte.py | 76 +++++ l10n_br_mdfe/security/ir.model.access.csv | 9 +- l10n_br_mdfe/views/document.xml | 208 +++++++++--- l10n_br_mdfe/views/modal/modal_rodoviario.xml | 10 - l10n_br_mdfe/views/product_product.xml | 21 ++ l10n_br_mdfe/views/res_partner.xml | 21 +- l10n_br_mdfe/views/transporte.xml | 14 + 18 files changed, 893 insertions(+), 262 deletions(-) create mode 100644 l10n_br_mdfe/models/product_product.py create mode 100644 l10n_br_mdfe/models/seguro_carga.py create mode 100644 l10n_br_mdfe/models/transporte.py create mode 100644 l10n_br_mdfe/views/product_product.xml create mode 100644 l10n_br_mdfe/views/transporte.xml diff --git a/l10n_br_mdfe/__manifest__.py b/l10n_br_mdfe/__manifest__.py index f672451ed4be..c8e775df1809 100644 --- a/l10n_br_mdfe/__manifest__.py +++ b/l10n_br_mdfe/__manifest__.py @@ -24,6 +24,9 @@ "views/mdfe_action.xml", "views/mdfe_menu.xml", "views/res_company.xml", + "views/transporte.xml", + "views/res_partner.xml", + "views/product_product.xml", "views/modal/modal_aquaviario.xml", "views/modal/modal_rodoviario.xml", "views/modal/modal_ferroviario.xml", @@ -34,8 +37,8 @@ "external_dependencies": { "python": [ "nfelib>=2.0.0", - "erpbrasil.transmissao", - "erpbrasil.edoc", + "erpbrasil.transmissao>=1.1.0", + "erpbrasil.edoc>=2.5.2", ] }, } diff --git a/l10n_br_mdfe/models/__init__.py b/l10n_br_mdfe/models/__init__.py index 0867bb980fdb..31cdfecdafcc 100644 --- a/l10n_br_mdfe/models/__init__.py +++ b/l10n_br_mdfe/models/__init__.py @@ -1,3 +1,4 @@ +from . import transporte from . import res_company from . import document from . import document_related @@ -8,3 +9,5 @@ from . import modal_ferroviario from . import modal_rodoviario from . import document_info +from . import seguro_carga +from . import product_product diff --git a/l10n_br_mdfe/models/document.py b/l10n_br_mdfe/models/document.py index b70abef368ab..db44aabb61e0 100644 --- a/l10n_br_mdfe/models/document.py +++ b/l10n_br_mdfe/models/document.py @@ -4,14 +4,18 @@ import re from unicodedata import normalize +from erpbrasil.assinatura import certificado as cert +from erpbrasil.transmissao import TransmissaoSOAP from nfelib.mdfe.bindings.v3_0.mdfe_modal_aereo_v3_00 import Aereo from nfelib.mdfe.bindings.v3_0.mdfe_modal_aquaviario_v3_00 import Aquav from nfelib.mdfe.bindings.v3_0.mdfe_modal_ferroviario_v3_00 import Ferrov from nfelib.mdfe.bindings.v3_0.mdfe_modal_rodoviario_v3_00 import Rodo from nfelib.mdfe.bindings.v3_0.mdfe_v3_00 import Mdfe from nfelib.nfe.ws.edoc_legacy import MDFeAdapter as edoc_mdfe +from requests import Session -from odoo import api, fields +from odoo import _, api, fields +from odoo.exceptions import UserError from odoo.addons.l10n_br_fiscal.constants.fiscal import ( EVENT_ENV_HML, @@ -67,7 +71,7 @@ class MDFe(spec_models.StackedModel): _mdfe_search_keys = ["mdfe30_Id"] # all m2o at this level will be stacked even if not required: - _force_stack_paths = "infmdfe.infAdic" + _force_stack_paths = ["infmdfe.infAdic", "infmdfe.tot", "infmdfe.infsolicnff"] INFMDFE_TREE = """ > @@ -78,6 +82,18 @@ class MDFe(spec_models.StackedModel): > """ + mdfe_version = fields.Selection( + string="MDF-e Version", + related="company_id.mdfe_version", + readonly=False, + ) + + mdfe_environment = fields.Selection( + string="MDF-e Environment", + related="company_id.mdfe_environment", + readonly=False, + ) + ########################## # MDF-e spec related fields ########################## @@ -86,7 +102,7 @@ class MDFe(spec_models.StackedModel): # MDF-e tag: infMDFe ########################## - mdfe30_versao = fields.Char(related="document_version") + mdfe30_versao = fields.Char(compute="_compute_mdfe_version") mdfe30_Id = fields.Char( compute="_compute_mdfe30_id_tag", @@ -98,6 +114,11 @@ class MDFe(spec_models.StackedModel): # Methods ########################## + @api.depends("mdfe_version") + def _compute_mdfe_version(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_versao = record.mdfe_version + @api.depends("document_type_id", "document_key") def _compute_mdfe30_id_tag(self): """Set schema data which are not just related fields""" @@ -105,11 +126,11 @@ def _compute_mdfe30_id_tag(self): for record in self.filtered(filtered_processador_edoc_mdfe): if ( record.document_type_id - and record.document_type.prefix + and record.document_type_id.prefix and record.document_key ): record.mdfe30_Id = "{}{}".format( - record.document_type.prefix, record.document_key + record.document_type_id.prefix, record.document_key ) else: record.mdfe30_Id = False @@ -413,7 +434,7 @@ def _compute_inf_percurso(self): ) rodo_seal_ids = fields.One2many( - comodel_name="l10n_br_mdfe.modal.rodoviario.lacre", inverse_name="document_id" + comodel_name="l10n_br_mdfe.transporte.lacre", inverse_name="document_id" ) ########################## @@ -439,16 +460,48 @@ def _export_fields_mdfe_30_infmodal(self, xsd_fields, class_obj, export_dict): export_dict["any_element"] = self._export_modal_ferroviario_fields() def _export_modal_aereo_fields(self): + optional_data = {} + if self.flight_date: + optional_data["dVoo"] = fields.Date.to_string(self.flight_date) + return Aereo( nac=self.airplane_nationality, matr=self.airplane_registration, nVoo=self.flight_number, - dVoo=fields.Date.to_string(self.flight_date), cAerEmb=self.boarding_airfield, cAerDes=self.landing_airfield, + **optional_data, ) def _export_modal_aquaviario_fields(self): + optional_data = {} + if self.ship_loading_ids: + optional_data["infTermCarreg"] = self.ship_loading_ids.export_fields_multi() + + if self.ship_unloading_ids: + optional_data[ + "infTermDescarreg" + ] = self.ship_unloading_ids.export_fields_multi() + + if self.ship_convoy_ids: + optional_data["infEmbComb"] = self.ship_convoy_ids.export_fields_multi() + + if self.ship_empty_load_ids: + optional_data[ + "infUnidCargaVazia" + ] = self.ship_empty_load_ids.export_fields_multi() + + if self.ship_empty_transport_ids: + optional_data[ + "infUnidTranspVazia" + ] = self.ship_empty_transport_ids.export_fields_multi() + + if self.transshipment_port: + optional_data["prtTrans"] = self.transshipment_port + + if self.ship_navigation_type: + optional_data["tpNav"] = self.ship_navigation_type + return Aquav( irin=self.ship_irin, tpEmb=self.ship_type, @@ -456,92 +509,153 @@ def _export_modal_aquaviario_fields(self): xEmbar=self.ship_name, cPrtEmb=self.ship_boarding_point, cPrtDest=self.ship_landing_point, - prtTrans=self.transshipment_port, - tpNav=self.ship_navigation_type, - infTermCarreg=[ - Aquav.InfTermCarreg(**loading.export_fields()) - for loading in self.ship_loading_ids - ], - infTermDescarreg=[ - Aquav.InfTermDescarreg(**unloading.export_fields()) - for unloading in self.ship_unloading_ids - ], - infEmbComb=[ - Aquav.InfEmbComb(**convoy.export_fields()) - for convoy in self.ship_convoy_ids - ], - infUnidCargaVazia=[ - Aquav.infUnidCargaVazia(**load.export_fields()) - for load in self.ship_empty_load_ids - ], - infUnidTranspVazia=[ - Aquav.infUnidTranspVazia(**transp.export_fields()) - for transp in self.ship_empty_transport_ids - ], + **optional_data, ) def _export_modal_ferroviario_fields(self): + train_optional_data = {} + if self.train_release_time: + train_optional_data["dhTrem"] = fields.Datetime.to_string( + self.train_release_time + ) + return Ferrov( trem=Ferrov.Trem( xPref=self.train_prefix, - dhTrem=fields.Datetime.to_string(self.train_release_time), xOri=self.train_origin, xDest=self.train_destiny, qVag=self.train_wagon_quantity, + **train_optional_data, ), - vag=[Ferrov.Vag(**vag.export_fields()) for vag in self.train_wagon_ids], + vag=self.train_wagon_ids.export_fields_multi(), ) def _export_modal_rodoviario_fields(self): + optional_data = {} + if self.rodo_scheduling_code: + optional_data["codAgPorto"] = self.rodo_scheduling_code + + if self.rodo_seal_ids: + optional_data["lacRodo"] = self.rodo_seal_ids.export_fields_multi( + Rodo.LacRodo + ) + + if self.rodo_tow_ids: + optional_data["veicReboque"] = self.rodo_tow_ids.export_fields_multi() + + has_antt_data = any( + [ + self.rodo_RNTRC, + self.rodo_ciot_ids, + self.rodo_toll_device_ids, + self.rod_toll_vehicle_categ, + self.rodo_contractor_ids, + self.rodo_payment_ids, + ] + ) + if has_antt_data: + optional_data["infANTT"] = self._export_modal_rodoviario_antt_fields() + + veic_optional_data = {} + if self.rodo_vehicle_code: + veic_optional_data["cInt"] = self.rodo_vehicle_code + + if self.rodo_vehicle_RENAVAM: + veic_optional_data["RENAVAM"] = self.rodo_vehicle_RENAVAM + + if self.rodo_vehicle_kg_capacity: + veic_optional_data["capKG"] = self.rodo_vehicle_kg_capacity + + if self.rodo_vehicle_m3_capacity: + veic_optional_data["capM3"] = self.rodo_vehicle_m3_capacity + + if self.rodo_vehicle_proprietary_id: + veic_optional_data[ + "prop" + ] = self.rodo_vehicle_proprietary_id.export_proprietary_fields( + Rodo.VeicTracao.Prop + ) + + if self.rodo_vehicle_m3_capacity: + veic_optional_data["capM3"] = self.rodo_vehicle_m3_capacity + + if self.rodo_vehicle_state_id: + veic_optional_data["UF"] = self.rodo_vehicle_state_id.code + + if self.rodo_vehicle_conductor_ids: + veic_optional_data[ + "condutor" + ] = self.rodo_vehicle_conductor_ids.export_fields_multi() + return Rodo( - codAgPorto=self.rodo_scheduling_code, - infANTT=Rodo.infANTT( - RNTRC=self.rodo_RNTRC, - infCIOT=self.rodo_ciot_ids.export_fields(), - valePed=Rodo.InfAntt.ValePed( - disp=[ - Rodo.InfAntt.ValePed.Disp(**dev.export_fields()) - for dev in self.rodo_toll_device_ids - ], - categCombVeic=self.rod_toll_vehicle_categ, - ), - infContratante=[ - Rodo.InfAntt.infContratante(**contr.export_contractor_fields()) - for contr in self.rodo_contractor_ids - ], - infPag=[ - Rodo.InfAntt.infPag(**pay.export_contractor_fields()) - for pay in self.rodo_payment_ids - ], - ), - lacRodo=[ - Rodo.infANTT.lacRodo(**seal.export_fields()) - for seal in self.rodo_seal_ids - ], - veicReboque=[ - Rodo.veicReboque(**tow.export_fields()) for tow in self.rodo_tow_ids - ], - veicTracao=Rodo.veicTracao( - cInt=self.rodo_vehicle_code, + veicTracao=Rodo.VeicTracao( placa=self.rodo_vehicle_plate, - RENAVAM=self.rodo_vehicle_RENAVAM, tara=self.rodo_vehicle_tare_weight, - capKG=self.rodo_vehicle_kg_capacity, - capM3=self.rodo_vehicle_m3_capacity, tpRod=self.rodo_vehicle_tire_type, tpCar=self.rodo_vehicle_type, - UF=self.rodo_vehicle_state_id.code, - condutor=[ - Rodo.veicTracao.condutor(**cond.export_fields()) - for cond in self.rodo_vehicle_conductor_ids - ], - prop=[ - Rodo.veicTracao.prop(**prop.export_proprietary_fields()) - for prop in self.rodo_vehicle_proprietary_id - ], + **veic_optional_data, ), + **optional_data, ) + def _export_modal_rodoviario_antt_fields(self): + antt_data = {} + if self.rodo_RNTRC: + antt_data["RNTRC"] = self.rodo_RNTRC + + if self.rodo_ciot_ids: + antt_data["infCIOT"] = self.rodo_ciot_ids.export_fields_multi() + + if self.rodo_contractor_ids: + antt_data[ + "infContratante" + ] = self.rodo_contractor_ids.export_contractor_fields() + + if self.rodo_payment_ids: + antt_data["infPag"] = self.rodo_payment_ids.export_fields_multi() + + has_vale_ped_data = self.rodo_toll_device_ids or self.rod_toll_vehicle_categ + if has_vale_ped_data: + antt_data["valePed"] = self._export_modal_rodoviario_vale_ped_fields() + + return Rodo.InfAntt(**antt_data) + + def _export_modal_rodoviario_vale_ped_fields(self): + vale_ped_data = {} + if self.rodo_toll_device_ids: + vale_ped_data["disp"] = self.rodo_toll_device_ids.export_fields_multi() + + if self.rod_toll_vehicle_categ: + vale_ped_data["categCombVeic"] = self.rod_toll_vehicle_categ + + return Rodo.InfAntt.ValePed(**vale_ped_data) + + ########################## + # MDF-e tag: seg + ########################## + + mdfe30_seg = fields.One2many( + comodel_name="l10n_br_mdfe.seguro.carga", + inverse_name="document_id", + string="Seguros da Carga", + ) + + ########################## + # MDF-e tag: prodPred + ########################## + + mdfe30_prodPred = fields.Many2one(comodel_name="product.product") + + ########################## + # MDF-e tag: lacres + ########################## + + mdfe30_lacres = fields.One2many( + comodel_name="l10n_br_mdfe.transporte.lacre", + inverse_name="document_id", + related="rodo_seal_ids", + ) + ########################## # MDF-e tag: infDoc ########################## @@ -595,7 +709,7 @@ def _compute_doc_inf(self): @api.depends("fiscal_additional_data") def _compute_mdfe30_additional_data(self): - for record in self: + for record in self.filtered(filtered_processador_edoc_mdfe): record.mdfe30_infCpl = False record.mdfe30_infAdFisco = False @@ -631,6 +745,41 @@ def _default_mdfe30_autxml(self): mdfe30_autXML = fields.One2many(default=_default_mdfe30_autxml) + ########################## + # NF-e tag: tot + ########################## + + mdfe30_qCTe = fields.Char(compute="_compute_tot") + + mdfe30_qNFe = fields.Char(compute="_compute_tot") + + mdfe30_qMDFe = fields.Char(compute="_compute_tot") + + mdfe30_cUnid = fields.Selection(default="01") + + mdfe30_vCarga = fields.Monetary(compute="_compute_tot_carga") + + mdfe30_qCarga = fields.Float(compute="_compute_tot_carga") + + ########################## + # MDF-e tag: tot + # Methods + ########################## + + @api.depends("unloading_city_ids") + def _compute_tot(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_qCTe = len(record.unloading_city_ids.mapped("cte_ids")) + record.mdfe30_qNFe = len(record.unloading_city_ids.mapped("nfe_ids")) + record.mdfe30_qMDFe = len(record.unloading_city_ids.mapped("mdfe_ids")) + + @api.depends("mdfe30_cUnid") + def _compute_tot_carga(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + # TODO: calcular valores + record.mdfe30_vCarga = 0 + record.mdfe30_qCarga = 0 + ################################ # Framework Spec model's methods ################################ @@ -675,7 +824,25 @@ def _serialize(self, edocs): return edocs def _processador(self): - params = self._prepare_processor_params() + certificate = False + if self.company_id.sudo().certificate_nfe_id: + certificate = self.company_id.sudo().certificate_nfe_id + elif self.company_id.sudo().certificate_ecnpj_id: + certificate = self.company_id.sudo().certificate_ecnpj_id + + if not certificate: + raise UserError(_("Certificado não encontrado")) + + certificado = cert.Certificado( + arquivo=certificate.file, + senha=certificate.password, + ) + session = Session() + session.verify = False + + params = { + "transmissao": TransmissaoSOAP(certificado, session), + } return edoc_mdfe(**params) def _document_export(self, pretty_print=True): @@ -689,14 +856,14 @@ def _document_export(self, pretty_print=True): event_id = self.event_ids.create_event_save_xml( company_id=self.company_id, environment=( - EVENT_ENV_PROD if self.nfe_environment == "1" else EVENT_ENV_HML + EVENT_ENV_PROD if self.mdfe_environment == "1" else EVENT_ENV_HML ), event_type="0", xml_file=xml_file, document_id=self, ) record.authorization_event_id = event_id - xml_assinado = processador.assina_raiz(edoc, edoc.infNFe.Id) + xml_assinado = processador.assina_raiz(edoc, edoc.infMDFe.Id) self._valida_xml(xml_assinado) return result diff --git a/l10n_br_mdfe/models/document_info.py b/l10n_br_mdfe/models/document_info.py index e31c411ba17a..4813a6c9688d 100644 --- a/l10n_br_mdfe/models/document_info.py +++ b/l10n_br_mdfe/models/document_info.py @@ -31,6 +31,8 @@ class MDFeMunicipioDescarga(spec_models.SpecModel): mdfe30_infNFe = fields.One2many(compute="_compute_document_data") + mdfe30_infMDFeTransp = fields.One2many(compute="_compute_document_data") + country_id = fields.Many2one( comodel_name="res.country.state", default=lambda self: self.env.ref("base.br"), @@ -54,6 +56,7 @@ class MDFeMunicipioDescarga(spec_models.SpecModel): selection=[ ("nfe", "NF-e"), ("cte", "CT-e"), + ("mdfe", "MDF-e"), ], string="Document Type", default="nfe", @@ -69,6 +72,11 @@ class MDFeMunicipioDescarga(spec_models.SpecModel): relation="mdfe_related_cte_carregamento_rel", ) + mdfe_ids = fields.Many2many( + comodel_name="l10n_br_fiscal.document.related", + relation="mdfe_related_mdfe_carregamento_rel", + ) + @api.depends("city_id") def _compute_city_data(self): for record in self: @@ -80,6 +88,7 @@ def _compute_document_data(self): for record in self: record.mdfe30_infCTe = [(5, 0, 0)] record.mdfe30_infNFe = [(5, 0, 0)] + record.mdfe30_infMDFeTransp = [(5, 0, 0)] if not record.document_type: continue @@ -88,7 +97,12 @@ def _compute_document_data(self): record.mdfe30_infNFe = [ (0, 0, {"mdfe30_chNFe": nfe.mdfe30_chNFe}) for nfe in record.nfe_ids ] - else: + elif record.document_type == "cte": record.mdfe30_infCTe = [ (0, 0, {"mdfe30_chCTe": cte.mdfe30_chCTe}) for cte in record.cte_ids ] + else: + record.mdfe30_infMDFeTransp = [ + (0, 0, {"mdfe30_chMDFe": mdfe.mdfe30_chMDFe}) + for mdfe in record.mdfe_ids + ] diff --git a/l10n_br_mdfe/models/document_related.py b/l10n_br_mdfe/models/document_related.py index 97dd8c0939fc..634179e452f9 100644 --- a/l10n_br_mdfe/models/document_related.py +++ b/l10n_br_mdfe/models/document_related.py @@ -11,6 +11,7 @@ class MDFeRelated(spec_models.StackedModel): _name = "l10n_br_fiscal.document.related" _inherit = [ "l10n_br_fiscal.document.related", + "mdfe.30.infmdfetransp", "mdfe.30.tmdfe_infnfe", "mdfe.30.infcte", ] @@ -21,8 +22,16 @@ class MDFeRelated(spec_models.StackedModel): _odoo_module = "l10n_br_mdfe" _spec_module = "odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_tipos_basico_v3_00" _spec_tab_name = "MDFe" - _stack_skip = ("mdfe30_infNFe_infMunDescarga_id", "mdfe30_infCTe_infMunDescarga_id") + _stack_skip = ( + "mdfe30_infNFe_infMunDescarga_id", + "mdfe30_infCTe_infMunDescarga_id", + "mdfe30_infMDFeTransp_infMunDescarga_id", + ) - mdfe30_chNFe = fields.Char(related="document_id.document_key") + mdfe30_chNFe = fields.Char(related="document_key") - mdfe30_chCTe = fields.Char(related="document_id.document_key") + mdfe30_chCTe = fields.Char(related="document_key") + + mdfe30_chMDFe = fields.Char(related="document_key") + + mdfe30_peri = fields.One2many(comodel_name="l10n_br_mdfe.transporte.perigoso") diff --git a/l10n_br_mdfe/models/modal_aquaviario.py b/l10n_br_mdfe/models/modal_aquaviario.py index 9dee796ccc6b..31490297e902 100644 --- a/l10n_br_mdfe/models/modal_aquaviario.py +++ b/l10n_br_mdfe/models/modal_aquaviario.py @@ -1,6 +1,8 @@ # Copyright 2023 KMEE # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from nfelib.mdfe.bindings.v3_0.mdfe_modal_aquaviario_v3_00 import Aquav + from odoo import api, fields from odoo.addons.spec_driven_model.models import spec_models @@ -58,10 +60,17 @@ def _compute_loading_harbor(self): @api.model def export_fields(self): - return { - "cTermCarreg": self.mdfe30_cTermCarreg, - "xTermCarreg": self.mdfe30_xTermCarreg, - } + if len(self) > 1: + return self.export_fields_multi() + + return Aquav.InfTermCarreg( + cTermCarreg=self.mdfe30_cTermCarreg, + xTermCarreg=self.mdfe30_xTermCarreg, + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalAquaviarioDescarregamento(spec_models.SpecModel): @@ -88,10 +97,17 @@ def _compute_unloading_harbor(self): @api.model def export_fields(self): - return { - "cTermDescarreg": self.mdfe30_cTermDescarreg, - "xTermDescarreg": self.mdfe30_xTermDescarreg, - } + if len(self) > 1: + return self.export_fields_multi() + + return Aquav.InfTermDescarreg( + cTermDescarreg=self.mdfe30_cTermDescarreg, + xTermDescarreg=self.mdfe30_xTermDescarreg, + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalAquaviarioComboio(spec_models.SpecModel): @@ -106,10 +122,17 @@ class MDFeModalAquaviarioComboio(spec_models.SpecModel): @api.model def export_fields(self): - return { - "cEmbComb": self.mdfe30_cEmbComb, - "xBalsa": self.mdfe30_xBalsa, - } + if len(self) > 1: + return self.export_fields_multi() + + return Aquav.InfEmbComb( + cEmbComb=self.mdfe30_cEmbComb, + xBalsa=self.mdfe30_xBalsa, + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalAquaviarioCargaVazia(spec_models.SpecModel): @@ -124,10 +147,17 @@ class MDFeModalAquaviarioCargaVazia(spec_models.SpecModel): @api.model def export_fields(self): - return { - "idUnidCargaVazia": self.mdfe30_idUnidCargaVazia, - "tpUnidCargaVazia": self.mdfe30_tpUnidCargaVazia, - } + if len(self) > 1: + return self.export_fields_multi() + + return Aquav.InfUnidCargaVazia( + idUnidCargaVazia=self.mdfe30_idUnidCargaVazia, + tpUnidCargaVazia=self.mdfe30_tpUnidCargaVazia, + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalAquaviarioTranporteVazio(spec_models.SpecModel): @@ -142,7 +172,14 @@ class MDFeModalAquaviarioTranporteVazio(spec_models.SpecModel): @api.model def export_fields(self): - return { - "idUnidTranspVazia": self.mdfe30_idUnidTranspVazia, - "tpUnidTranspVazia": self.mdfe30_tpUnidTranspVazia, - } + if len(self) > 1: + return self.export_fields_multi() + + return Aquav.InfUnidTranspVazia( + idUnidTranspVazia=self.mdfe30_idUnidTranspVazia, + tpUnidTranspVazia=self.mdfe30_tpUnidTranspVazia, + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] diff --git a/l10n_br_mdfe/models/modal_ferroviario.py b/l10n_br_mdfe/models/modal_ferroviario.py index d2a5c3a9beb9..0e95412000b9 100644 --- a/l10n_br_mdfe/models/modal_ferroviario.py +++ b/l10n_br_mdfe/models/modal_ferroviario.py @@ -1,6 +1,8 @@ # Copyright 2023 KMEE # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from nfelib.mdfe.bindings.v3_0.mdfe_modal_ferroviario_v3_00 import Ferrov + from odoo import api, fields from odoo.addons.spec_driven_model.models import spec_models @@ -48,12 +50,25 @@ class MDFeModalFerroviarioVagao(spec_models.SpecModel): @api.model def export_fields(self): - return { - "pesoBC": self.mdfe30_pesoBC, - "pesoR": self.mdfe30_pesoR, - "tpVag": self.mdfe30_tpVag, - "serie": self.mdfe30_serie, - "nVag": self.mdfe30_nVag, - "nSeq": self.mdfe30_nSeq, - "TU": self.mdfe30_TU, - } + if len(self) > 1: + return self.export_fields_multi() + + optional_data = {} + if self.mdfe30_tpVag: + optional_data["tpVag"] = self.mdfe30_tpVag + + if self.mdfe30_tpVag: + optional_data["nSeq"] = self.mdfe30_nSeq + + return Ferrov.Vag( + pesoBC=self.mdfe30_pesoBC, + pesoR=self.mdfe30_pesoR, + serie=self.mdfe30_serie, + nVag=self.mdfe30_nVag, + TU=self.mdfe30_TU, + **optional_data + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] diff --git a/l10n_br_mdfe/models/modal_rodoviario.py b/l10n_br_mdfe/models/modal_rodoviario.py index 510687a2963e..2e85d8f6be96 100644 --- a/l10n_br_mdfe/models/modal_rodoviario.py +++ b/l10n_br_mdfe/models/modal_rodoviario.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from erpbrasil.base.misc import punctuation_rm +from nfelib.mdfe.bindings.v3_0.mdfe_modal_rodoviario_v3_00 import Rodo from odoo import api, fields @@ -23,7 +24,7 @@ class MDFeModalRodoviario(spec_models.SpecModel): comodel_name="l10n_br_mdfe.modal.rodoviario.reboque" ) - mdfe30_lacRodo = fields.One2many(comodel_name="l10n_br_mdfe.modal.rodoviario.lacre") + mdfe30_lacRodo = fields.One2many(comodel_name="l10n_br_mdfe.transporte.lacre") class MDFeModalRodoviarioANTT(spec_models.SpecModel): @@ -72,13 +73,20 @@ def _compute_ciot_choice(self): @api.model def export_fields(self): + if len(self) > 1: + return self.export_fields_multi() + cnpj_cpf_data = {} if self.is_company: - cnpj_cpf_data["CNPJ"]: self.mdfe30_CNPJ + cnpj_cpf_data["CNPJ"] = self.mdfe30_CNPJ else: - cnpj_cpf_data["CPF"]: self.mdfe30_CPF + cnpj_cpf_data["CPF"] = self.mdfe30_CPF + + return Rodo.InfAntt.InfCiot(CIOT=self.mdfe30_CIOT, **cnpj_cpf_data) - return {"CIOT": self.mdfe30_CIOT, **cnpj_cpf_data} + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalRodoviarioValePedagio(spec_models.SpecModel): @@ -102,14 +110,31 @@ class MDFeModalRodoviarioValePedagioDispositivo(spec_models.SpecModel): @api.model def export_fields(self): - return { - "CNPJForn": self.mdfe30_CNPJForn, - "CNPJPg": self.mdfe30_CNPJPg, - "CPFPg": self.mdfe30_CPFPg, - "nCompra": self.mdfe30_nCompra, - "vValePed": self.mdfe30_vValePed, - "tpValePed": self.mdfe30_tpValePed, - } + if len(self) > 1: + return self.export_fields_multi() + + optional_data = {} + if self.mdfe30_CNPJPg: + optional_data["CNPJPg"] = self.mdfe30_CNPJPg + + if self.mdfe30_CPFPg: + optional_data["CPFPg"] = self.mdfe30_CPFPg + + if self.mdfe30_nCompra: + optional_data["nCompra"] = self.mdfe30_nCompra + + if self.mdfe30_tpValePed: + optional_data["tpValePed"] = self.mdfe30_tpValePed + + return Rodo.InfAntt.ValePed.Disp( + CNPJForn=self.mdfe30_CNPJForn, + vValePed=self.mdfe30_vValePed, + **optional_data, + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalRodoviarioPagamento(spec_models.SpecModel): @@ -170,25 +195,42 @@ def _compute_mdfe_data(self): @api.model def export_fields(self): - cnpj_cpf_data = {} + if len(self) > 1: + return self.export_fields_multi() + + additional_data = {} if self.mdfe30_choice1 == "mdfe30_idEstrangeiro": - cnpj_cpf_data["idEstrangeiro"] = self.mdfe30_idEstrangeiro + additional_data["idEstrangeiro"] = self.mdfe30_idEstrangeiro elif self.mdfe30_choice1 == "mdfe30_CNPJ": - cnpj_cpf_data["CNPJ"]: self.mdfe30_CNPJ + additional_data["CNPJ"] = self.mdfe30_CNPJ else: - cnpj_cpf_data["CPF"]: self.mdfe30_CPF + additional_data["CPF"] = self.mdfe30_CPF + + if self.mdfe30_infPrazo: + additional_data["infPrazo"] = self.mdfe30_infPrazo.export_fields() + + if self.mdfe30_comp: + additional_data["comp"] = self.mdfe30_comp.export_fields() + + if self.mdfe30_indAltoDesemp: + additional_data["indAltoDesemp"] = self.mdfe30_indAltoDesemp - return { - "xNome": self.mdfe30_xNome, - "comp": self.mdfe30_comp.export_fields(), - "vContrato": self.mdfe30_vContrato, - "indAltoDesemp": self.mdfe30_indAltoDesemp, - "indPag": self.mdfe30_indPag, - "vAdiant": self.mdfe30_vAdiant, - "infPrazo": self.mdfe30_infPrazo.export_fields(), - "infBanc": self.mdfe30_infBanc.export_fields(), - **cnpj_cpf_data, - } + if self.mdfe30_vAdiant: + additional_data["vAdiant"] = self.mdfe30_vAdiant + + if self.mdfe30_xNome: + additional_data["xNome"] = self.mdfe30_xNome + + return Rodo.InfAntt.InfPag( + vContrato=self.mdfe30_vContrato, + indPag=self.mdfe30_indPag, + infBanc=self.mdfe30_infBanc.export_fields(), + **additional_data, + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalRodoviarioPagamentoBanco(spec_models.SpecModel): @@ -227,14 +269,21 @@ def _compute_payment_type(self): @api.model def export_fields(self): + if len(self) > 1: + return self.export_fields_multi() + bank_data = {} if self.mdfe30_choice1 == "mdfe30_codBanco": bank_data["codBanco"] = self.mdfe30_codBanco bank_data["codAgencia"] = self.mdfe30_codAgencia elif self.mdfe30_choice1 == "mdfe30_PIX": - bank_data["PIX"]: self.mdfe30_PIX + bank_data["PIX"] = self.mdfe30_PIX - return bank_data + return Rodo.InfAntt.InfPag.InfBanc(**bank_data) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalRodoviarioPagamentoFrete(spec_models.SpecModel): @@ -247,11 +296,20 @@ class MDFeModalRodoviarioPagamentoFrete(spec_models.SpecModel): @api.model def export_fields(self): - return { - "tpComp": self.mdfe30_tpComp, - "vComp": self.mdfe30_vComp, - "xComp": self.mdfe30_xComp, - } + if len(self) > 1: + return self.export_fields_multi() + + optional_data = {} + if self.mdfe30_vComp: + optional_data["xComp"] = self.mdfe30_vComp + + return Rodo.InfAntt.InfPag.Comp( + tpComp=self.mdfe30_tpComp, vComp=self.mdfe30_vComp, **optional_data + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalRodoviarioPagamentoPrazo(spec_models.SpecModel): @@ -266,11 +324,18 @@ class MDFeModalRodoviarioPagamentoPrazo(spec_models.SpecModel): @api.model def export_fields(self): - return { - "nParcela": self.mdfe30_nParcela, - "dVenc": self.mdfe30_dVenc, - "vParcela": self.mdfe30_vParcela, - } + if len(self) > 1: + return self.export_fields_multi() + + return Rodo.InfAntt.InfPag.InfPrazo( + nParcela=self.mdfe30_nParcela, + dVenc=self.mdfe30_dVenc, + vParcela=self.mdfe30_vParcela, + ) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalRodoviarioVeiculoTracao(spec_models.SpecModel): @@ -308,10 +373,14 @@ class MDFeModalRodoviarioVeiculoCondutor(spec_models.SpecModel): @api.model def export_fields(self): - return { - "xNome": self.mdfe30_xNome, - "CPF": self.mdfe30_CPF, - } + if len(self) > 1: + return self.export_fields_multi() + + return Rodo.VeicTracao.Condutor(xNome=self.mdfe30_xNome, CPF=self.mdfe30_CPF) + + @api.model + def export_fields_multi(self): + return [record.export_fields() for record in self] class MDFeModalRodoviarioReboque(spec_models.SpecModel): @@ -332,27 +401,37 @@ class MDFeModalRodoviarioReboque(spec_models.SpecModel): @api.model def export_fields(self): - return { - "cInt": self.mdfe30_cInt, - "placa": self.mdfe30_placa, - "RENAVAM": self.mdfe30_RENAVAM, - "tara": self.mdfe30_tara, - "capKG": self.mdfe30_capKG, - "capM3": self.mdfe30_capM3, - "prop": self.mdfe30_prop, - "tpCar": self.mdfe30_tpCar, - "UF": self.mdfe30_UF, - } - - -class MDFeModalRodoviarioLacre(spec_models.SpecModel): - _name = "l10n_br_mdfe.modal.rodoviario.lacre" - _inherit = "mdfe.30.lacrodo" + if len(self) > 1: + return self.export_fields_multi() - document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + optional_data = {} + if self.mdfe30_cInt: + optional_data["cInt"] = self.mdfe30_cInt + + if self.mdfe30_RENAVAM: + optional_data["RENAVAM"] = self.mdfe30_RENAVAM + + if self.mdfe30_capKG: + optional_data["capKG"] = self.mdfe30_capKG - mdfe30_nLacre = fields.Char(required=True) + if self.mdfe30_capM3: + optional_data["capM3"] = self.mdfe30_capM3 + + if self.mdfe30_prop: + optional_data["prop"] = self.mdfe30_prop.export_proprietary_fields( + Rodo.VeicReboque.Prop + ) + + if self.mdfe30_UF: + optional_data["UF"] = self.mdfe30_UF + + return Rodo.VeicReboque( + placa=self.mdfe30_placa, + tara=self.mdfe30_tara, + tpCar=self.mdfe30_tpCar, + **optional_data, + ) @api.model - def export_fields(self): - return {"nLacre": self.mdfe30_nLacre} + def export_fields_multi(self): + return [record.export_fields() for record in self] diff --git a/l10n_br_mdfe/models/product_product.py b/l10n_br_mdfe/models/product_product.py new file mode 100644 index 000000000000..6f515a8271d8 --- /dev/null +++ b/l10n_br_mdfe/models/product_product.py @@ -0,0 +1,22 @@ +# 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 ProductProduct(spec_models.SpecModel): + _name = "product.product" + _inherit = [ + "product.product", + "mdfe.30.prodpred", + ] + + mdfe30_xProd = fields.Char(related="name") + + mdfe30_cEAN = fields.Char(related="barcode") + + mdfe30_NCM = fields.Char(string="ncm_id.code") + + mdfe30_tpCarga = fields.Selection(default="05") diff --git a/l10n_br_mdfe/models/res_partner.py b/l10n_br_mdfe/models/res_partner.py index ae5b2a3972b8..374dcb6c9773 100644 --- a/l10n_br_mdfe/models/res_partner.py +++ b/l10n_br_mdfe/models/res_partner.py @@ -3,6 +3,7 @@ from erpbrasil.base.fiscal import cnpj_cpf from erpbrasil.base.misc import format_zipcode, punctuation_rm +from nfelib.mdfe.bindings.v3_0.mdfe_modal_rodoviario_v3_00 import Rodo from odoo import api, fields @@ -23,6 +24,8 @@ class ResPartner(spec_models.SpecModel): "mdfe.30.veicreboque_prop", "mdfe.30.veictracao_prop", "mdfe.30.infcontratante", + "mdfe.30.infresp", + "mdfe.30.infseg", ] _mdfe_search_keys = ["mdfe30_CNPJ", "mdfe30_CPF", "mdfe_xNome"] @@ -132,6 +135,12 @@ def _prepare_import_dict( mdfe30_xContato = fields.Char(related="legal_name") + mdfe30_xSeg = fields.Char(related="legal_name") + + mdfe30_respSeg = fields.Selection(default="1") + + mdfe30_tpProp = fields.Selection(default="0") + mdfe30_choice8 = fields.Selection( selection=[("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], string="CNPJ/CPF do Parceiro Autorizado", @@ -156,13 +165,15 @@ def _prepare_import_dict( mdfe30_choice11 = fields.Selection( selection=[("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], - string="CNPJ/CPF/Id Estrangeiro do Contratante", + string="CNPJ/CPF do Contratante", compute="_compute_mdfe_data", ) - mdfe30_tpProp = fields.Selection(required=True) - - mdfe30_RNTRC = fields.Char(required=True) + mdfe30_choice12 = fields.Selection( + selection=[("mdfe30_CNPJ", "CNPJ"), ("mdfe30_CPF", "CPF")], + string="CNPJ/CPF do Responsável pelo Seguro da Carga", + compute="_compute_mdfe_data", + ) @api.depends("company_type", "inscr_est", "cnpj_cpf", "country_id") def _compute_mdfe_data(self): @@ -179,6 +190,7 @@ def _compute_mdfe_data(self): rec.mdfe30_choice9 = "mdfe30_CNPJ" rec.mdfe30_choice10 = "mdfe30_CNPJ" rec.mdfe30_choice11 = "mdfe30_CNPJ" + rec.mdfe30_choice12 = "mdfe30_CNPJ" rec.mdfe30_CNPJ = cnpj_cpf rec.mdfe30_CPF = None else: @@ -187,6 +199,7 @@ def _compute_mdfe_data(self): rec.mdfe30_choice9 = "mdfe30_CPF" rec.mdfe30_choice10 = "mdfe30_CPF" rec.mdfe30_choice11 = "mdfe30_CPF" + rec.mdfe30_choice12 = "mdfe30_CPF" rec.mdfe30_CPF = cnpj_cpf rec.mdfe30_CNPJ = None else: @@ -195,6 +208,7 @@ def _compute_mdfe_data(self): rec.mdfe30_choice9 = False rec.mdfe30_choice10 = False rec.mdfe30_choice11 = False + rec.mdfe30_choice12 = False rec.mdfe30_CNPJ = "" rec.mdfe30_CPF = "" @@ -215,6 +229,7 @@ def _inverse_mdfe30_CNPJ(self): rec.mdfe30_choice9 = "mdfe30_CPF" rec.mdfe30_choice10 = "mdfe30_CPF" rec.mdfe30_choice11 = "mdfe30_CPF" + rec.mdfe30_choice12 = "mdfe30_CPF" rec.cnpj_cpf = cnpj_cpf.formata(str(rec.mdfe30_CNPJ)) def _inverse_mdfe30_CPF(self): @@ -226,6 +241,7 @@ def _inverse_mdfe30_CPF(self): rec.mdfe30_choice9 = "mdfe30_CNPJ" rec.mdfe30_choice10 = "mdfe30_CNPJ" rec.mdfe30_choice11 = "mdfe30_CNPJ" + rec.mdfe30_choice12 = "mdfe30_CNPJ" rec.cnpj_cpf = cnpj_cpf.formata(str(rec.mdfe30_CPF)) def _inverse_mdfe30_IE(self): @@ -288,29 +304,47 @@ def _inverse_mdfe30_ender(self): @api.model def export_contractor_fields(self): + if len(self) > 1: + return self.export_contractor_fields_multi() + cnpj_cpf_data = {} - if self.choice10 == "mdfe30_idEstrangeiro": + if self.mdfe30_choice10 == "mdfe30_idEstrangeiro": cnpj_cpf_data["idEstrangeiro"] = self.mdfe30_idEstrangeiro - elif self.choice10 == "mdfe30_CNPJ": - cnpj_cpf_data["CNPJ"]: self.mdfe30_CNPJ + elif self.mdfe30_choice10 == "mdfe30_CNPJ": + cnpj_cpf_data["CNPJ"] = self.mdfe30_CNPJ else: - cnpj_cpf_data["CPF"]: self.mdfe30_CPF + cnpj_cpf_data["CPF"] = self.mdfe30_CPF - return {"xNome": self.name, **cnpj_cpf_data} + return Rodo.InfAntt.InfContratante(xNome=self.name, **cnpj_cpf_data) @api.model - def export_proprietary_fields(self): + def export_contractor_fields_multi(self): + return [record.export_contractor_fields() for record in self] + + @api.model + def export_proprietary_fields(self, binding): + if len(self) > 1: + return self.export_proprietary_fields_multi() + cnpj_cpf_data = {} - if self.choice10 == "mdfe30_CNPJ": - cnpj_cpf_data["CNPJ"]: self.mdfe30_CNPJ + if self.mdfe30_choice10 == "mdfe30_CNPJ": + cnpj_cpf_data["CNPJ"] = self.mdfe30_CNPJ else: - cnpj_cpf_data["CPF"]: self.mdfe30_CPF - - return { - "xNome": self.mdfe30_xNome, - "tpProp": self.mdfe30_tpProp, - "RNTRC": self.mdfe30_RNTRC, - "IE": self.mdfe30_IE, - "UF": self.mdfe30_UF, + cnpj_cpf_data["CPF"] = self.mdfe30_CPF + + optional_data = {} + if self.mdfe30_tpProp: + optional_data["tpProp"] = self.mdfe30_tpProp + + return binding( + xNome=self.mdfe30_xNome, + RNTRC=self.mdfe30_RNTRC, + IE=self.mdfe30_IE, + UF=self.mdfe30_UF, **cnpj_cpf_data, - } + **optional_data + ) + + @api.model + def export_proprietary_fields_multi(self, binding): + return [record.export_proprietary_fields(binding) for record in self] diff --git a/l10n_br_mdfe/models/seguro_carga.py b/l10n_br_mdfe/models/seguro_carga.py new file mode 100644 index 000000000000..db59a184b4d6 --- /dev/null +++ b/l10n_br_mdfe/models/seguro_carga.py @@ -0,0 +1,23 @@ +# 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 MDFeSeguroCarga(spec_models.SpecModel): + _name = "l10n_br_mdfe.seguro.carga" + _inherit = "mdfe.30.seg" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_infResp = fields.Many2one(comodel_name="res.partner") + + mdfe30_infSeg = fields.Many2one( + comodel_name="res.partner", + domain=[ + ("is_company", "=", True), + ("cnpj_cpf", "!=", False), + ], + ) diff --git a/l10n_br_mdfe/models/transporte.py b/l10n_br_mdfe/models/transporte.py new file mode 100644 index 000000000000..94c4a467846a --- /dev/null +++ b/l10n_br_mdfe/models/transporte.py @@ -0,0 +1,76 @@ +# Copyright 2023 KMEE +# 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 MDFeTranporte(spec_models.SpecModel): + _name = "l10n_br_mdfe.transporte" + _inherit = "mdfe.30.infmdfetransp" + + mdfe30_chMDFe = fields.Char(required=True) + + mdfe30_infUnidTransp = fields.One2many(comodel_name="l10n_br_mdfe.transporte.inf") + + mdfe30_peri = fields.One2many(comodel_name="l10n_br_mdfe.transporte.perigoso") + + +class MDFeTranporteInf(spec_models.SpecModel): + _name = "l10n_br_mdfe.transporte.inf" + _inherit = "mdfe.30.tunidadetransp" + + mdfe30_tpUnidTransp = fields.Selection(required=True) + + mdfe30_idUnidTransp = fields.Char(required=True) + + mdfe30_lacUnidTransp = fields.One2many(comodel_name="l10n_br_mdfe.transporte.lacre") + + mdfe30_infUnidCarga = fields.One2many( + comodel_name="l10n_br_mdfe.transporte.carga.inf" + ) + + +class MDFeTranporteCargaInf(spec_models.SpecModel): + _name = "l10n_br_mdfe.transporte.carga.inf" + _inherit = "mdfe.30.tunidcarga" + + mdfe30_tpUnidCarga = fields.Selection(required=True) + + mdfe30_idUnidCarga = fields.Char(required=True) + + mdfe30_lacUnidCarga = fields.One2many(comodel_name="l10n_br_mdfe.transporte.lacre") + + +class MDFeTranportePerigoso(spec_models.SpecModel): + _name = "l10n_br_mdfe.transporte.perigoso" + _inherit = [ + "mdfe.30.infmdfetransp_peri", + "mdfe.30.infnfe_peri", + "mdfe.30.infcte_peri", + ] + + mdfe30_nONU = fields.Char(required=True) + + mdfe30_qTotProd = fields.Char(required=True) + + +class MDFeLacre(spec_models.SpecModel): + _name = "l10n_br_mdfe.transporte.lacre" + _inherit = ["mdfe.30.lacunidtransp", "mdfe.30.lacunidcarga", "mdfe.30.lacrodo"] + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + mdfe30_nLacre = fields.Char(required=True) + + @api.model + def export_fields(self, binding): + if len(self) > 1: + return self.export_fields_multi() + + return binding(nLacre=self.mdfe30_nLacre) + + @api.model + def export_fields_multi(self, binding): + return [record.export_fields(binding) for record in self] diff --git a/l10n_br_mdfe/security/ir.model.access.csv b/l10n_br_mdfe/security/ir.model.access.csv index 54f5d5dd62eb..ae0ba8b85d37 100644 --- a/l10n_br_mdfe/security/ir.model.access.csv +++ b/l10n_br_mdfe/security/ir.model.access.csv @@ -13,7 +13,6 @@ l10n_br_mdfe_modal_rodoviario_pagamento_banco_user,l10n_br_mdfe_modal_rodoviario l10n_br_mdfe_modal_rodoviario_veiculo_tracao_user,l10n_br_mdfe_modal_rodoviario_veiculo_tracao_user,model_l10n_br_mdfe_modal_rodoviario_veiculo_tracao,base.group_user,1,1,1,1 l10n_br_mdfe_modal_rodoviario_veiculo_condutor_user,l10n_br_mdfe_modal_rodoviario_veiculo_condutor_user,model_l10n_br_mdfe_modal_rodoviario_veiculo_condutor,base.group_user,1,1,1,1 l10n_br_mdfe_modal_rodoviario_reboque_user,l10n_br_mdfe_modal_rodoviario_reboque_user,model_l10n_br_mdfe_modal_rodoviario_reboque,base.group_user,1,1,1,1 -l10n_br_mdfe_modal_rodoviario_lacre_user,l10n_br_mdfe_modal_rodoviario_lacre_user,model_l10n_br_mdfe_modal_rodoviario_lacre,base.group_user,1,1,1,1 l10n_br_mdfe_modal_ferroviario_user,l10n_br_mdfe_modal_ferroviario_user,model_l10n_br_mdfe_modal_ferroviario,base.group_user,1,1,1,1 l10n_br_mdfe_modal_ferroviario_user_trem,l10n_br_mdfe_modal_ferroviario_user_trem,model_l10n_br_mdfe_modal_ferroviario_trem,base.group_user,1,1,1,1 @@ -28,3 +27,11 @@ l10n_br_mdfe_modal_aquaviario_transporte_vazio_user,l10n_br_mdfe_modal_aquaviari l10n_br_mdfe_document_info_user,l10n_br_mdfe_document_info_user,model_l10n_br_mdfe_document_info,base.group_user,1,1,1,1 l10n_br_mdfe_municipio_descarga_user,l10n_br_mdfe_municipio_descarga_user,model_l10n_br_mdfe_municipio_descarga,base.group_user,1,1,1,1 + +l10n_br_mdfe_transporte_user,l10n_br_mdfe_transporte_user,model_l10n_br_mdfe_transporte,base.group_user,1,1,1,1 +l10n_br_mdfe_transporte_inf_user,l10n_br_mdfe_transporte_inf_user,model_l10n_br_mdfe_transporte_inf,base.group_user,1,1,1,1 +l10n_br_mdfe_transporte_carga_inf_user,l10n_br_mdfe_transporte_carga_inf_user,model_l10n_br_mdfe_transporte_carga_inf,base.group_user,1,1,1,1 +l10n_br_mdfe_transporte_lacre_user,l10n_br_mdfe_transporte_lacre_user,model_l10n_br_mdfe_transporte_lacre,base.group_user,1,1,1,1 +l10n_br_mdfe_transporte_perigoso_user,l10n_br_mdfe_transporte_perigoso_user,model_l10n_br_mdfe_transporte_perigoso,base.group_user,1,1,1,1 + +l10n_br_mdfe_seguro_carga_user,l10n_br_mdfe_seguro_carga_user,model_l10n_br_mdfe_seguro_carga,base.group_user,1,1,1,1 diff --git a/l10n_br_mdfe/views/document.xml b/l10n_br_mdfe/views/document.xml index ccb314675f77..30857abff44d 100644 --- a/l10n_br_mdfe/views/document.xml +++ b/l10n_br_mdfe/views/document.xml @@ -13,6 +13,10 @@ 1 + + 1 + + 1 @@ -55,10 +59,53 @@ - + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+
+
+ - + - + - - + + - + - + @@ -102,11 +170,16 @@ - + + + + + + + + + + @@ -147,7 +220,10 @@ colspan="4" > - + @@ -155,15 +231,30 @@ - - - + + + - - + + @@ -216,60 +307,77 @@ colspan="4" > - + - + - - + + - + - - - + + - - - + + + +
- - - - + + + + - - - - - - -
+ + + + + + + + + + + + + + +
+ + +
diff --git a/l10n_br_mdfe/views/modal/modal_rodoviario.xml b/l10n_br_mdfe/views/modal/modal_rodoviario.xml index 5b3a2cfa47e5..4e1fe9c17303 100644 --- a/l10n_br_mdfe/views/modal/modal_rodoviario.xml +++ b/l10n_br_mdfe/views/modal/modal_rodoviario.xml @@ -124,16 +124,6 @@
- - modal.rodoviario.lacre.tree.view (in l10n_br_mdfe) - l10n_br_mdfe.modal.rodoviario.lacre - - - - - - - modal.rodoviario.ANTT.form.view (in l10n_br_mdfe) l10n_br_mdfe.modal.rodoviario.antt diff --git a/l10n_br_mdfe/views/product_product.xml b/l10n_br_mdfe/views/product_product.xml new file mode 100644 index 000000000000..025b1486f697 --- /dev/null +++ b/l10n_br_mdfe/views/product_product.xml @@ -0,0 +1,21 @@ + + + + + + mdfe.product.product.form (in l10n_br_mdfe) + product.product + + + + + + + + + + + + + diff --git a/l10n_br_mdfe/views/res_partner.xml b/l10n_br_mdfe/views/res_partner.xml index 3cf7e85d5634..2fc3ffc450ff 100644 --- a/l10n_br_mdfe/views/res_partner.xml +++ b/l10n_br_mdfe/views/res_partner.xml @@ -3,15 +3,24 @@ License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - - res.partner.form (in l10n_br_mdfe) + + mdfe.res.partner.form (in l10n_br_mdfe) res.partner - + - + + + + + + + + + + + + - - diff --git a/l10n_br_mdfe/views/transporte.xml b/l10n_br_mdfe/views/transporte.xml new file mode 100644 index 000000000000..2b447a648663 --- /dev/null +++ b/l10n_br_mdfe/views/transporte.xml @@ -0,0 +1,14 @@ + + + + + transporte.lacre.tree.view (in l10n_br_mdfe) + l10n_br_mdfe.transporte.lacre + + + + + + + From bb39642c5e773d22581556d294c4d802ba35a1bf Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Fri, 15 Sep 2023 19:47:20 -0300 Subject: [PATCH 14/72] [FIX] l10n_br_nfe: reset changes on processor --- l10n_br_nfe/models/document.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_br_nfe/models/document.py b/l10n_br_nfe/models/document.py index dd7f97148de3..e49752901aaa 100644 --- a/l10n_br_nfe/models/document.py +++ b/l10n_br_nfe/models/document.py @@ -813,6 +813,7 @@ def _processador(self): if not certificate: raise UserError(_("Certificado não encontrado")) + self._check_nfe_environment() certificado = cert.Certificado( arquivo=certificate.file, @@ -820,7 +821,6 @@ def _processador(self): ) session = Session() session.verify = False - params = { "transmissao": TransmissaoSOAP(certificado, session), "uf": self.company_id.state_id.ibge_code, From 41e3597116ebb61715b3fbe38575e60b377bc54d Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Tue, 19 Sep 2023 13:46:08 -0300 Subject: [PATCH 15/72] [ADD] l10n_br_fiscal: qr code generation workflow --- l10n_br_fiscal/models/document_workflow.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/l10n_br_fiscal/models/document_workflow.py b/l10n_br_fiscal/models/document_workflow.py index f3e93d94d7c9..43039f7357d8 100644 --- a/l10n_br_fiscal/models/document_workflow.py +++ b/l10n_br_fiscal/models/document_workflow.py @@ -93,6 +93,7 @@ def _exec_before_SITUACAO_EDOC_A_ENVIAR(self, old_state, new_state): self._document_number() self._document_comment() self._document_check() + self._document_qrcode() self._document_export() return True @@ -402,3 +403,6 @@ def action_document_correction(self): "this fical document you are not the document issuer" ) ) + + def _document_qrcode(self): + pass From d3f6203dfb42b1718fdf8cda2a7c8b69866dfc34 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Tue, 19 Sep 2023 13:46:57 -0300 Subject: [PATCH 16/72] [ADD] MDFe document supplement indo --- l10n_br_mdfe/models/document_supplement.py | 17 +++++++++++++++++ l10n_br_mdfe/security/ir.model.access.csv | 5 +++++ 2 files changed, 22 insertions(+) create mode 100644 l10n_br_mdfe/models/document_supplement.py diff --git a/l10n_br_mdfe/models/document_supplement.py b/l10n_br_mdfe/models/document_supplement.py new file mode 100644 index 000000000000..64ad94a8c74a --- /dev/null +++ b/l10n_br_mdfe/models/document_supplement.py @@ -0,0 +1,17 @@ +# Copyright 2023 KMEE (Felipe Zago Rodrigues ) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo.addons.spec_driven_model.models import spec_models + + +class MDFeSupplement(spec_models.StackedModel): + _name = "l10n_br_fiscal.document.supplement" + _inherit = "mdfe.30.infmdfesupl" + _stacked = "mdfe.30.infmdfesupl" + _field_prefix = "mdfe30_" + _schema_name = "mdfe" + _schema_version = "3.0.0" + _odoo_module = "l10n_br_mdfe" + _spec_module = "odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_tipos_basico_v3_00" + _spec_tab_name = "MDFe" diff --git a/l10n_br_mdfe/security/ir.model.access.csv b/l10n_br_mdfe/security/ir.model.access.csv index ae0ba8b85d37..d2a60cf9c39c 100644 --- a/l10n_br_mdfe/security/ir.model.access.csv +++ b/l10n_br_mdfe/security/ir.model.access.csv @@ -35,3 +35,8 @@ l10n_br_mdfe_transporte_lacre_user,l10n_br_mdfe_transporte_lacre_user,model_l10n l10n_br_mdfe_transporte_perigoso_user,l10n_br_mdfe_transporte_perigoso_user,model_l10n_br_mdfe_transporte_perigoso,base.group_user,1,1,1,1 l10n_br_mdfe_seguro_carga_user,l10n_br_mdfe_seguro_carga_user,model_l10n_br_mdfe_seguro_carga,base.group_user,1,1,1,1 + +l10n_br_fiscal_document_supplement_user,l10n_br_fiscal_document_supplement_user,model_l10n_br_fiscal_document_supplement,base.group_user,1,1,1,1 + +l10n_br_mdfe_product_lotacao_user,l10n_br_mdfe_product_lotacao_user,model_l10n_br_mdfe_product_lotacao,base.group_user,1,1,1,1 +l10n_br_mdfe_product_lotacao_local_user,l10n_br_mdfe_product_lotacao_local_user,model_l10n_br_mdfe_product_lotacao_local,base.group_user,1,1,1,1 From d6553d3a25562bf51453c440236996f0bff00a99 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Tue, 19 Sep 2023 13:48:52 -0300 Subject: [PATCH 17/72] [ADD] improvements on modal implementation --- l10n_br_mdfe/constants/mdfe.py | 2 +- l10n_br_mdfe/models/__init__.py | 1 + l10n_br_mdfe/models/document.py | 259 ++++++++++++++++++++--- l10n_br_mdfe/models/modal_aquaviario.py | 4 +- l10n_br_mdfe/models/modal_ferroviario.py | 19 +- l10n_br_mdfe/models/modal_rodoviario.py | 20 +- l10n_br_mdfe/models/product_product.py | 46 +++- l10n_br_mdfe/models/transporte.py | 2 +- l10n_br_mdfe/views/document.xml | 5 +- l10n_br_mdfe/views/product_product.xml | 41 ++++ 10 files changed, 354 insertions(+), 45 deletions(-) diff --git a/l10n_br_mdfe/constants/mdfe.py b/l10n_br_mdfe/constants/mdfe.py index 3a6f2b769a7a..8fff1df401bf 100644 --- a/l10n_br_mdfe/constants/mdfe.py +++ b/l10n_br_mdfe/constants/mdfe.py @@ -15,7 +15,7 @@ ("3", "3 - Prestador de serviço de transporte que emitirá CT-e Globalizado"), ] -MDFE_EMIT_TYPE_DEFAULT = "1" +MDFE_EMIT_TYPE_DEFAULT = "2" MDFE_TRANSP_TYPE = [ ("1", "Empresa de Transporte de Cargas – ETC"), diff --git a/l10n_br_mdfe/models/__init__.py b/l10n_br_mdfe/models/__init__.py index 31cdfecdafcc..e6b35b40c0a1 100644 --- a/l10n_br_mdfe/models/__init__.py +++ b/l10n_br_mdfe/models/__init__.py @@ -11,3 +11,4 @@ from . import document_info from . import seguro_carga from . import product_product +from . import document_supplement diff --git a/l10n_br_mdfe/models/document.py b/l10n_br_mdfe/models/document.py index db44aabb61e0..0bd0ba8d2f64 100644 --- a/l10n_br_mdfe/models/document.py +++ b/l10n_br_mdfe/models/document.py @@ -2,9 +2,11 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import re +from datetime import datetime, timedelta, timezone from unicodedata import normalize from erpbrasil.assinatura import certificado as cert +from erpbrasil.base.fiscal.edoc import ChaveEdoc from erpbrasil.transmissao import TransmissaoSOAP from nfelib.mdfe.bindings.v3_0.mdfe_modal_aereo_v3_00 import Aereo from nfelib.mdfe.bindings.v3_0.mdfe_modal_aquaviario_v3_00 import Aquav @@ -18,10 +20,22 @@ from odoo.exceptions import UserError from odoo.addons.l10n_br_fiscal.constants.fiscal import ( + AUTORIZADO, + CANCELADO, + CANCELADO_DENTRO_PRAZO, + CANCELADO_FORA_PRAZO, + DENEGADO, EVENT_ENV_HML, EVENT_ENV_PROD, + LOTE_PROCESSADO, MODELO_FISCAL_MDFE, PROCESSADOR_OCA, + SITUACAO_EDOC_AUTORIZADA, + SITUACAO_EDOC_CANCELADA, + SITUACAO_EDOC_DENEGADA, + SITUACAO_EDOC_REJEITADA, + SITUACAO_FISCAL_CANCELADO, + SITUACAO_FISCAL_CANCELADO_EXTEMPORANEO, ) from odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_modal_aquaviario_v3_00 import ( AQUAV_TPNAV, @@ -307,26 +321,26 @@ def _compute_inf_percurso(self): mdfe30_versaoModal = fields.Char(related="mdfe30_infModal.mdfe30_versaoModal") # Campos do Modal Aéreo - airplane_nationality = fields.Char() + airplane_nationality = fields.Char(size=4) - airplane_registration = fields.Char() + airplane_registration = fields.Char(size=6) - flight_number = fields.Char() + flight_number = fields.Char(size=9) flight_date = fields.Date() - boarding_airfield = fields.Char(default=MDFE_MODAL_DEFAULT_AIRCRAFT) + boarding_airfield = fields.Char(default=MDFE_MODAL_DEFAULT_AIRCRAFT, size=4) - landing_airfield = fields.Char(default=MDFE_MODAL_DEFAULT_AIRCRAFT) + landing_airfield = fields.Char(default=MDFE_MODAL_DEFAULT_AIRCRAFT, size=4) # Campos do Modal Aquaviário - ship_irin = fields.Char() + ship_irin = fields.Char(size=10) ship_type = fields.Selection(selection=MDFE_MODAL_SHIP_TYPES) - ship_code = fields.Char() + ship_code = fields.Char(size=10) - ship_name = fields.Char() + ship_name = fields.Char(size=60) ship_travel_number = fields.Char() @@ -334,22 +348,26 @@ def _compute_inf_percurso(self): ship_landing_point = fields.Selection(selection=MDFE_MODAL_HARBORS) - transshipment_port = fields.Char() + transshipment_port = fields.Char(size=60) ship_navigation_type = fields.Selection(selection=AQUAV_TPNAV) ship_loading_ids = fields.One2many( comodel_name="l10n_br_mdfe.modal.aquaviario.carregamento", inverse_name="document_id", + size=5, ) ship_unloading_ids = fields.One2many( comodel_name="l10n_br_mdfe.modal.aquaviario.descarregamento", inverse_name="document_id", + size=5, ) ship_convoy_ids = fields.One2many( - comodel_name="l10n_br_mdfe.modal.aquaviario.comboio", inverse_name="document_id" + comodel_name="l10n_br_mdfe.modal.aquaviario.comboio", + inverse_name="document_id", + size=30, ) ship_empty_load_ids = fields.One2many( @@ -363,13 +381,13 @@ def _compute_inf_percurso(self): ) # Campos do Modal Ferroviário - train_prefix = fields.Char(string="Train Prefix") + train_prefix = fields.Char(string="Train Prefix", size=10) train_release_time = fields.Datetime(string="Train Release Time") - train_origin = fields.Char(string="Train Origin") + train_origin = fields.Char(string="Train Origin", size=3) - train_destiny = fields.Char(string="Train Destiny") + train_destiny = fields.Char(string="Train Destiny", size=3) train_wagon_quantity = fields.Char(string="Train Wagon Quantity") @@ -378,7 +396,7 @@ def _compute_inf_percurso(self): ) # Campos do Modal Rodoviário - rodo_scheduling_code = fields.Char(string="Scheduling Code") + rodo_scheduling_code = fields.Char(string="Scheduling Code", size=16) rodo_ciot_ids = fields.One2many( comodel_name="l10n_br_mdfe.modal.rodoviario.ciot", inverse_name="document_id" @@ -393,7 +411,7 @@ def _compute_inf_percurso(self): rodo_contractor_ids = fields.Many2many(comodel_name="res.partner") - rodo_RNTRC = fields.Char() + rodo_RNTRC = fields.Char(size=8) rodo_payment_ids = fields.One2many( comodel_name="l10n_br_mdfe.modal.rodoviario.pagamento", @@ -405,11 +423,12 @@ def _compute_inf_percurso(self): rodo_vehicle_conductor_ids = fields.One2many( comodel_name="l10n_br_mdfe.modal.rodoviario.veiculo.condutor", inverse_name="document_id", + size=10, ) - rodo_vehicle_code = fields.Char() + rodo_vehicle_code = fields.Char(size=10) - rodo_vehicle_RENAVAM = fields.Char() + rodo_vehicle_RENAVAM = fields.Char(size=11) rodo_vehicle_plate = fields.Char() @@ -430,7 +449,9 @@ def _compute_inf_percurso(self): ) rodo_tow_ids = fields.One2many( - comodel_name="l10n_br_mdfe.modal.rodoviario.reboque", inverse_name="document_id" + comodel_name="l10n_br_mdfe.modal.rodoviario.reboque", + inverse_name="document_id", + size=3, ) rodo_seal_ids = fields.One2many( @@ -460,17 +481,13 @@ def _export_fields_mdfe_30_infmodal(self, xsd_fields, class_obj, export_dict): export_dict["any_element"] = self._export_modal_ferroviario_fields() def _export_modal_aereo_fields(self): - optional_data = {} - if self.flight_date: - optional_data["dVoo"] = fields.Date.to_string(self.flight_date) - return Aereo( nac=self.airplane_nationality, matr=self.airplane_registration, nVoo=self.flight_number, cAerEmb=self.boarding_airfield, cAerDes=self.landing_airfield, - **optional_data, + dVoo=fields.Date.to_string(self.flight_date), ) def _export_modal_aquaviario_fields(self): @@ -509,14 +526,16 @@ def _export_modal_aquaviario_fields(self): xEmbar=self.ship_name, cPrtEmb=self.ship_boarding_point, cPrtDest=self.ship_landing_point, + nViag=self.ship_travel_number, **optional_data, ) def _export_modal_ferroviario_fields(self): train_optional_data = {} if self.train_release_time: - train_optional_data["dhTrem"] = fields.Datetime.to_string( - self.train_release_time + train_optional_data["dhTrem"] = ( + datetime.strftime(self.train_release_time, "%Y-%m-%dT%H:%M:%S") + + str(timezone(timedelta(hours=-3)))[3:] ) return Ferrov( @@ -769,9 +788,21 @@ def _default_mdfe30_autxml(self): @api.depends("unloading_city_ids") def _compute_tot(self): for record in self.filtered(filtered_processador_edoc_mdfe): - record.mdfe30_qCTe = len(record.unloading_city_ids.mapped("cte_ids")) - record.mdfe30_qNFe = len(record.unloading_city_ids.mapped("nfe_ids")) - record.mdfe30_qMDFe = len(record.unloading_city_ids.mapped("mdfe_ids")) + cte_qty = len(record.unloading_city_ids.mapped("cte_ids")) + nfe_qty = len(record.unloading_city_ids.mapped("nfe_ids")) + mdfe_qty = len(record.unloading_city_ids.mapped("mdfe_ids")) + + record.mdfe30_qCTe = cte_qty and cte_qty or False + record.mdfe30_qNFe = nfe_qty and nfe_qty or False + record.mdfe30_qMDFe = mdfe_qty and mdfe_qty or False + + ########################## + # NF-e tag: infMDFeSupl + ########################## + + mdfe30_infMDFeSupl = fields.Many2one( + comodel_name="l10n_br_fiscal.document.supplement", + ) @api.depends("mdfe30_cUnid") def _compute_tot_carga(self): @@ -819,7 +850,12 @@ def _serialize(self, edocs): filtered_processador_edoc_mdfe ): inf_mdfe = record.export_ds()[0] - mdfe = Mdfe(infMDFe=inf_mdfe, infMDFeSupl=None, signature=None) + + inf_mdfe_supl = None + if record.mdfe30_infMDFeSupl: + inf_mdfe_supl = record.mdfe30_infMDFeSupl.export_ds()[0] + + mdfe = Mdfe(infMDFe=inf_mdfe, infMDFeSupl=inf_mdfe_supl, signature=None) edocs.append(mdfe) return edocs @@ -842,9 +878,44 @@ def _processador(self): params = { "transmissao": TransmissaoSOAP(certificado, session), + "versao": self.mdfe_version, + "ambiente": self.mdfe_environment, + "uf": self.company_id.state_id.ibge_code, } return edoc_mdfe(**params) + def _generate_key(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + date = fields.Datetime.context_timestamp(record, record.document_date) + chave_edoc = ChaveEdoc( + ano_mes=date.strftime("%y%m").zfill(4), + cnpj_cpf_emitente=record.company_cnpj_cpf, + codigo_uf=( + record.company_state_id and record.company_state_id.ibge_code or "" + ), + forma_emissao=int(self.mdfe_transmission), + modelo_documento=record.document_type_id.code or "", + numero_documento=record.document_number or "", + numero_serie=record.document_serie or "", + validar=False, + ) + record.key_random_code = chave_edoc.codigo_aleatorio + record.key_check_digit = chave_edoc.digito_verificador + record.document_key = chave_edoc.chave + + def _document_qrcode(self): + for record in self.filtered(filtered_processador_edoc_mdfe): + record.mdfe30_infMDFeSupl = self.env[ + "l10n_br_fiscal.document.supplement" + ].create({"mdfe30_qrCodMDFe": record.get_mdfe_qrcode()}) + + def _document_cancel(self, justificative): + result = super(MDFe, self)._document_cancel(justificative) + online_event = self.filtered(filtered_processador_edoc_mdfe) + if online_event: + online_event.cancel_mdfe() + return result + def _document_export(self, pretty_print=True): result = super()._document_export() for record in self.filtered(filtered_processador_edoc_mdfe): @@ -872,3 +943,133 @@ def _valida_xml(self, xml_file): erros = Mdfe.schema_validation(xml_file) erros = "\n".join(erros) self.write({"xml_error_message": erros or False}) + + def atualiza_status_mdfe(self, processo): + self.ensure_one() + + infProt = processo.resposta.protMDFe.infProt + + if infProt.cStat in AUTORIZADO: + state = SITUACAO_EDOC_AUTORIZADA + 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=processo.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(MDFe, self)._eletronic_document_send() + for record in self.filtered(filtered_processador_edoc_mdfe): + if record.xml_error_message: + return + + processador = record._processador() + for edoc in record.serialize(): + processo = None + for p in processador.processar_documento(edoc): + processo = p + if processo.webservice == "mdfeRecepcaoLote": + record.authorization_event_id._save_event_file( + processo.envio_xml.decode("utf-8"), "xml" + ) + + if processo.resposta.cStat in LOTE_PROCESSADO + ["100"]: + record.atualiza_status_mdfe(processo) + + elif processo.resposta.cStat in DENEGADO: + record._change_state(SITUACAO_EDOC_DENEGADA) + record.write( + { + "status_code": processo.resposta.cStat, + "status_name": processo.resposta.xMotivo, + } + ) + + else: + record._change_state(SITUACAO_EDOC_REJEITADA) + record.write( + { + "status_code": processo.resposta.cStat, + "status_name": processo.resposta.xMotivo, + } + ) + + def cancel_mdfe(self): + self.ensure_one() + processador = self._processador() + + if not self.authorization_protocol: + raise UserError(_("Authorization Protocol Not Found!")) + + processo = processador.cancela_documento( + chave=self.document_key, + protocolo_autorizacao=self.authorization_protocol, + justificativa=self.cancel_reason.replace("\n", "\\n"), + ) + + self.cancel_event_id = self.event_ids.create_event_save_xml( + company_id=self.company_id, + environment=( + EVENT_ENV_PROD if self.mdfe_environment == "1" else EVENT_ENV_HML + ), + event_type="2", + xml_file=processo.envio_xml.decode("utf-8"), + document_id=self, + ) + + infEvento = processo.resposta.infEvento + if infEvento.cStat not in CANCELADO: + mensagem = "Erro no cancelamento" + mensagem += "\nCódigo: " + infEvento.cStat + mensagem += "\nMotivo: " + infEvento.xMotivo + raise UserError(mensagem) + + if infEvento.cStat == CANCELADO_FORA_PRAZO: + self.state_fiscal = SITUACAO_FISCAL_CANCELADO_EXTEMPORANEO + elif infEvento.cStat == CANCELADO_DENTRO_PRAZO: + self.state_fiscal = SITUACAO_FISCAL_CANCELADO + + self.state_edoc = SITUACAO_EDOC_CANCELADA + self.cancel_event_id.set_done( + status_code=infEvento.cStat, + response=infEvento.xMotivo, + protocol_date=fields.Datetime.to_string( + datetime.fromisoformat(infEvento.dhRegEvento) + ), + protocol_number=infEvento.nProt, + file_response_xml=processo.retorno.content.decode("utf-8"), + ) + + def get_mdfe_qrcode(self): + if self.document_type != MODELO_FISCAL_MDFE: + return + + processador = self._processador() + if self.mdfe_transmission == "1": + return processador.monta_qrcode(self.document_key) + + serialized_doc = self.serialize()[0] + xml = processador.assina_raiz(serialized_doc, serialized_doc.infMDFe.Id) + return processador.monta_qrcode_contingencia(serialized_doc, xml) diff --git a/l10n_br_mdfe/models/modal_aquaviario.py b/l10n_br_mdfe/models/modal_aquaviario.py index 31490297e902..ccb6eecd0326 100644 --- a/l10n_br_mdfe/models/modal_aquaviario.py +++ b/l10n_br_mdfe/models/modal_aquaviario.py @@ -116,9 +116,9 @@ class MDFeModalAquaviarioComboio(spec_models.SpecModel): document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") - mdfe30_cEmbComb = fields.Char(required=True) + mdfe30_cEmbComb = fields.Char(required=True, size=10) - mdfe30_xBalsa = fields.Char(required=True) + mdfe30_xBalsa = fields.Char(required=True, size=60) @api.model def export_fields(self): diff --git a/l10n_br_mdfe/models/modal_ferroviario.py b/l10n_br_mdfe/models/modal_ferroviario.py index 0e95412000b9..306ab441f0a8 100644 --- a/l10n_br_mdfe/models/modal_ferroviario.py +++ b/l10n_br_mdfe/models/modal_ferroviario.py @@ -3,7 +3,8 @@ from nfelib.mdfe.bindings.v3_0.mdfe_modal_ferroviario_v3_00 import Ferrov -from odoo import api, fields +from odoo import _, api, fields +from odoo.exceptions import UserError from odoo.addons.spec_driven_model.models import spec_models @@ -48,6 +49,16 @@ class MDFeModalFerroviarioVagao(spec_models.SpecModel): mdfe30_TU = fields.Char(required=True) + @api.constrains("mdfe30_serie") + def check_serie(self): + for _record in self.filtered(lambda v: len(v.mdfe30_serie) != 3): + raise UserError(_("Wagon serie must have exactly 3 digits.")) + + @api.constrains("mdfe30_tpVag") + def check_tp_vag(self): + for _record in self.filtered(lambda v: len(v.mdfe30_tpVag) != 3): + raise UserError(_("Wagon type must have exactly 3 digits.")) + @api.model def export_fields(self): if len(self) > 1: @@ -61,11 +72,11 @@ def export_fields(self): optional_data["nSeq"] = self.mdfe30_nSeq return Ferrov.Vag( - pesoBC=self.mdfe30_pesoBC, - pesoR=self.mdfe30_pesoR, + pesoBC="{:.3f}".format(self.mdfe30_pesoBC), + pesoR="{:.3f}".format(self.mdfe30_pesoR), + TU="{:.3f}".format(float(self.mdfe30_TU)), serie=self.mdfe30_serie, nVag=self.mdfe30_nVag, - TU=self.mdfe30_TU, **optional_data ) diff --git a/l10n_br_mdfe/models/modal_rodoviario.py b/l10n_br_mdfe/models/modal_rodoviario.py index 2e85d8f6be96..b4071b1a8799 100644 --- a/l10n_br_mdfe/models/modal_rodoviario.py +++ b/l10n_br_mdfe/models/modal_rodoviario.py @@ -44,6 +44,8 @@ class MDFeModalRodoviarioANTT(spec_models.SpecModel): comodel_name="l10n_br_mdfe.modal.rodoviario.pagamento" ) + mdfe30_RNTRC = fields.Char(size=8) + class MDFeModalRodoviarioCIOT(spec_models.SpecModel): _name = "l10n_br_mdfe.modal.rodoviario.ciot" @@ -52,7 +54,7 @@ class MDFeModalRodoviarioCIOT(spec_models.SpecModel): document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") - mdfe30_CIOT = fields.Char(required=True) + mdfe30_CIOT = fields.Char(required=True, size=12) is_company = fields.Boolean(string="É empresa?", required=True) @@ -128,7 +130,7 @@ def export_fields(self): return Rodo.InfAntt.ValePed.Disp( CNPJForn=self.mdfe30_CNPJForn, - vValePed=self.mdfe30_vValePed, + vValePed="{:.2f}".format(self.mdfe30_vValePed), **optional_data, ) @@ -222,7 +224,7 @@ def export_fields(self): additional_data["xNome"] = self.mdfe30_xNome return Rodo.InfAntt.InfPag( - vContrato=self.mdfe30_vContrato, + vContrato="{:.2f}".format(self.mdfe30_vContrato), indPag=self.mdfe30_indPag, infBanc=self.mdfe30_infBanc.export_fields(), **additional_data, @@ -300,11 +302,13 @@ def export_fields(self): return self.export_fields_multi() optional_data = {} - if self.mdfe30_vComp: - optional_data["xComp"] = self.mdfe30_vComp + if self.mdfe30_xComp: + optional_data["xComp"] = self.mdfe30_xComp return Rodo.InfAntt.InfPag.Comp( - tpComp=self.mdfe30_tpComp, vComp=self.mdfe30_vComp, **optional_data + tpComp=self.mdfe30_tpComp, + vComp="{:.2f}".format(self.mdfe30_vComp), + **optional_data, ) @api.model @@ -399,6 +403,10 @@ class MDFeModalRodoviarioReboque(spec_models.SpecModel): mdfe30_tpCar = fields.Selection(required=True) + mdfe30_cInt = fields.Char(size=10) + + mdfe30_RENAVAM = fields.Char(size=11) + @api.model def export_fields(self): if len(self) > 1: diff --git a/l10n_br_mdfe/models/product_product.py b/l10n_br_mdfe/models/product_product.py index 6f515a8271d8..39ef283f168e 100644 --- a/l10n_br_mdfe/models/product_product.py +++ b/l10n_br_mdfe/models/product_product.py @@ -1,7 +1,7 @@ # Copyright 2023 KMEE # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields +from odoo import api, fields from odoo.addons.spec_driven_model.models import spec_models @@ -20,3 +20,47 @@ class ProductProduct(spec_models.SpecModel): mdfe30_NCM = fields.Char(string="ncm_id.code") mdfe30_tpCarga = fields.Selection(default="05") + + mdfe30_infLotacao = fields.Many2one(comodel_name="l10n_br_mdfe.product.lotacao") + + +class MDFeProductLotacao(spec_models.SpecModel): + _name = "l10n_br_mdfe.product.lotacao" + _inherit = "mdfe.30.inflotacao" + + mdfe30_infLocalCarrega = fields.Many2one( + comodel_name="l10n_br_mdfe.product.lotacao.local", + required=True, + ) + + mdfe30_infLocalDescarrega = fields.Many2one( + comodel_name="l10n_br_mdfe.product.lotacao.local", + required=True, + ) + + +class MDFeProductLotacaoLocal(spec_models.SpecModel): + _name = "l10n_br_mdfe.product.lotacao.local" + _inherit = ["mdfe.30.inflocalcarrega", "mdfe.30.inflocaldescarrega"] + + local_type = fields.Selection( + selection=[ + ("CEP", "CEP"), + ("coord", "Coordenadas"), + ], + default="CEP", + ) + + mdfe30_choice12 = fields.Selection( + selection=[("mdfe30_CEP", "CEP"), ("mdfe30_latitude", "Latitude/Longitude")], + string="Tipo de Local", + compute="_compute_choice", + ) + + @api.depends("local_type") + def _compute_choice(self): + for record in self: + if record.local_type == "CEP": + pass + else: + pass diff --git a/l10n_br_mdfe/models/transporte.py b/l10n_br_mdfe/models/transporte.py index 94c4a467846a..a2a3d5505415 100644 --- a/l10n_br_mdfe/models/transporte.py +++ b/l10n_br_mdfe/models/transporte.py @@ -62,7 +62,7 @@ class MDFeLacre(spec_models.SpecModel): document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") - mdfe30_nLacre = fields.Char(required=True) + mdfe30_nLacre = fields.Char(required=True, size=20) @api.model def export_fields(self, binding): diff --git a/l10n_br_mdfe/views/document.xml b/l10n_br_mdfe/views/document.xml index 30857abff44d..3c0815e11b8b 100644 --- a/l10n_br_mdfe/views/document.xml +++ b/l10n_br_mdfe/views/document.xml @@ -201,7 +201,10 @@ name="flight_number" attrs="{'required': [('mdfe_modal', '=', '2')]}" /> - + + + + product.lotacao.form.view (in l10n_br_mdfe) + l10n_br_mdfe.product.lotacao + +
+ + + + + + +
+
+
+ + + product.lotacao.local.form.view (in l10n_br_mdfe) + l10n_br_mdfe.product.lotacao.local + +
+ + + + + + + + +
+
+
From d28ab0a8924c654221d75e8cedf2df65d628194f Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Tue, 19 Sep 2023 15:41:40 -0300 Subject: [PATCH 18/72] [ADD] l10n_br_fiscal: evento de encerramento de MDFe --- l10n_br_fiscal/constants/fiscal.py | 3 +++ l10n_br_fiscal/models/document_event.py | 2 ++ l10n_br_fiscal/views/document_view.xml | 6 +++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/l10n_br_fiscal/constants/fiscal.py b/l10n_br_fiscal/constants/fiscal.py index ad1bf6335308..d7d5fe4082f0 100644 --- a/l10n_br_fiscal/constants/fiscal.py +++ b/l10n_br_fiscal/constants/fiscal.py @@ -355,6 +355,7 @@ LOTE_PROCESSADO = ["104"] LOTE_EM_PROCESSAMENTO = ["105"] CONTINGENCIA = ("108", "109") +ENCERRADO = ["132", "135"] CANCELAMENTO_HOMOLOGADO = ["101", "151"] @@ -378,6 +379,7 @@ SITUACAO_EDOC_CANCELADA = "cancelada" SITUACAO_EDOC_DENEGADA = "denegada" SITUACAO_EDOC_INUTILIZADA = "inutilizada" +SITUACAO_EDOC_ENCERRADA = "encerrada" SITUACAO_EDOC = [ @@ -389,6 +391,7 @@ (SITUACAO_EDOC_CANCELADA, "Cancelada"), (SITUACAO_EDOC_DENEGADA, "Denegada"), (SITUACAO_EDOC_INUTILIZADA, "Inutilizada"), + (SITUACAO_EDOC_ENCERRADA, "Encerrada"), ] SITUACAO_EDOC_DICT = dict(SITUACAO_EDOC) diff --git a/l10n_br_fiscal/models/document_event.py b/l10n_br_fiscal/models/document_event.py index d613ce041e9f..7440c7d68825 100644 --- a/l10n_br_fiscal/models/document_event.py +++ b/l10n_br_fiscal/models/document_event.py @@ -30,6 +30,7 @@ "12": "dist-dfe", "13": "man", "14": "cce", + "15": "enc-mdfe", } @@ -84,6 +85,7 @@ def _compute_display_name(self): ("12", "Distribuição DFe"), ("13", "Manifestação"), ("14", "Carta de Correção"), + ("15", "Encerramento de MDFe"), ], string="Service", ) diff --git a/l10n_br_fiscal/views/document_view.xml b/l10n_br_fiscal/views/document_view.xml index fceaacfe3f07..7755378c869d 100644 --- a/l10n_br_fiscal/views/document_view.xml +++ b/l10n_br_fiscal/views/document_view.xml @@ -148,7 +148,7 @@ type="object" string="Invalidar Numeração" groups="l10n_br_fiscal.group_user" - attrs="{'invisible': [('state_edoc', 'in', ('em_digitacao', 'autorizada', 'cancelada', 'denegada'))]}" + attrs="{'invisible': [('state_edoc', 'in', ('em_digitacao', 'autorizada', 'cancelada', 'denegada', 'encerrada'))]}" />