From 3e2b1ec6c5f81b2e54c7d1ec3bdb0376a218e0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Wed, 19 Jun 2024 04:38:01 +0000 Subject: [PATCH 01/27] [REF] l10n_br_cte_spec: black 24.4.2 formatting --- .../models/v4_0/cte_modal_aereo_v4_00.py | 37 +- .../models/v4_0/cte_modal_aquaviario_v4_00.py | 34 +- .../models/v4_0/cte_modal_dutoviario_v4_00.py | 1 + .../v4_0/cte_modal_ferroviario_v4_00.py | 9 +- .../v4_0/cte_modal_rodoviario_os_v4_00.py | 7 +- .../models/v4_0/cte_modal_rodoviario_v4_00.py | 6 +- .../models/v4_0/cte_multi_modal_v4_00.py | 6 +- .../models/v4_0/cte_tipos_basico_v4_00.py | 460 ++++++++++++++---- .../v4_0/evento_cte_tipos_basico_v4_00.py | 14 +- .../models/v4_0/tipos_geral_cte_v4_00.py | 1 + 10 files changed, 460 insertions(+), 115 deletions(-) diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_aereo_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_aereo_v4_00.py index ca0ea2c6db56..4f9683a151d4 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_aereo_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_aereo_v4_00.py @@ -30,7 +30,10 @@ "06", "gelo seco para refrigeração (especificar no campo observações a quantidade)", ), - ("07", "não restrito (especificar a Disposição Especial no campo observações)"), + ( + "07", + "não restrito (especificar a Disposição Especial no campo observações)", + ), ( "08", "artigo perigoso em carga consolidada (especificar a quantidade no campo observações)", @@ -39,10 +42,22 @@ "09", "autorização da autoridade governamental anexa (especificar no campo observações)", ), - ("10", "baterias de íons de lítio em conformidade com a Seção II da PI965 – CAO"), - ("11", "baterias de íons de lítio em conformidade com a Seção II da PI966"), - ("12", "baterias de íons de lítio em conformidade com a Seção II da PI967"), - ("13", "baterias de metal lítio em conformidade com a Seção II da PI968 — CAO"), + ( + "10", + "baterias de íons de lítio em conformidade com a Seção II da PI965 – CAO", + ), + ( + "11", + "baterias de íons de lítio em conformidade com a Seção II da PI966", + ), + ( + "12", + "baterias de íons de lítio em conformidade com a Seção II da PI967", + ), + ( + "13", + "baterias de metal lítio em conformidade com a Seção II da PI968 — CAO", + ), ("14", "baterias de metal lítio em conformidade com a Seção II da PI969"), ("15", "baterias de metal lítio em conformidade com a Seção II da PI970"), ("99", "outro (especificar no campo observações)"), @@ -51,6 +66,7 @@ class Aereo(models.AbstractModel): "Informações do modal Aéreo" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aereo" _inherit = "spec.mixin.cte" @@ -87,11 +103,15 @@ class Aereo(models.AbstractModel): ) cte40_natCarga = fields.Many2one( - comodel_name="cte.40.natcarga", string="Natureza da carga", xsd_required=True + comodel_name="cte.40.natcarga", + string="Natureza da carga", + xsd_required=True, ) cte40_tarifa = fields.Many2one( - comodel_name="cte.40.tarifa", string="Informações de tarifa", xsd_required=True + comodel_name="cte.40.tarifa", + string="Informações de tarifa", + xsd_required=True, ) cte40_peri = fields.One2many( @@ -109,6 +129,7 @@ class Aereo(models.AbstractModel): class NatCarga(models.AbstractModel): "Natureza da carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.natcarga" _inherit = "spec.mixin.cte" @@ -126,9 +147,9 @@ class NatCarga(models.AbstractModel): ) - class Tarifa(models.AbstractModel): "Informações de tarifa" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tarifa" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_aquaviario_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_aquaviario_v4_00.py index cc85f0bb6e25..e574119a2003 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_aquaviario_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_aquaviario_v4_00.py @@ -24,6 +24,7 @@ class Aquav(models.AbstractModel): "Informações do modal Aquaviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aquav" _inherit = "spec.mixin.cte" @@ -45,10 +46,14 @@ class Aquav(models.AbstractModel): help=("AFRMM (Adicional de Frete para Renovação da Marinha Mercante)"), ) - cte40_xNavio = fields.Char(string="Identificação do Navio", xsd_required=True) + cte40_xNavio = fields.Char( + string="Identificação do Navio", xsd_required=True + ) cte40_balsa = fields.One2many( - "cte.40.balsa", "cte40_balsa_aquav_id", string="Grupo de informações das balsas" + "cte.40.balsa", + "cte40_balsa_aquav_id", + string="Grupo de informações das balsas", ) cte40_nViag = fields.Char(string="Número da Viagem") @@ -88,6 +93,7 @@ class Aquav(models.AbstractModel): class Balsa(models.AbstractModel): "Grupo de informações das balsas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.balsa" _inherit = "spec.mixin.cte" @@ -96,7 +102,9 @@ class Balsa(models.AbstractModel): cte40_balsa_aquav_id = fields.Many2one( comodel_name="cte.40.aquav", xsd_implicit=True, ondelete="cascade" ) - cte40_xBalsa = fields.Char(string="Identificador da Balsa", xsd_required=True) + cte40_xBalsa = fields.Char( + string="Identificador da Balsa", xsd_required=True + ) class DetCont(models.AbstractModel): @@ -112,14 +120,18 @@ class DetCont(models.AbstractModel): comodel_name="cte.40.aquav", xsd_implicit=True, ondelete="cascade" ) cte40_nCont = fields.Char( - string="Identificação do Container", xsd_required=True, xsd_type="TContainer" + string="Identificação do Container", + xsd_required=True, + xsd_type="TContainer", ) cte40_lacre = fields.One2many( "cte.40.lacre", "cte40_lacre_detCont_id", string="Grupo de informações dos lacres", - help=("Grupo de informações dos lacres dos cointainers da qtde da carga"), + help=( + "Grupo de informações dos lacres dos cointainers da qtde da carga" + ), ) cte40_infDoc = fields.Many2one( @@ -131,6 +143,7 @@ class DetCont(models.AbstractModel): class Lacre(models.AbstractModel): "Grupo de informações dos lacres dos cointainers da qtde da carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.lacre" _inherit = "spec.mixin.cte" @@ -144,6 +157,7 @@ class Lacre(models.AbstractModel): class AquavInfDoc(models.AbstractModel): "Informações dos documentos dos conteiners" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aquav_infdoc" _inherit = "spec.mixin.cte" @@ -168,13 +182,16 @@ class AquavInfDoc(models.AbstractModel): class AquavInfNf(models.AbstractModel): "Informações das NF" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aquav_infnf" _inherit = "spec.mixin.cte" _binding_type = "Aquav.DetCont.InfDoc.InfNf" cte40_infNF_infDoc_id = fields.Many2one( - comodel_name="cte.40.aquav_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.aquav_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_serie = fields.Char(string="Série", xsd_required=True) @@ -190,13 +207,16 @@ class AquavInfNf(models.AbstractModel): class AquavInfNfe(models.AbstractModel): "Informações das NFe" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.aquav_infnfe" _inherit = "spec.mixin.cte" _binding_type = "Aquav.DetCont.InfDoc.InfNfe" cte40_infNFe_infDoc_id = fields.Many2one( - comodel_name="cte.40.aquav_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.aquav_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_chave = fields.Char( string="Chave de acesso da NF-e", xsd_required=True, xsd_type="TChDFe" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_dutoviario_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_dutoviario_v4_00.py index 049ecddcd0da..499f1fb6b22b 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_dutoviario_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_dutoviario_v4_00.py @@ -10,6 +10,7 @@ class Duto(models.AbstractModel): "Informações do modal Dutoviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.duto" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_ferroviario_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_ferroviario_v4_00.py index 528246f366f4..83469dc6a1c2 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_ferroviario_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_ferroviario_v4_00.py @@ -32,6 +32,7 @@ class TenderFer(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tenderfer" _inherit = "spec.mixin.cte" @@ -58,7 +59,10 @@ class TenderFer(models.AbstractModel): cte40_xMun = fields.Char( string="Nome do município", xsd_required=True, - help=("Nome do município\nInformar EXTERIOR para operações com o " "exterior."), + help=( + "Nome do município\nInformar EXTERIOR para operações com o " + "exterior." + ), ) cte40_CEP = fields.Char(string="CEP", xsd_required=True) @@ -74,6 +78,7 @@ class TenderFer(models.AbstractModel): class Ferrov(models.AbstractModel): "Informações do modal Ferroviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.ferrov" _inherit = "spec.mixin.cte" @@ -108,6 +113,7 @@ class Ferrov(models.AbstractModel): class TrafMut(models.AbstractModel): "Detalhamento de informações para o tráfego mútuo" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.trafmut" _inherit = "spec.mixin.cte" @@ -157,6 +163,7 @@ class TrafMut(models.AbstractModel): class FerroEnv(models.AbstractModel): "Informações das Ferrovias Envolvidas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.ferroenv" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_os_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_os_v4_00.py index cd85efc1bdc9..00a35ea8047e 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_os_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_os_v4_00.py @@ -25,6 +25,7 @@ class RodoOs(models.AbstractModel): "Informações do modal Rodoviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.rodoos" _inherit = "spec.mixin.cte" @@ -53,7 +54,9 @@ class RodoOs(models.AbstractModel): ), ) - cte40_veic = fields.Many2one(comodel_name="cte.40.veic", string="Dados do Veículo") + cte40_veic = fields.Many2one( + comodel_name="cte.40.veic", string="Dados do Veículo" + ) cte40_infFretamento = fields.Many2one( comodel_name="cte.40.inffretamento", @@ -64,6 +67,7 @@ class RodoOs(models.AbstractModel): class Veic(models.AbstractModel): "Dados do Veículo" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.veic" _inherit = "spec.mixin.cte" @@ -163,6 +167,7 @@ class Prop(models.AbstractModel): class InfFretamento(models.AbstractModel): "Dados do fretamento (apenas para Transporte de Pessoas)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.inffretamento" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_v4_00.py index 569ac6087f79..e9c048fe476c 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_modal_rodoviario_v4_00.py @@ -12,6 +12,7 @@ class Rodo(models.AbstractModel): "Informações do modal Rodoviário" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.rodo" _inherit = "spec.mixin.cte" @@ -35,6 +36,7 @@ class Rodo(models.AbstractModel): class Occ(models.AbstractModel): "Ordens de Coleta associados" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.occ" _inherit = "spec.mixin.cte" @@ -45,7 +47,9 @@ class Occ(models.AbstractModel): ) cte40_serie = fields.Char(string="Série da OCC") - cte40_nOcc = fields.Char(string="Número da Ordem de coleta", xsd_required=True) + cte40_nOcc = fields.Char( + string="Número da Ordem de coleta", xsd_required=True + ) cte40_dEmi = fields.Date( string="Data de emissão da ordem de coleta", diff --git a/l10n_br_cte_spec/models/v4_0/cte_multi_modal_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_multi_modal_v4_00.py index df1d6a616358..34201d7161f9 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_multi_modal_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_multi_modal_v4_00.py @@ -16,6 +16,7 @@ class Multimodal(models.AbstractModel): "Informações do Multimodal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.multimodal" _inherit = "spec.mixin.cte" @@ -32,7 +33,8 @@ class Multimodal(models.AbstractModel): string="Indicador Negociável", xsd_required=True, help=( - "Indicador Negociável\nPreencher com: 0 - Não Negociável; 1 - " "Negociável" + "Indicador Negociável\nPreencher com: 0 - Não Negociável; 1 - " + "Negociável" ), ) @@ -44,6 +46,7 @@ class Multimodal(models.AbstractModel): class MultimodalSeg(models.AbstractModel): "Informações de Seguro do Multimodal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.multimodal_seg" _inherit = "spec.mixin.cte" @@ -73,6 +76,7 @@ class MultimodalSeg(models.AbstractModel): class InfSeg(models.AbstractModel): "Informações da seguradora" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infseg" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py index 235fa73e02f7..b532fe9630c1 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py @@ -269,6 +269,7 @@ class TrespTec(models.AbstractModel): "Tipo Dados da Responsável Técnico" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tresptec" _inherit = "spec.mixin.cte" @@ -334,6 +335,7 @@ class TrespTec(models.AbstractModel): class TendOrg(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendorg" _inherit = "spec.mixin.cte" @@ -360,7 +362,10 @@ class TendOrg(models.AbstractModel): cte40_xMun = fields.Char( string="Nome do município", xsd_required=True, - help=("Nome do município\nInformar EXTERIOR para operações com o " "exterior."), + help=( + "Nome do município\nInformar EXTERIOR para operações com o " + "exterior." + ), ) cte40_CEP = fields.Char(string="CEP") @@ -382,6 +387,7 @@ class TendOrg(models.AbstractModel): class TendReEnt(models.AbstractModel): "Tipo Dados do Local de Retirada ou Entrega" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendreent" _inherit = "spec.mixin.cte" @@ -424,7 +430,10 @@ class TendReEnt(models.AbstractModel): cte40_xMun = fields.Char( string="Nome do município", xsd_required=True, - help=("Nome do município\nInformar EXTERIOR para operações com o " "exterior."), + help=( + "Nome do município\nInformar EXTERIOR para operações com o " + "exterior." + ), ) cte40_UF = fields.Selection( @@ -438,6 +447,7 @@ class TendReEnt(models.AbstractModel): class TendeEmi(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendeemi" _inherit = "spec.mixin.cte" @@ -460,10 +470,15 @@ class TendeEmi(models.AbstractModel): cte40_xMun = fields.Char(string="Nome do município", xsd_required=True) - cte40_CEP = fields.Char(string="CEP", help="CEP\nInformar zeros não significativos") + cte40_CEP = fields.Char( + string="CEP", help="CEP\nInformar zeros não significativos" + ) cte40_UF = fields.Selection( - TUF_SEM_EX, string="Sigla da UF", xsd_required=True, xsd_type="TUF_sem_EX" + TUF_SEM_EX, + string="Sigla da UF", + xsd_required=True, + xsd_type="TUF_sem_EX", ) cte40_fone = fields.Char(string="Telefone", xsd_type="TFone") @@ -471,6 +486,7 @@ class TendeEmi(models.AbstractModel): class Tendereco(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendereco" _inherit = "spec.mixin.cte" @@ -497,7 +513,10 @@ class Tendereco(models.AbstractModel): cte40_xMun = fields.Char( string="Nome do município", xsd_required=True, - help=("Nome do município\nInformar EXTERIOR para operações com o " "exterior."), + help=( + "Nome do município\nInformar EXTERIOR para operações com o " + "exterior." + ), ) cte40_CEP = fields.Char( @@ -513,7 +532,8 @@ class Tendereco(models.AbstractModel): ) cte40_cPais = fields.Char( - string="Código do país", help="Código do país\nUtilizar a tabela do BACEN" + string="Código do país", + help="Código do país\nUtilizar a tabela do BACEN", ) cte40_xPais = fields.Char(string="Nome do país") @@ -521,6 +541,7 @@ class Tendereco(models.AbstractModel): class Tendernac(models.AbstractModel): "Tipo Dados do Endereço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tendernac" _inherit = "spec.mixin.cte" @@ -548,7 +569,8 @@ class Tendernac(models.AbstractModel): string="Nome do município, ", xsd_required=True, help=( - "Nome do município, , informar EXTERIOR para operações com o " "exterior." + "Nome do município, , informar EXTERIOR para operações com o " + "exterior." ), ) @@ -565,6 +587,7 @@ class Tendernac(models.AbstractModel): class Timp(models.AbstractModel): "Tipo Dados do Imposto CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.timp" _inherit = "spec.mixin.cte" @@ -573,6 +596,7 @@ class Timp(models.AbstractModel): class TimpOs(models.AbstractModel): "Tipo Dados do Imposto para CT-e OS" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.timpos" _inherit = "spec.mixin.cte" @@ -581,6 +605,7 @@ class TimpOs(models.AbstractModel): class Tlocal(models.AbstractModel): "Tipo Dados do Local de Origem ou Destino" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tlocal" _inherit = "spec.mixin.cte" @@ -602,6 +627,7 @@ class Tlocal(models.AbstractModel): class TprotCte(models.AbstractModel): "Tipo Protocolo de status resultado do processamento da CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcte" _inherit = "spec.mixin.cte" @@ -622,6 +648,7 @@ class TprotCte(models.AbstractModel): class TprotCteInfProt(models.AbstractModel): "Dados do protocolo de status" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcte_infprot" _inherit = "spec.mixin.cte" @@ -650,7 +677,9 @@ class TprotCteInfProt(models.AbstractModel): string="Data e hora de processamento", xsd_required=True, xsd_type="TDateTimeUTC", - help=("Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD."), + help=( + "Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD." + ), ) cte40_nProt = fields.Char( @@ -666,7 +695,9 @@ class TprotCteInfProt(models.AbstractModel): ), ) - cte40_cStat = fields.Char(string="Código do status do CT-e", xsd_required=True) + cte40_cStat = fields.Char( + string="Código do status do CT-e", xsd_required=True + ) cte40_xMotivo = fields.Char( string="Descrição literal do status do CT-e", @@ -679,6 +710,7 @@ class TprotCteInfProt(models.AbstractModel): class TprotCteInfFisco(models.AbstractModel): "Mensagem do Fisco" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcte_inffisco" _inherit = "spec.mixin.cte" @@ -717,6 +749,7 @@ class TprotCteOs(models.AbstractModel): class TprotCteOsInfProt(models.AbstractModel): "Dados do protocolo de status" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcteos_infprot" _inherit = "spec.mixin.cte" @@ -745,7 +778,9 @@ class TprotCteOsInfProt(models.AbstractModel): string="Data e hora de processamento", xsd_required=True, xsd_type="TDateTimeUTC", - help=("Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD."), + help=( + "Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD." + ), ) cte40_nProt = fields.Char( @@ -761,7 +796,9 @@ class TprotCteOsInfProt(models.AbstractModel): ), ) - cte40_cStat = fields.Char(string="Código do status do CT-e", xsd_required=True) + cte40_cStat = fields.Char( + string="Código do status do CT-e", xsd_required=True + ) cte40_xMotivo = fields.Char( string="Descrição literal do status do CT-e", @@ -774,6 +811,7 @@ class TprotCteOsInfProt(models.AbstractModel): class TprotCteOsInfFisco(models.AbstractModel): "Mensagem do Fisco" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotcteos_inffisco" _inherit = "spec.mixin.cte" @@ -812,6 +850,7 @@ class TprotGtve(models.AbstractModel): class TprotGtveInfProt(models.AbstractModel): "Dados do protocolo de status" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotgtve_infprot" _inherit = "spec.mixin.cte" @@ -840,7 +879,9 @@ class TprotGtveInfProt(models.AbstractModel): string="Data e hora de processamento", xsd_required=True, xsd_type="TDateTimeUTC", - help=("Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD."), + help=( + "Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD." + ), ) cte40_nProt = fields.Char( @@ -856,7 +897,9 @@ class TprotGtveInfProt(models.AbstractModel): ), ) - cte40_cStat = fields.Char(string="Código do status da GTV-e", xsd_required=True) + cte40_cStat = fields.Char( + string="Código do status da GTV-e", xsd_required=True + ) cte40_xMotivo = fields.Char( string="Descrição literal do status da GTV-e", @@ -869,6 +912,7 @@ class TprotGtveInfProt(models.AbstractModel): class TprotGtveInfFisco(models.AbstractModel): "Mensagem do Fisco" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprotgtve_inffisco" _inherit = "spec.mixin.cte" @@ -885,6 +929,7 @@ class TprotGtveInfFisco(models.AbstractModel): class TunidCarga(models.AbstractModel): "Tipo Dados Unidade de Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tunidcarga" _inherit = "spec.mixin.cte" @@ -894,13 +939,17 @@ class TunidCarga(models.AbstractModel): comodel_name="cte.40.tcte_infnf", xsd_implicit=True, ondelete="cascade" ) cte40_infUnidCarga_infNFe_id = fields.Many2one( - comodel_name="cte.40.tcte_infnfe", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infnfe", + xsd_implicit=True, + ondelete="cascade", ) cte40_infUnidCarga_infOutros_id = fields.Many2one( comodel_name="cte.40.infoutros", xsd_implicit=True, ondelete="cascade" ) cte40_infUnidCarga_TUnidadeTransp_id = fields.Many2one( - comodel_name="cte.40.tunidadetransp", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tunidadetransp", + xsd_implicit=True, + ondelete="cascade", ) cte40_tpUnidCarga = fields.Selection( TTIPOUNIDCARGA, @@ -938,6 +987,7 @@ class TunidCarga(models.AbstractModel): class LacUnidCarga(models.AbstractModel): "Lacres das Unidades de Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.lacunidcarga" _inherit = "spec.mixin.cte" @@ -974,6 +1024,7 @@ class TcteOs(models.AbstractModel): class TcteOsInfCte(models.AbstractModel): "Informações do CT-e do tipo GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infcte" _inherit = "spec.mixin.cte" @@ -988,7 +1039,9 @@ class TcteOsInfCte(models.AbstractModel): cte40_compl = fields.Many2one( comodel_name="cte.40.tcteos_compl", string="Dados complementares do CT-e", - help=("Dados complementares do CT-e para fins operacionais ou comerciais"), + help=( + "Dados complementares do CT-e para fins operacionais ou comerciais" + ), ) cte40_emit = fields.Many2one( @@ -1070,6 +1123,7 @@ class TcteOsInfCte(models.AbstractModel): class TcteOsIde(models.AbstractModel): "Identificação da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_ide" _inherit = "spec.mixin.cte" @@ -1127,7 +1181,10 @@ class TcteOsIde(models.AbstractModel): cte40_dhEmi = fields.Char( string="Data e hora de emissão do CT-e OS", xsd_required=True, - help=("Data e hora de emissão do CT-e OS\nFormato AAAA-MM-DDTHH:MM:DD " "TZD"), + help=( + "Data e hora de emissão do CT-e OS\nFormato AAAA-MM-DDTHH:MM:DD " + "TZD" + ), ) cte40_tpImp = fields.Selection( @@ -1145,8 +1202,8 @@ class TcteOsIde(models.AbstractModel): string="Forma de emissão do CT-e", xsd_required=True, help=( - "Forma de emissão do CT-e\nPreencher com:\n1 - Normal;\n 5 - " - "Contingência FSDA;\n7 - Autorização pela SVC-RS;\n 8 - " + "Forma de emissão do CT-e\nPreencher com:\n 1 - Normal;\n 5 - " + "Contingência FSDA;\n 7 - Autorização pela SVC-RS;\n 8 - " "Autorização pela SVC-SP" ), ) @@ -1287,7 +1344,8 @@ class TcteOsIde(models.AbstractModel): string="UF do início da prestação", xsd_type="TUf", help=( - "UF do início da prestação\nInformar 'EX' para operações com o " "exterior." + "UF do início da prestação\nInformar 'EX' para operações com o " + "exterior." ), ) @@ -1334,11 +1392,14 @@ class TcteOsIde(models.AbstractModel): ), ) - cte40_xJust = fields.Char(string="Justificativa da entrada em contingência") + cte40_xJust = fields.Char( + string="Justificativa da entrada em contingência" + ) class InfPercurso(models.AbstractModel): "Informações do Percurso do CT-e Outros Serviços" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infpercurso" _inherit = "spec.mixin.cte" @@ -1361,6 +1422,7 @@ class InfPercurso(models.AbstractModel): class TcteOsCompl(models.AbstractModel): "Dados complementares da GTV-e para fins operacionais ou comerciais" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_compl" _inherit = "spec.mixin.cte" @@ -1418,11 +1480,15 @@ class TcteOsObsCont(models.AbstractModel): _binding_type = "TcteOs.InfCte.Compl.ObsCont" cte40_ObsCont_compl_id = fields.Many2one( - comodel_name="cte.40.tcteos_compl", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_compl", + xsd_implicit=True, + ondelete="cascade", ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TcteOsObsFisco(models.AbstractModel): @@ -1435,15 +1501,20 @@ class TcteOsObsFisco(models.AbstractModel): _binding_type = "TcteOs.InfCte.Compl.ObsFisco" cte40_ObsFisco_compl_id = fields.Many2one( - comodel_name="cte.40.tcteos_compl", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_compl", + xsd_implicit=True, + ondelete="cascade", ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TcteOsEmit(models.AbstractModel): "Identificação do Emitente da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_emit" _inherit = "spec.mixin.cte" @@ -1456,10 +1527,13 @@ class TcteOsEmit(models.AbstractModel): help="CNPJ do emitente\nInformar zeros não significativos", ) - cte40_IE = fields.Char(string="Inscrição Estadual do Emitente", xsd_required=True) + cte40_IE = fields.Char( + string="Inscrição Estadual do Emitente", xsd_required=True + ) cte40_IEST = fields.Char( - string="Inscrição Estadual", help="Inscrição Estadual do Substituto Tributário" + string="Inscrição Estadual", + help="Inscrição Estadual do Substituto Tributário", ) cte40_xNome = fields.Char( @@ -1548,6 +1622,7 @@ class TcteOsToma(models.AbstractModel): class TcteOsVPrest(models.AbstractModel): "Valores da Prestação de Serviço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_vprest" _inherit = "spec.mixin.cte" @@ -1580,13 +1655,16 @@ class TcteOsVPrest(models.AbstractModel): class TcteOsVPrestComp(models.AbstractModel): "Componentes do Valor da Prestação" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_vprest_comp" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.VPrest.Comp" cte40_Comp_vPrest_id = fields.Many2one( - comodel_name="cte.40.tcteos_vprest", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_vprest", + xsd_implicit=True, + ondelete="cascade", ) cte40_xNome = fields.Char( string="Nome do componente", @@ -1607,6 +1685,7 @@ class TcteOsVPrestComp(models.AbstractModel): class TcteOsImp(models.AbstractModel): "Informações relativas aos Impostos" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_imp" _inherit = "spec.mixin.cte" @@ -1655,11 +1734,15 @@ class InfTribFed(models.AbstractModel): _binding_type = "TcteOs.InfCte.Imp.InfTribFed" cte40_vPIS = fields.Monetary( - string="Valor do PIS", xsd_type="TDec_1302", currency_field="brl_currency_id" + string="Valor do PIS", + xsd_type="TDec_1302", + currency_field="brl_currency_id", ) cte40_vCOFINS = fields.Monetary( - string="Valor COFINS", xsd_type="TDec_1302", currency_field="brl_currency_id" + string="Valor COFINS", + xsd_type="TDec_1302", + currency_field="brl_currency_id", ) cte40_vIR = fields.Monetary( @@ -1669,11 +1752,15 @@ class InfTribFed(models.AbstractModel): ) cte40_vINSS = fields.Monetary( - string="Valor do INSS", xsd_type="TDec_1302", currency_field="brl_currency_id" + string="Valor do INSS", + xsd_type="TDec_1302", + currency_field="brl_currency_id", ) cte40_vCSLL = fields.Monetary( - string="Valor do CSLL", xsd_type="TDec_1302", currency_field="brl_currency_id" + string="Valor do CSLL", + xsd_type="TDec_1302", + currency_field="brl_currency_id", ) @@ -1687,7 +1774,9 @@ class TcteOsAutXml(models.AbstractModel): _binding_type = "TcteOs.InfCte.AutXml" cte40_autXML_infCte_id = fields.Many2one( - comodel_name="cte.40.tcteos_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_CNPJ = fields.Char( string="CNPJ do autorizado", @@ -1708,6 +1797,7 @@ class TcteOsAutXml(models.AbstractModel): class TcteOsInfCteNorm(models.AbstractModel): "Grupo de informações do CT-e Normal e Substituto" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctenorm" _inherit = "spec.mixin.cte" @@ -1745,7 +1835,8 @@ class TcteOsInfCteNorm(models.AbstractModel): cte40_refCTeCanc = fields.Char( string="Chave de acesso do CT-e Cancelado", help=( - "Chave de acesso do CT-e Cancelado\nSomente para Transporte de " "Valores" + "Chave de acesso do CT-e Cancelado\nSomente para Transporte de " + "Valores" ), ) @@ -1763,6 +1854,7 @@ class TcteOsInfCteNorm(models.AbstractModel): class InfServico(models.AbstractModel): "Informações da Prestação do Serviço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infservico" _inherit = "spec.mixin.cte" @@ -1811,13 +1903,16 @@ class TcteOsInfQ(models.AbstractModel): class InfDocRef(models.AbstractModel): "Informações dos documentos referenciados" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infdocref" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteNorm.InfDocRef" cte40_infDocRef_infCTeNorm_id = fields.Many2one( - comodel_name="cte.40.tcteos_infctenorm", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infctenorm", + xsd_implicit=True, + ondelete="cascade", ) cte40_nDoc = fields.Char( string="Número", choice="infdocref", xsd_choice_required=True @@ -1857,13 +1952,16 @@ class InfDocRef(models.AbstractModel): class TcteOsSeg(models.AbstractModel): "Informações de Seguro da Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_seg" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteNorm.Seg" cte40_seg_infCTeNorm_id = fields.Many2one( - comodel_name="cte.40.tcteos_infctenorm", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infctenorm", + xsd_implicit=True, + ondelete="cascade", ) cte40_respSeg = fields.Selection( SEG_RESPSEG, @@ -1885,6 +1983,7 @@ class TcteOsSeg(models.AbstractModel): class TcteOsInfModal(models.AbstractModel): "Informações do modal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infmodal" _inherit = "spec.mixin.cte" @@ -1899,6 +1998,7 @@ class TcteOsInfModal(models.AbstractModel): class TcteOsInfCteSub(models.AbstractModel): "Informações do CT-e de substituição" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctesub" _inherit = "spec.mixin.cte" @@ -1913,6 +2013,7 @@ class TcteOsInfCteSub(models.AbstractModel): class TcteOsCobr(models.AbstractModel): "Dados da cobrança do CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_cobr" _inherit = "spec.mixin.cte" @@ -1929,6 +2030,7 @@ class TcteOsCobr(models.AbstractModel): class TcteOsFat(models.AbstractModel): "Dados da fatura" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_fat" _inherit = "spec.mixin.cte" @@ -1957,13 +2059,16 @@ class TcteOsFat(models.AbstractModel): class TcteOsDup(models.AbstractModel): "Dados das duplicatas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_dup" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteNorm.Cobr.Dup" cte40_dup_cobr_id = fields.Many2one( - comodel_name="cte.40.tcteos_cobr", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_cobr", + xsd_implicit=True, + ondelete="cascade", ) cte40_nDup = fields.Char(string="Número da duplicata") @@ -1982,15 +2087,20 @@ class TcteOsDup(models.AbstractModel): class InfGtve(models.AbstractModel): "Informações das GTV-e relacionadas ao CT-e OS" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infgtve" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteNorm.InfGtve" cte40_infGTVe_infCTeNorm_id = fields.Many2one( - comodel_name="cte.40.tcteos_infctenorm", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infctenorm", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_chCTe = fields.Char( + string="Chave de acesso da GTV-e", xsd_required=True ) - cte40_chCTe = fields.Char(string="Chave de acesso da GTV-e", xsd_required=True) cte40_comp = fields.One2many( "cte.40.tcteos_infctenorm_comp", @@ -2001,6 +2111,7 @@ class InfGtve(models.AbstractModel): class TcteOsInfCteNormComp(models.AbstractModel): "Componentes do Valor da Prestação" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctenorm_comp" _inherit = "spec.mixin.cte" @@ -2037,21 +2148,27 @@ class TcteOsInfCteNormComp(models.AbstractModel): class TcteOsInfCteComp(models.AbstractModel): "Detalhamento do CT-e complementado" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctecomp" _inherit = "spec.mixin.cte" _binding_type = "TcteOs.InfCte.InfCteComp" cte40_infCteComp_infCte_id = fields.Many2one( - comodel_name="cte.40.tcteos_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcteos_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_chCTe = fields.Char( - string="Chave do CT-e complementado", xsd_required=True, xsd_type="TChDFe" + string="Chave do CT-e complementado", + xsd_required=True, + xsd_type="TChDFe", ) class TcteOsInfCteSupl(models.AbstractModel): "Informações suplementares da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_infctesupl" _inherit = "spec.mixin.cte" @@ -2064,6 +2181,7 @@ class TcteOsInfCteSupl(models.AbstractModel): class Tgtve(models.AbstractModel): "Tipo Guia de Transporte de Valores Eletrônica (Modelo 64)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve" _inherit = "spec.mixin.cte" @@ -2085,6 +2203,7 @@ class Tgtve(models.AbstractModel): class TgtveInfCte(models.AbstractModel): "Informações do CT-e do tipo GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_infcte" _inherit = "spec.mixin.cte" @@ -2099,7 +2218,10 @@ class TgtveInfCte(models.AbstractModel): cte40_compl = fields.Many2one( comodel_name="cte.40.tgtve_compl", string="Dados complementares da GTV-e", - help=("Dados complementares da GTV-e para fins operacionais ou " "comerciais"), + help=( + "Dados complementares da GTV-e para fins operacionais ou " + "comerciais" + ), ) cte40_emit = fields.Many2one( @@ -2185,6 +2307,7 @@ class TgtveInfCte(models.AbstractModel): class TgtveIde(models.AbstractModel): "Identificação da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_ide" _inherit = "spec.mixin.cte" @@ -2195,7 +2318,9 @@ class TgtveIde(models.AbstractModel): string="Código da UF do emitente da GTV-e", xsd_required=True, xsd_type="TCodUfIBGE", - help=("Código da UF do emitente da GTV-e.\nUtilizar a Tabela do IBGE."), + help=( + "Código da UF do emitente da GTV-e.\nUtilizar a Tabela do IBGE." + ), ) cte40_cCT = fields.Char( @@ -2233,12 +2358,16 @@ class TgtveIde(models.AbstractModel): help="Série da GTV-e\nPreencher com '0' no caso de série única", ) - cte40_nCT = fields.Char(string="Número da GTV-e", xsd_required=True, xsd_type="TNF") + cte40_nCT = fields.Char( + string="Número da GTV-e", xsd_required=True, xsd_type="TNF" + ) cte40_dhEmi = fields.Char( string="Data e hora de emissão da GTV-e", xsd_required=True, - help=("Data e hora de emissão da GTV-e\nFormato AAAA-MM-DDTHH:MM:DD TZD"), + help=( + "Data e hora de emissão da GTV-e\nFormato AAAA-MM-DDTHH:MM:DD TZD" + ), ) cte40_tpImp = fields.Selection( @@ -2357,13 +2486,18 @@ class TgtveIde(models.AbstractModel): cte40_dhSaidaOrig = fields.Char( string="Data e hora de saida da origem", xsd_required=True, - help=("Data e hora de saida da origem\nFormato AAAA-MM-DDTHH:MM:DD TZD"), + help=( + "Data e hora de saida da origem\nFormato AAAA-MM-DDTHH:MM:DD TZD" + ), ) cte40_dhChegadaDest = fields.Char( string="Data e hora de chegada no destino", xsd_required=True, - help=("Data e hora de chegada no destino\nFormato AAAA-MM-DDTHH:MM:DD " "TZD"), + help=( + "Data e hora de chegada no destino\nFormato AAAA-MM-DDTHH:MM:DD " + "TZD" + ), ) cte40_toma = fields.Many2one( @@ -2391,7 +2525,9 @@ class TgtveIde(models.AbstractModel): ), ) - cte40_xJust = fields.Char(string="Justificativa da entrada em contingência") + cte40_xJust = fields.Char( + string="Justificativa da entrada em contingência" + ) class TgtveToma(models.AbstractModel): @@ -2481,6 +2617,7 @@ class TomaTerceiro(models.AbstractModel): class TgtveCompl(models.AbstractModel): "Dados complementares da GTV-e para fins operacionais ou comerciais" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_compl" _inherit = "spec.mixin.cte" @@ -2538,11 +2675,15 @@ class TgtveObsCont(models.AbstractModel): _binding_type = "Tgtve.InfCte.Compl.ObsCont" cte40_ObsCont_compl_id = fields.Many2one( - comodel_name="cte.40.tgtve_compl", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tgtve_compl", + xsd_implicit=True, + ondelete="cascade", ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TgtveObsFisco(models.AbstractModel): @@ -2555,15 +2696,20 @@ class TgtveObsFisco(models.AbstractModel): _binding_type = "Tgtve.InfCte.Compl.ObsFisco" cte40_ObsFisco_compl_id = fields.Many2one( - comodel_name="cte.40.tgtve_compl", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tgtve_compl", + xsd_implicit=True, + ondelete="cascade", ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TgtveEmit(models.AbstractModel): "Identificação do Emitente da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_emit" _inherit = "spec.mixin.cte" @@ -2576,10 +2722,13 @@ class TgtveEmit(models.AbstractModel): help="CNPJ do emitente\nInformar zeros não significativos", ) - cte40_IE = fields.Char(string="Inscrição Estadual do Emitente", xsd_required=True) + cte40_IE = fields.Char( + string="Inscrição Estadual do Emitente", xsd_required=True + ) cte40_IEST = fields.Char( - string="Inscrição Estadual", help="Inscrição Estadual do Substituto Tributário" + string="Inscrição Estadual", + help="Inscrição Estadual do Substituto Tributário", ) cte40_xNome = fields.Char( @@ -2722,6 +2871,7 @@ class TgtveDest(models.AbstractModel): class DetGtv(models.AbstractModel): "Grupo de informações detalhadas da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.detgtv" _inherit = "spec.mixin.cte" @@ -2748,13 +2898,15 @@ class DetGtv(models.AbstractModel): "cte40_infVeiculo_detGTV_id", string="Grupo de informações", help=( - "Grupo de informações dos veículos utilizados no transporte de " "valores" + "Grupo de informações dos veículos utilizados no transporte de " + "valores" ), ) class TgtveInfEspecie(models.AbstractModel): "Informações das Espécies transportadas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_infespecie" _inherit = "spec.mixin.cte" @@ -2767,7 +2919,9 @@ class TgtveInfEspecie(models.AbstractModel): INFESPECIE_TPESPECIE, string="Tipo da Espécie", xsd_required=True, - help=("Tipo da Espécie\n1 - Cédula\n2 - Cheque\n3 - Moeda\n4 - Outros"), + help=( + "Tipo da Espécie\n1 - Cédula\n2 - Cheque\n3 - Moeda\n4 - Outros" + ), ) cte40_vEspecie = fields.Monetary( @@ -2831,7 +2985,9 @@ class TgtveAutXml(models.AbstractModel): _binding_type = "Tgtve.InfCte.AutXml" cte40_autXML_infCte_id = fields.Many2one( - comodel_name="cte.40.tgtve_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tgtve_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_CNPJ = fields.Char( string="CNPJ do autorizado", @@ -2852,6 +3008,7 @@ class TgtveAutXml(models.AbstractModel): class TgtveInfCteSupl(models.AbstractModel): "Informações suplementares da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tgtve_infctesupl" _inherit = "spec.mixin.cte" @@ -2864,6 +3021,7 @@ class TgtveInfCteSupl(models.AbstractModel): class TretCte(models.AbstractModel): "Tipo Retorno do Pedido de Autorização de CT-e (Modelo 57)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tretcte" _inherit = "spec.mixin.cte" @@ -2910,11 +3068,14 @@ class TretCte(models.AbstractModel): xsd_type="TProtCTe", ) - cte40_versao = fields.Char(string="versao", xsd_required=True, xsd_type="TVerCTe") + cte40_versao = fields.Char( + string="versao", xsd_required=True, xsd_type="TVerCTe" + ) class TretCteOs(models.AbstractModel): "Tipo Retorno do Pedido de Autorização de CT-e OS (Modelo 67)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tretcteos" _inherit = "spec.mixin.cte" @@ -2961,11 +3122,14 @@ class TretCteOs(models.AbstractModel): xsd_type="TProtCTeOS", ) - cte40_versao = fields.Char(string="versao", xsd_required=True, xsd_type="TVerCTe") + cte40_versao = fields.Char( + string="versao", xsd_required=True, xsd_type="TVerCTe" + ) class TretGtve(models.AbstractModel): "Tipo Retorno do Pedido de Autorização de GTV-e (Modelo 64)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tretgtve" _inherit = "spec.mixin.cte" @@ -3012,11 +3176,14 @@ class TretGtve(models.AbstractModel): xsd_type="TProtGTVe", ) - cte40_versao = fields.Char(string="versao", xsd_required=True, xsd_type="TVerCTe") + cte40_versao = fields.Char( + string="versao", xsd_required=True, xsd_type="TVerCTe" + ) class TunidadeTransp(models.AbstractModel): "Tipo Dados Unidade de Transporte" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tunidadetransp" _inherit = "spec.mixin.cte" @@ -3026,7 +3193,9 @@ class TunidadeTransp(models.AbstractModel): comodel_name="cte.40.tcte_infnf", xsd_implicit=True, ondelete="cascade" ) cte40_infUnidTransp_infNFe_id = fields.Many2one( - comodel_name="cte.40.tcte_infnfe", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infnfe", + xsd_implicit=True, + ondelete="cascade", ) cte40_infUnidTransp_infOutros_id = fields.Many2one( comodel_name="cte.40.infoutros", xsd_implicit=True, ondelete="cascade" @@ -3084,19 +3253,23 @@ class TunidadeTransp(models.AbstractModel): class LacUnidTransp(models.AbstractModel): "Lacres das Unidades de Transporte" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.lacunidtransp" _inherit = "spec.mixin.cte" _binding_type = "TunidadeTransp.LacUnidTransp" cte40_lacUnidTransp_TUnidadeTransp_id = fields.Many2one( - comodel_name="cte.40.tunidadetransp", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tunidadetransp", + xsd_implicit=True, + ondelete="cascade", ) cte40_nLacre = fields.Char(string="Número do lacre", xsd_required=True) class Tcte(models.AbstractModel): "Tipo Conhecimento de Transporte Eletrônico (Modelo 57)" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte" _inherit = "spec.mixin.cte" @@ -3116,6 +3289,7 @@ class Tcte(models.AbstractModel): class TcteInfCte(models.AbstractModel): "Informações do CT-e do tipo GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infcte" _inherit = "spec.mixin.cte" @@ -3130,7 +3304,9 @@ class TcteInfCte(models.AbstractModel): cte40_compl = fields.Many2one( comodel_name="cte.40.tcte_compl", string="Dados complementares do CT-e", - help=("Dados complementares do CT-e para fins operacionais ou comerciais"), + help=( + "Dados complementares do CT-e para fins operacionais ou comerciais" + ), ) cte40_emit = fields.Many2one( @@ -3216,7 +3392,9 @@ class TcteInfCte(models.AbstractModel): cte40_infSolicNFF = fields.Many2one( comodel_name="cte.40.tcte_infsolicnff", string="Grupo de informações do pedido", - help=("Grupo de informações do pedido de emissão da Nota Fiscal Fácil"), + help=( + "Grupo de informações do pedido de emissão da Nota Fiscal Fácil" + ), ) cte40_infPAA = fields.Many2one( @@ -3243,6 +3421,7 @@ class TcteInfCte(models.AbstractModel): class TcteIde(models.AbstractModel): "Identificação da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_ide" _inherit = "spec.mixin.cte" @@ -3292,12 +3471,16 @@ class TcteIde(models.AbstractModel): help="Série do CT-e\nPreencher com '0' no caso de série única", ) - cte40_nCT = fields.Char(string="Número do CT-e", xsd_required=True, xsd_type="TNF") + cte40_nCT = fields.Char( + string="Número do CT-e", xsd_required=True, xsd_type="TNF" + ) cte40_dhEmi = fields.Char( string="Data e hora de emissão do CT-e", xsd_required=True, - help=("Data e hora de emissão do CT-e\nFormato AAAA-MM-DDTHH:MM:DD TZD"), + help=( + "Data e hora de emissão do CT-e\nFormato AAAA-MM-DDTHH:MM:DD TZD" + ), ) cte40_tpImp = fields.Selection( @@ -3337,7 +3520,9 @@ class TcteIde(models.AbstractModel): string="Tipo do Ambiente", xsd_required=True, xsd_type="TAmb", - help=("Tipo do Ambiente\nPreencher com:1 - Produção; 2 - Homologação."), + help=( + "Tipo do Ambiente\nPreencher com:1 - Produção; 2 - Homologação." + ), ) cte40_tpCTe = fields.Selection( @@ -3460,7 +3645,8 @@ class TcteIde(models.AbstractModel): xsd_required=True, xsd_type="TUf", help=( - "UF do início da prestação\nInformar 'EX' para operações com o " "exterior." + "UF do início da prestação\nInformar 'EX' para operações com o " + "exterior." ), ) @@ -3543,7 +3729,9 @@ class TcteIde(models.AbstractModel): ), ) - cte40_xJust = fields.Char(string="Justificativa da entrada em contingência") + cte40_xJust = fields.Char( + string="Justificativa da entrada em contingência" + ) class Toma3(models.AbstractModel): @@ -3637,6 +3825,7 @@ class TcteToma4(models.AbstractModel): class TcteCompl(models.AbstractModel): "Dados complementares da GTV-e para fins operacionais ou comerciais" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_compl" _inherit = "spec.mixin.cte" @@ -3731,7 +3920,9 @@ class Fluxo(models.AbstractModel): ), ) - cte40_pass = fields.One2many("cte.40.pass", "cte40_pass_fluxo_id", string="pass") + cte40_pass = fields.One2many( + "cte.40.pass", "cte40_pass_fluxo_id", string="pass" + ) cte40_xDest = fields.Char( string="Sigla ou código interno (xDest)", @@ -3773,6 +3964,7 @@ class Pass(models.AbstractModel): class Entrega(models.AbstractModel): "Informações ref. a previsão de entrega" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.entrega" _inherit = "spec.mixin.cte" @@ -3784,7 +3976,8 @@ class Entrega(models.AbstractModel): choice="entrega", xsd_choice_required=True, help=( - "Entrega sem data definida\nEsta opção é proibida para o modal " "aéreo." + "Entrega sem data definida\nEsta opção é proibida para o modal " + "aéreo." ), ) @@ -3837,12 +4030,16 @@ class SemData(models.AbstractModel): SEMDATA_TPPER, string="Tipo de data/período programado", xsd_required=True, - help=("Tipo de data/período programado para entrega\n0- Sem data " "definida"), + help=( + "Tipo de data/período programado para entrega\n0- Sem data " + "definida" + ), ) class ComData(models.AbstractModel): "Entrega com data definida" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.comdata" _inherit = "spec.mixin.cte" @@ -3870,6 +4067,7 @@ class ComData(models.AbstractModel): class NoPeriodo(models.AbstractModel): "Entrega no período definido" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.noperiodo" _inherit = "spec.mixin.cte" @@ -3899,6 +4097,7 @@ class NoPeriodo(models.AbstractModel): class SemHora(models.AbstractModel): "Entrega sem hora definida" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.semhora" _inherit = "spec.mixin.cte" @@ -3914,6 +4113,7 @@ class SemHora(models.AbstractModel): class ComHora(models.AbstractModel): "Entrega com hora definida" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.comhora" _inherit = "spec.mixin.cte" @@ -3942,6 +4142,7 @@ class ComHora(models.AbstractModel): class NoInter(models.AbstractModel): "Entrega no intervalo de horário definido" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.nointer" _inherit = "spec.mixin.cte" @@ -3983,7 +4184,9 @@ class TcteObsCont(models.AbstractModel): ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TcteObsFisco(models.AbstractModel): @@ -4000,11 +4203,14 @@ class TcteObsFisco(models.AbstractModel): ) cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) - cte40_xCampo = fields.Char(string="Identificação do campo", xsd_required=True) + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) class TcteEmit(models.AbstractModel): "Identificação do Emitente da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_emit" _inherit = "spec.mixin.cte" @@ -4037,7 +4243,8 @@ class TcteEmit(models.AbstractModel): ) cte40_IEST = fields.Char( - string="Inscrição Estadual", help="Inscrição Estadual do Substituto Tributário" + string="Inscrição Estadual", + help="Inscrição Estadual do Substituto Tributário", ) cte40_xNome = fields.Char( @@ -4128,6 +4335,7 @@ class TcteRem(models.AbstractModel): class Exped(models.AbstractModel): "Informações do Expedidor da Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.exped" _inherit = "spec.mixin.cte" @@ -4180,6 +4388,7 @@ class Exped(models.AbstractModel): class Receb(models.AbstractModel): "Informações do Recebedor da Carga" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.receb" _inherit = "spec.mixin.cte" @@ -4297,6 +4506,7 @@ class TcteDest(models.AbstractModel): class TcteVPrest(models.AbstractModel): "Valores da Prestação de Serviço" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_vprest" _inherit = "spec.mixin.cte" @@ -4329,13 +4539,16 @@ class TcteVPrest(models.AbstractModel): class TcteVPrestComp(models.AbstractModel): "Componentes do Valor da Prestação" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_vprest_comp" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.VPrest.Comp" cte40_Comp_vPrest_id = fields.Many2one( - comodel_name="cte.40.tcte_vprest", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_vprest", + xsd_implicit=True, + ondelete="cascade", ) cte40_xNome = fields.Char( string="Nome do componente", @@ -4356,6 +4569,7 @@ class TcteVPrestComp(models.AbstractModel): class TcteImp(models.AbstractModel): "Informações relativas aos Impostos" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_imp" _inherit = "spec.mixin.cte" @@ -4393,7 +4607,9 @@ class TcteAutXml(models.AbstractModel): _binding_type = "Tcte.InfCte.AutXml" cte40_autXML_infCte_id = fields.Many2one( - comodel_name="cte.40.tcte_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_CNPJ = fields.Char( string="CNPJ do autorizado", @@ -4414,6 +4630,7 @@ class TcteAutXml(models.AbstractModel): class TcteInfSolicNff(models.AbstractModel): "Grupo de informações do pedido de emissão da Nota Fiscal Fácil" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infsolicnff" _inherit = "spec.mixin.cte" @@ -4432,6 +4649,7 @@ class TcteInfSolicNff(models.AbstractModel): class TcteInfPaa(models.AbstractModel): "Grupo de Informação do Provedor de Assinatura e Autorização" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infpaa" _inherit = "spec.mixin.cte" @@ -4454,6 +4672,7 @@ class TcteInfPaa(models.AbstractModel): class TctePaasignature(models.AbstractModel): "Assinatura RSA do Emitente para DFe gerados por PAA" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_paasignature" _inherit = "spec.mixin.cte" @@ -4480,6 +4699,7 @@ class TctePaasignature(models.AbstractModel): class TcteInfCteNorm(models.AbstractModel): "Grupo de informações do CT-e Normal e Substituto" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infctenorm" _inherit = "spec.mixin.cte" @@ -4504,7 +4724,8 @@ class TcteInfCteNorm(models.AbstractModel): ) cte40_docAnt = fields.Many2one( - comodel_name="cte.40.docant", string="Documentos de Transporte Anterior" + comodel_name="cte.40.docant", + string="Documentos de Transporte Anterior", ) cte40_infModal = fields.Many2one( @@ -4529,7 +4750,8 @@ class TcteInfCteNorm(models.AbstractModel): ) cte40_infGlobalizado = fields.Many2one( - comodel_name="cte.40.infglobalizado", string="Informações do CT-e Globalizado" + comodel_name="cte.40.infglobalizado", + string="Informações do CT-e Globalizado", ) cte40_infServVinc = fields.Many2one( @@ -4541,6 +4763,7 @@ class TcteInfCteNorm(models.AbstractModel): class InfCarga(models.AbstractModel): "Informações da Carga do CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infcarga" _inherit = "spec.mixin.cte" @@ -4559,7 +4782,10 @@ class InfCarga(models.AbstractModel): cte40_proPred = fields.Char( string="Produto predominante", xsd_required=True, - help=("Produto predominante\nInformar a descrição do produto " "predominante"), + help=( + "Produto predominante\nInformar a descrição do produto " + "predominante" + ), ) cte40_xOutCat = fields.Char( @@ -4702,7 +4928,9 @@ class TcteInfNf(models.AbstractModel): _binding_type = "Tcte.InfCte.InfCteNorm.InfDoc.InfNf" cte40_infNF_infDoc_id = fields.Many2one( - comodel_name="cte.40.tcte_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_nRoma = fields.Char(string="Número do Romaneio da NF") @@ -4835,13 +5063,16 @@ class TcteInfNf(models.AbstractModel): class TcteInfNfe(models.AbstractModel): "Informações das NF-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infnfe" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.InfDoc.InfNfe" cte40_infNFe_infDoc_id = fields.Many2one( - comodel_name="cte.40.tcte_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_chave = fields.Char( string="Chave de acesso da NF-e", xsd_required=True, xsd_type="TChDFe" @@ -4891,13 +5122,16 @@ class TcteInfNfe(models.AbstractModel): class InfOutros(models.AbstractModel): "Informações dos demais documentos" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infoutros" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.InfDoc.InfOutros" cte40_infOutros_infDoc_id = fields.Many2one( - comodel_name="cte.40.tcte_infdoc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infdoc", + xsd_implicit=True, + ondelete="cascade", ) cte40_tpDoc = fields.Selection( INFOUTROS_TPDOC, @@ -4967,6 +5201,7 @@ class InfOutros(models.AbstractModel): class DocAnt(models.AbstractModel): "Documentos de Transporte Anterior" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.docant" _inherit = "spec.mixin.cte" @@ -4981,6 +5216,7 @@ class DocAnt(models.AbstractModel): class EmiDocAnt(models.AbstractModel): "Emissor do documento anterior" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.emidocant" _inherit = "spec.mixin.cte" @@ -5027,12 +5263,16 @@ class EmiDocAnt(models.AbstractModel): "cte.40.iddocant", "cte40_idDocAnt_emiDocAnt_id", string="Informações de identificação", - help=("Informações de identificação dos documentos de Transporte " "Anterior"), + help=( + "Informações de identificação dos documentos de Transporte " + "Anterior" + ), ) class IdDocAnt(models.AbstractModel): "Informações de identificação dos documentos de Transporte Anterior" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.iddocant" _inherit = "spec.mixin.cte" @@ -5062,10 +5302,13 @@ class IdDocAnt(models.AbstractModel): class IdDocAntPap(models.AbstractModel): "Documentos de transporte anterior em papel" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.iddocantpap" _inherit = "spec.mixin.cte" - _binding_type = "Tcte.InfCte.InfCteNorm.DocAnt.EmiDocAnt.IdDocAnt.IdDocAntPap" + _binding_type = ( + "Tcte.InfCte.InfCteNorm.DocAnt.EmiDocAnt.IdDocAnt.IdDocAntPap" + ) cte40_idDocAntPap_idDocAnt_id = fields.Many2one( comodel_name="cte.40.iddocant", xsd_implicit=True, ondelete="cascade" @@ -5084,23 +5327,32 @@ class IdDocAntPap(models.AbstractModel): ), ) - cte40_serie = fields.Char(string="Série do Documento Fiscal", xsd_required=True) + cte40_serie = fields.Char( + string="Série do Documento Fiscal", xsd_required=True + ) cte40_subser = fields.Char(string="Série do Documento Fiscal (subser)") - cte40_nDoc = fields.Char(string="Número do Documento Fiscal", xsd_required=True) + cte40_nDoc = fields.Char( + string="Número do Documento Fiscal", xsd_required=True + ) cte40_dEmi = fields.Date( - string="Data de emissão (AAAA-MM-DD)", xsd_required=True, xsd_type="TData" + string="Data de emissão (AAAA-MM-DD)", + xsd_required=True, + xsd_type="TData", ) class IdDocAntEle(models.AbstractModel): "Documentos de transporte anterior eletrônicos" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.iddocantele" _inherit = "spec.mixin.cte" - _binding_type = "Tcte.InfCte.InfCteNorm.DocAnt.EmiDocAnt.IdDocAnt.IdDocAntEle" + _binding_type = ( + "Tcte.InfCte.InfCteNorm.DocAnt.EmiDocAnt.IdDocAnt.IdDocAntEle" + ) cte40_idDocAntEle_idDocAnt_id = fields.Many2one( comodel_name="cte.40.iddocant", xsd_implicit=True, ondelete="cascade" @@ -5112,6 +5364,7 @@ class IdDocAntEle(models.AbstractModel): class TcteInfModal(models.AbstractModel): "Informações do modal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infmodal" _inherit = "spec.mixin.cte" @@ -5126,13 +5379,16 @@ class TcteInfModal(models.AbstractModel): class VeicNovos(models.AbstractModel): "informações dos veículos transportados" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.veicnovos" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.VeicNovos" cte40_veicNovos_infCTeNorm_id = fields.Many2one( - comodel_name="cte.40.tcte_infctenorm", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infctenorm", + xsd_implicit=True, + ondelete="cascade", ) cte40_chassi = fields.Char(string="Chassi do veículo", xsd_required=True) @@ -5167,6 +5423,7 @@ class VeicNovos(models.AbstractModel): class TcteCobr(models.AbstractModel): "Dados da cobrança do CT-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_cobr" _inherit = "spec.mixin.cte" @@ -5183,6 +5440,7 @@ class TcteCobr(models.AbstractModel): class TcteFat(models.AbstractModel): "Dados da fatura" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_fat" _inherit = "spec.mixin.cte" @@ -5211,6 +5469,7 @@ class TcteFat(models.AbstractModel): class TcteDup(models.AbstractModel): "Dados das duplicatas" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_dup" _inherit = "spec.mixin.cte" @@ -5236,6 +5495,7 @@ class TcteDup(models.AbstractModel): class TcteInfCteSub(models.AbstractModel): "Informações do CT-e de substituição" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infctesub" _inherit = "spec.mixin.cte" @@ -5248,12 +5508,14 @@ class TcteInfCteSub(models.AbstractModel): ) cte40_indAlteraToma = fields.Selection( - INFCTESUB_INDALTERATOMA, string="Indicador de CT-e Alteração de Tomador" + INFCTESUB_INDALTERATOMA, + string="Indicador de CT-e Alteração de Tomador", ) class InfGlobalizado(models.AbstractModel): "Informações do CT-e Globalizado" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infglobalizado" _inherit = "spec.mixin.cte" @@ -5268,6 +5530,7 @@ class InfGlobalizado(models.AbstractModel): class InfServVinc(models.AbstractModel): "Informações do Serviço Vinculado a Multimodal" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infservvinc" _inherit = "spec.mixin.cte" @@ -5282,13 +5545,16 @@ class InfServVinc(models.AbstractModel): class InfCteMultimodal(models.AbstractModel): "informações do CT-e multimodal vinculado" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.infctemultimodal" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.InfServVinc.InfCteMultimodal" cte40_infCTeMultimodal_infServVinc_id = fields.Many2one( - comodel_name="cte.40.infservvinc", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.infservvinc", + xsd_implicit=True, + ondelete="cascade", ) cte40_chCTeMultimodal = fields.Char( string="Chave de acesso do CT-e Multimodal", @@ -5299,21 +5565,27 @@ class InfCteMultimodal(models.AbstractModel): class TcteInfCteComp(models.AbstractModel): "Detalhamento do CT-e complementado" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infctecomp" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteComp" cte40_infCteComp_infCte_id = fields.Many2one( - comodel_name="cte.40.tcte_infcte", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infcte", + xsd_implicit=True, + ondelete="cascade", ) cte40_chCTe = fields.Char( - string="Chave do CT-e complementado", xsd_required=True, xsd_type="TChDFe" + string="Chave do CT-e complementado", + xsd_required=True, + xsd_type="TChDFe", ) class TcteInfCteSupl(models.AbstractModel): "Informações suplementares da GTV-e" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_infctesupl" _inherit = "spec.mixin.cte" diff --git a/l10n_br_cte_spec/models/v4_0/evento_cte_tipos_basico_v4_00.py b/l10n_br_cte_spec/models/v4_0/evento_cte_tipos_basico_v4_00.py index f79fa41e25f7..c9279805e240 100644 --- a/l10n_br_cte_spec/models/v4_0/evento_cte_tipos_basico_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/evento_cte_tipos_basico_v4_00.py @@ -25,13 +25,16 @@ class Tevento(models.AbstractModel): "Tipo Evento" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tevento" _inherit = "spec.mixin.cte" _binding_type = "Tevento" cte40_infEvento = fields.Many2one( - comodel_name="cte.40.tevento_infevento", string="infEvento", xsd_required=True + comodel_name="cte.40.tevento_infevento", + string="infEvento", + xsd_required=True, ) cte40_versao = fields.Char( @@ -142,6 +145,7 @@ class TeventoInfEvento(models.AbstractModel): class DetEvento(models.AbstractModel): "Detalhamento do evento específico" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.detevento" _inherit = "spec.mixin.cte" @@ -172,6 +176,7 @@ class TeventoInfSolicNff(models.AbstractModel): class TeventoInfPaa(models.AbstractModel): "Grupo de Informação do Provedor de Assinatura e Autorização" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tevento_infpaa" _inherit = "spec.mixin.cte" @@ -194,6 +199,7 @@ class TeventoInfPaa(models.AbstractModel): class TeventoPaasignature(models.AbstractModel): "Assinatura RSA do Emitente para DFe gerados por PAA" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tevento_paasignature" _inherit = "spec.mixin.cte" @@ -220,6 +226,7 @@ class TeventoPaasignature(models.AbstractModel): class TretEvento(models.AbstractModel): "Tipo retorno do Evento" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tretevento" _inherit = "spec.mixin.cte" @@ -309,6 +316,7 @@ class TretEventoInfEvento(models.AbstractModel): class TprocEvento(models.AbstractModel): "Tipo procEvento" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tprocevento" _inherit = "spec.mixin.cte" @@ -335,7 +343,9 @@ class TprocEvento(models.AbstractModel): cte40_ipTransmissor = fields.Char( string="IP do transmissor", xsd_type="TIPv4", - help=("IP do transmissor do documento fiscal para o ambiente autorizador"), + help=( + "IP do transmissor do documento fiscal para o ambiente autorizador" + ), ) cte40_nPortaCon = fields.Char( diff --git a/l10n_br_cte_spec/models/v4_0/tipos_geral_cte_v4_00.py b/l10n_br_cte_spec/models/v4_0/tipos_geral_cte_v4_00.py index 57e85909abf1..d54b6327637b 100644 --- a/l10n_br_cte_spec/models/v4_0/tipos_geral_cte_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/tipos_geral_cte_v4_00.py @@ -194,6 +194,7 @@ class TrsakeyValueType(models.AbstractModel): "Tipo que representa uma chave publica padrão RSA" + _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.trsakeyvaluetype" _inherit = "spec.mixin.cte" From b8856a48f739ec0068d88f7ed4df18b16b871e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Sun, 16 Jun 2024 03:55:37 +0000 Subject: [PATCH 02/27] [IMP] l10n_br_cte_spec: NT 2024.002 --- .../models/v4_0/cte_tipos_basico_v4_00.py | 1487 ++++++++++++++++- l10n_br_cte_spec/readme/DESCRIPTION.rst | 8 +- 2 files changed, 1469 insertions(+), 26 deletions(-) diff --git a/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py b/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py index b532fe9630c1..b6efa7c4946e 100644 --- a/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py +++ b/l10n_br_cte_spec/models/v4_0/cte_tipos_basico_v4_00.py @@ -100,6 +100,12 @@ ("3", "3"), ] +"Tipos Finalidade de CT-e Simplificado" +TFINCTESIMP = [ + ("4", "4"), + ("5", "5"), +] + "Tipo Finalidade da GTV-e" TFINGTVE = [ ("4", "4"), @@ -113,6 +119,13 @@ ("04", "04"), ] +"Tipo Modal transporte do CTe Simplificado" +TMODTRANSPSIMP = [ + ("01", "01"), + ("02", "02"), + ("03", "03"), +] + "Tipo processo de emissão do CT-e" TPROCEMI = [ ("0", "0"), @@ -183,6 +196,13 @@ ("1", "1"), ] +"""indica se a prestação é total ou parcial em relação as notas do + documento anterior""" +INFDOCANT_TPPREST = [ + ("1", "Total"), + ("2", "Parcial"), +] + "Tipo da Espécie" INFESPECIE_TPESPECIE = [ ("1", "Numerário"), @@ -216,6 +236,27 @@ ("05", "MMBTU"), ] +"Tipo da Medida" +INFQ_TPMED = [ + ("00", "Cubagem da NF-e"), + ("01", "Cubagem Aferida pelo Transportador"), + ("02", "Peso Bruto da NF-e"), + ("03", "Peso Bruto Aferido pelo Transportador"), + ("04", "Peso Cubado"), + ("05", "Peso Base do Cálculo do Frete"), + ("06", "Peso para uso Operacional"), + ("07", "Caixas"), + ("08", "Paletes"), + ("09", "Sacas"), + ("10", "Containers"), + ("11", "Rolos"), + ("12", "Bombonas"), + ("13", "Latas"), + ("14", "Litragem"), + ("15", "Milhão de BTU (British Thermal Units)"), + ("99", "Outros"), +] + "Tipo de hora" NOINTER_TPHOR = [ ("4", "No intervalo de tempo"), @@ -260,10 +301,17 @@ ("4", "Outros"), ] +"Indicador do papel do tomador na prestação do serviço" +TOMA_INDIETOMA = [ + ("1", "Contribuinte ICMS"), + ("2", "Contribuinte isento de inscrição"), + ("9", "Não Contribuinte"), +] + "Tomador do Serviço" TOMA_TOMA = [ ("0", "Remetente"), - ("1", "Destinatário"), + ("1", "Expedidor"), ] @@ -935,6 +983,11 @@ class TunidCarga(models.AbstractModel): _inherit = "spec.mixin.cte" _binding_type = "TunidCarga" + cte40_infUnidCarga_infNFe_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infnfe", + xsd_implicit=True, + ondelete="cascade", + ) cte40_infUnidCarga_infNF_id = fields.Many2one( comodel_name="cte.40.tcte_infnf", xsd_implicit=True, ondelete="cascade" ) @@ -1065,6 +1118,7 @@ class TcteOsInfCte(models.AbstractModel): comodel_name="cte.40.tcteos_vprest", string="Valores da Prestação de Serviço", xsd_required=True, + xsd_type="TDec_1302", ) cte40_imp = fields.Many2one( @@ -1202,8 +1256,8 @@ class TcteOsIde(models.AbstractModel): string="Forma de emissão do CT-e", xsd_required=True, help=( - "Forma de emissão do CT-e\nPreencher com:\n 1 - Normal;\n 5 - " - "Contingência FSDA;\n 7 - Autorização pela SVC-RS;\n 8 - " + "Forma de emissão do CT-e\nPreencher com:\n1 - Normal;\n 5 - " + "Contingência FSDA;\n7 - Autorização pela SVC-RS;\n 8 - " "Autorização pela SVC-SP" ), ) @@ -1617,11 +1671,12 @@ class TcteOsToma(models.AbstractModel): xsd_type="TEndereco", ) - cte40_email = fields.Char(string="Endereço de email") + cte40_email = fields.Char(string="Endereço de email", xsd_type="TEmail") class TcteOsVPrest(models.AbstractModel): - "Valores da Prestação de Serviço" + """Valorl da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS""" _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcteos_vprest" @@ -3127,6 +3182,61 @@ class TretCteOs(models.AbstractModel): ) +class TretCteSimp(models.AbstractModel): + """Tipo Retorno do Pedido de Autorização de CT-e Simplificado (Modelo + 57)""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tretctesimp" + _inherit = "spec.mixin.cte" + _binding_type = "TretCteSimp" + + cte40_tpAmb = fields.Selection( + TAMB, + string="Identificação do Ambiente", + xsd_required=True, + xsd_type="TAmb", + help="Identificação do Ambiente:\n1 - Produção\n2 - Homologação", + ) + + cte40_cUF = fields.Selection( + TCODUFIBGE, + string="Identificação da UF", + xsd_required=True, + xsd_type="TCodUfIBGE", + ) + + cte40_verAplic = fields.Char( + string="Versão do Aplicativo que processou", + xsd_required=True, + xsd_type="TVerAplic", + help="Versão do Aplicativo que processou a CT-e", + ) + + cte40_cStat = fields.Char( + string="código do status do retorno da consulta", + xsd_required=True, + xsd_type="TStat", + ) + + cte40_xMotivo = fields.Char( + string="Descrição literal do status", + xsd_required=True, + xsd_type="TMotivo", + help="Descrição literal do status do do retorno da consulta.", + ) + + cte40_protCTe = fields.Many2one( + comodel_name="cte.40.tprotcte", + string="Reposta ao processamento do CT-e", + xsd_type="TProtCTe", + ) + + cte40_versao = fields.Char( + string="versao", xsd_required=True, xsd_type="TVerCTe" + ) + + class TretGtve(models.AbstractModel): "Tipo Retorno do Pedido de Autorização de GTV-e (Modelo 64)" @@ -3189,6 +3299,11 @@ class TunidadeTransp(models.AbstractModel): _inherit = "spec.mixin.cte" _binding_type = "TunidadeTransp" + cte40_infUnidTransp_infNFe_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infnfe", + xsd_implicit=True, + ondelete="cascade", + ) cte40_infUnidTransp_infNF_id = fields.Many2one( comodel_name="cte.40.tcte_infnf", xsd_implicit=True, ondelete="cascade" ) @@ -3348,6 +3463,7 @@ class TcteInfCte(models.AbstractModel): comodel_name="cte.40.tcte_vprest", string="Valores da Prestação de Serviço", xsd_required=True, + xsd_type="TDec_1302", ) cte40_imp = fields.Many2one( @@ -3614,9 +3730,9 @@ class TcteIde(models.AbstractModel): string="Tipo do Serviço", xsd_required=True, help=( - "Tipo do Serviço\nPreencher com: \n0 - Normal;1 - " - "Subcontratação;\n2 - Redespacho;3 - Redespacho Intermediário; 4 -" - " Serviço Vinculado a Multimodal" + "Tipo do Serviço\nPreencher com: \n0 - Normal;\n1 - " + "Subcontratação;\n2 - Redespacho;\n3 - Redespacho Intermediário; " + "\n4 - Serviço Vinculado a Multimodal" ), ) @@ -3851,7 +3967,7 @@ class TcteCompl(models.AbstractModel): cte40_xEmi = fields.Char(string="Funcionário emissor do CTe") cte40_fluxo = fields.Many2one( - comodel_name="cte.40.fluxo", + comodel_name="cte.40.tcte_fluxo", string="Previsão do fluxo da carga", help=( "Previsão do fluxo da carga\nPreenchimento obrigatório para o " @@ -3898,12 +4014,12 @@ class TcteCompl(models.AbstractModel): ) -class Fluxo(models.AbstractModel): +class TcteFluxo(models.AbstractModel): """Previsão do fluxo da carga Preenchimento obrigatório para o modal aéreo.""" _description = textwrap.dedent(" %s" % (__doc__,)) - _name = "cte.40.fluxo" + _name = "cte.40.tcte_fluxo" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.Compl.Fluxo" @@ -3921,7 +4037,7 @@ class Fluxo(models.AbstractModel): ) cte40_pass = fields.One2many( - "cte.40.pass", "cte40_pass_fluxo_id", string="pass" + "cte.40.tcte_pass", "cte40_pass_fluxo_id", string="pass" ) cte40_xDest = fields.Char( @@ -3939,14 +4055,14 @@ class Fluxo(models.AbstractModel): cte40_xRota = fields.Char(string="Código da Rota de Entrega") -class Pass(models.AbstractModel): +class TctePass(models.AbstractModel): _description = "pass" - _name = "cte.40.pass" + _name = "cte.40.tcte_pass" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.Compl.Fluxo.Pass" cte40_pass_fluxo_id = fields.Many2one( - comodel_name="cte.40.fluxo", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_fluxo", xsd_implicit=True, ondelete="cascade" ) cte40_xPass = fields.Char( string="Sigla ou código interno", @@ -4505,7 +4621,8 @@ class TcteDest(models.AbstractModel): class TcteVPrest(models.AbstractModel): - "Valores da Prestação de Serviço" + """Valorl da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS""" _description = textwrap.dedent(" %s" % (__doc__,)) _name = "cte.40.tcte_vprest" @@ -4706,7 +4823,7 @@ class TcteInfCteNorm(models.AbstractModel): _binding_type = "Tcte.InfCte.InfCteNorm" cte40_infCarga = fields.Many2one( - comodel_name="cte.40.infcarga", + comodel_name="cte.40.tcte_infcarga", string="Informações da Carga do CT-e", xsd_required=True, ) @@ -4761,11 +4878,11 @@ class TcteInfCteNorm(models.AbstractModel): ) -class InfCarga(models.AbstractModel): +class TcteInfCarga(models.AbstractModel): "Informações da Carga do CT-e" _description = textwrap.dedent(" %s" % (__doc__,)) - _name = "cte.40.infcarga" + _name = "cte.40.tcte_infcarga" _inherit = "spec.mixin.cte" _binding_type = "Tcte.InfCte.InfCteNorm.InfCarga" @@ -4841,7 +4958,9 @@ class TcteInfQ(models.AbstractModel): _binding_type = "Tcte.InfCte.InfCteNorm.InfCarga.InfQ" cte40_infQ_infCarga_id = fields.Many2one( - comodel_name="cte.40.infcarga", xsd_implicit=True, ondelete="cascade" + comodel_name="cte.40.tcte_infcarga", + xsd_implicit=True, + ondelete="cascade", ) cte40_cUnid = fields.Selection( INFQ_CUNID, @@ -5594,3 +5713,1331 @@ class TcteInfCteSupl(models.AbstractModel): cte40_qrCodCTe = fields.Char( string="Texto com o QR-Code impresso no DACTE", xsd_required=True ) + + +class TcteSimp(models.AbstractModel): + """Tipo Conhecimento de Transporte Eletrônico (Modelo 57) - Modelo + Simplificado""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp" + + cte40_infCte = fields.Many2one( + comodel_name="cte.40.tctesimp_infcte", + string="Informações do CT-e", + xsd_required=True, + ) + + cte40_infCTeSupl = fields.Many2one( + comodel_name="cte.40.tctesimp_infctesupl", + string="Informações suplementares do CT-e", + ) + + +class TcteSimpInfCte(models.AbstractModel): + "Informações do CT-e do tipo GTV-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infcte" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte" + + cte40_ide = fields.Many2one( + comodel_name="cte.40.tctesimp_ide", + string="Identificação do CT-e", + xsd_required=True, + ) + + cte40_compl = fields.Many2one( + comodel_name="cte.40.tctesimp_compl", + string="Dados complementares do CT-e", + help=( + "Dados complementares do CT-e para fins operacionais ou comerciais" + ), + ) + + cte40_emit = fields.Many2one( + comodel_name="cte.40.tctesimp_emit", + string="Identificação do Emitente do CT-e", + xsd_required=True, + ) + + cte40_toma = fields.Many2one( + comodel_name="cte.40.tctesimp_toma", + string="Identificação do tomador do serviço", + xsd_required=True, + choice="infcte", + xsd_choice_required=True, + help="Identificação do tomador do serviço no CT-e", + ) + + cte40_infCarga = fields.Many2one( + comodel_name="cte.40.tctesimp_infcarga", + string="Informações da Carga do CT-e", + xsd_required=True, + ) + + cte40_det = fields.One2many( + "cte.40.det", + "cte40_det_infCte_id", + string="Detalhamento das entregas / prestações", + help="Detalhamento das entregas / prestações do CTe Simplificado", + ) + + cte40_infModal = fields.Many2one( + comodel_name="cte.40.tctesimp_infmodal", + string="Informações do modal", + xsd_required=True, + ) + + cte40_cobr = fields.Many2one( + comodel_name="cte.40.tctesimp_cobr", string="Dados da cobrança do CT-e" + ) + + cte40_infCteSub = fields.Many2one( + comodel_name="cte.40.tctesimp_infctesub", + string="Informações do CT-e de substituição", + ) + + cte40_imp = fields.Many2one( + comodel_name="cte.40.tctesimp_imp", + string="Informações relativas aos Impostos", + xsd_required=True, + ) + + cte40_total = fields.Many2one( + comodel_name="cte.40.total", + string="Valores Totais do CTe", + xsd_required=True, + ) + + cte40_autXML = fields.One2many( + "cte.40.tctesimp_autxml", + "cte40_autXML_infCte_id", + string="Autorizados para download do XML do DF-e", + help=( + "Autorizados para download do XML do DF-e\nInformar CNPJ ou CPF. " + "Preencher os zeros não significativos." + ), + ) + + cte40_infRespTec = fields.Many2one( + comodel_name="cte.40.tresptec", + string="Informações do Responsável Técnico", + xsd_type="TRespTec", + help="Informações do Responsável Técnico pela emissão do DF-e", + ) + + cte40_infSolicNFF = fields.Many2one( + comodel_name="cte.40.tctesimp_infsolicnff", + string="Grupo de informações do pedido", + help=( + "Grupo de informações do pedido de emissão da Nota Fiscal Fácil" + ), + ) + + cte40_infPAA = fields.Many2one( + comodel_name="cte.40.tctesimp_infpaa", + string="Grupo de Informação do Provedor", + help="Grupo de Informação do Provedor de Assinatura e Autorização", + ) + + cte40_versao = fields.Char( + string="Versão do leiaute", + xsd_required=True, + help="Versão do leiaute\nEx: '4.00'", + ) + + cte40_Id = fields.Char( + string="Identificador da tag a ser assinada", + xsd_required=True, + help=( + "Identificador da tag a ser assinada\nInformar a chave de acesso " + "do CT-e e precedida do literal 'CTe'" + ), + ) + + +class TcteSimpIde(models.AbstractModel): + "Identificação da GTV-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_ide" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Ide" + + cte40_cUF = fields.Selection( + TCODUFIBGE, + string="Código da UF do emitente do CT-e", + xsd_required=True, + xsd_type="TCodUfIBGE", + help=("Código da UF do emitente do CT-e.\nUtilizar a Tabela do IBGE."), + ) + + cte40_cCT = fields.Char( + string="Código numérico que compõe a Chave", + xsd_required=True, + help=( + "Código numérico que compõe a Chave de Acesso.\nNúmero aleatório " + "gerado pelo emitente para cada CT-e, com o objetivo de evitar " + "acessos indevidos ao documento." + ), + ) + + cte40_CFOP = fields.Char( + string="Código Fiscal de Operações e Prestações", + xsd_required=True, + xsd_type="TCfop", + ) + + cte40_natOp = fields.Char(string="Natureza da Operação", xsd_required=True) + + cte40_mod = fields.Selection( + TMODCT, + string="Modelo do documento fiscal", + xsd_required=True, + xsd_type="TModGTVe", + help=( + "Modelo do documento fiscal\nUtilizar o código 57 para " + "identificação do CT-e, emitido em substituição aos modelos de " + "conhecimentos em papel." + ), + ) + + cte40_serie = fields.Char( + string="Série do CT-e", + xsd_required=True, + help="Série do CT-e\nPreencher com '0' no caso de série única", + ) + + cte40_nCT = fields.Char( + string="Número do CT-e", xsd_required=True, xsd_type="TNF" + ) + + cte40_dhEmi = fields.Char( + string="Data e hora de emissão do CT-e", + xsd_required=True, + help=( + "Data e hora de emissão do CT-e\nFormato AAAA-MM-DDTHH:MM:DD TZD" + ), + ) + + cte40_tpImp = fields.Selection( + IDE_TPIMP, + string="Formato de impressão do DACTE", + xsd_required=True, + help=( + "Formato de impressão do DACTE\nPreencher com: 1 - Retrato; 2 - " + "Paisagem." + ), + ) + + cte40_tpEmis = fields.Selection( + IDE_TPEMIS, + string="Forma de emissão do CT-e", + xsd_required=True, + help=( + "Forma de emissão do CT-e\nPreencher com:\n1 - Normal;\n3 - Regime" + " Especial NFF; \n4 - EPEC pela SVC; \n7 - Autorização pela SVC-" + "RS;\n8 - Autorização pela SVC-SP" + ), + ) + + cte40_cDV = fields.Char( + string="Digito Verificador da chave de acesso", + xsd_required=True, + help=( + "Digito Verificador da chave de acesso do CT-e\nInformar o dígito" + " de controle da chave de acesso do CT-e, que deve ser calculado " + "com a aplicação do algoritmo módulo 11 (base 2,9) da chave de " + "acesso." + ), + ) + + cte40_tpAmb = fields.Selection( + TAMB, + string="Tipo do Ambiente", + xsd_required=True, + xsd_type="TAmb", + help=( + "Tipo do Ambiente\nPreencher com:1 - Produção; 2 - Homologação." + ), + ) + + cte40_tpCTe = fields.Selection( + TFINCTESIMP, + string="Tipo do CT-e Simplificado", + xsd_required=True, + help=( + "Tipo do CT-e Simplificado\nPreencher com:\n4 - CTe " + "Simplificado\n5 - Substituição CTe Simplificado" + ), + ) + + cte40_procEmi = fields.Selection( + TPROCEMI, + string="Identificador do processo de emissão", + xsd_required=True, + xsd_type="TProcEmi", + help=( + "Identificador do processo de emissão do CT-e\nPreencher com: " + "\n\t\t\t\t\t\t\t\t\t\t\t0 - emissão de CT-e com aplicativo do " + "contribuinte;\n\t\t\t\t\t\t\t\t\t\t\t3- emissão CT-e pelo " + "contribuinte com aplicativo fornecido pelo SEBRAE." + ), + ) + + cte40_verProc = fields.Char( + string="Versão do processo de emissão", + xsd_required=True, + help=( + "Versão do processo de emissão\nInformar a versão do aplicativo " + "emissor de CT-e." + ), + ) + + cte40_cMunEnv = fields.Char( + string="Código do Município de envio do CT-e", + xsd_required=True, + xsd_type="TCodMunIBGE", + help=( + "Código do Município de envio do CT-e (de onde o documento foi " + "transmitido)\nUtilizar a tabela do IBGE. Informar 9999999 para as" + " operações com o exterior." + ), + ) + + cte40_xMunEnv = fields.Char( + string="Nome do Município de envio do CT-e", + xsd_required=True, + help=( + "Nome do Município de envio do CT-e (de onde o documento foi " + "transmitido)\nInformar PAIS/Municipio para as operações com o " + "exterior." + ), + ) + + cte40_UFEnv = fields.Selection( + TUF, + string="Sigla da UF de envio do CT-e", + xsd_required=True, + xsd_type="TUf", + help=( + "Sigla da UF de envio do CT-e (de onde o documento foi " + "transmitido)\nInformar 'EX' para operações com o exterior." + ), + ) + + cte40_modal = fields.Selection( + TMODTRANSPSIMP, + string="Modal", + xsd_required=True, + help=("Modal\nPreencher com:\n01-Rodoviário\n02-Aéreo\n03-Aquaviário"), + ) + + cte40_tpServ = fields.Selection( + IDE_TPSERV, + string="Tipo do Serviço", + xsd_required=True, + help=( + "Tipo do Serviço\nPreencher com: \n0 - Normal;\n1 - " + "Subcontratação;\n2 - Redespacho;" + ), + ) + + cte40_UFIni = fields.Selection( + TUF, + string="UF do início da prestação", + xsd_required=True, + xsd_type="TUf", + help=( + "UF do início da prestação\nInformar 'EX' para operações com o " + "exterior." + ), + ) + + cte40_UFFim = fields.Selection( + TUF, + string="UF do término da prestação", + xsd_required=True, + xsd_type="TUf", + help=( + "UF do término da prestação\nInformar 'EX' para operações com o " + "exterior." + ), + ) + + cte40_retira = fields.Selection( + IDE_RETIRA, + string="Indicador se o Recebedor retira", + xsd_required=True, + help=( + "Indicador se o Recebedor retira no Aeroporto, Filial, Porto ou " + "Estação de Destino?\nPreencher com: 0 - sim; 1 - não" + ), + ) + + cte40_xDetRetira = fields.Char(string="Detalhes do retira") + + cte40_dhCont = fields.Datetime( + string="Data e Hora da entrada em contingência", + xsd_type="TDateTimeUTC", + help=( + "Data e Hora da entrada em contingência\nInformar a data e hora no" + " formato AAAA-MM-DDTHH:MM:SS" + ), + ) + + cte40_xJust = fields.Char( + string="Justificativa da entrada em contingência" + ) + + +class TcteSimpCompl(models.AbstractModel): + "Dados complementares da GTV-e para fins operacionais ou comerciais" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_compl" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl" + + cte40_xCaracAd = fields.Char( + string="Característica adicional do transporte", + help=( + "Característica adicional do transporte\nTexto livre:\nREENTREGA; " + "DEVOLUÇÃO; REFATURAMENTO; etc" + ), + ) + + cte40_xCaracSer = fields.Char( + string="Característica adicional do serviço", + help=( + "Característica adicional do serviço\nTexto " + "livre:\n\t\t\t\t\t\t\t\t\t\t\tENTREGA EXPRESSA; LOGÍSTICA " + "REVERSA; CONVENCIONAL; EMERGENCIAL; etc" + ), + ) + + cte40_fluxo = fields.Many2one( + comodel_name="cte.40.tctesimp_fluxo", + string="Previsão do fluxo da carga", + help=( + "Previsão do fluxo da carga\nPreenchimento obrigatório para o " + "modal aéreo." + ), + ) + + cte40_xObs = fields.Char(string="Observações Gerais") + + cte40_obsCont = fields.One2many( + "cte.40.tctesimp_obscont", + "cte40_ObsCont_compl_id", + string="Campo de uso livre do contribuinte", + help=( + "Campo de uso livre do contribuinte\nInformar o nome do campo no " + "atributo xCampo e o conteúdo do campo no XTexto" + ), + ) + + cte40_obsFisco = fields.One2many( + "cte.40.tctesimp_obsfisco", + "cte40_ObsFisco_compl_id", + string="ObsFisco", + help=( + "Campo de uso livre do contribuinte\nInformar o nome do campo no " + "atributo xCampo e o conteúdo do campo no XTexto" + ), + ) + + +class TcteSimpFluxo(models.AbstractModel): + """Previsão do fluxo da carga + Preenchimento obrigatório para o modal aéreo.""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_fluxo" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl.Fluxo" + + cte40_xOrig = fields.Char( + string="Sigla ou código interno", + help=( + "Sigla ou código interno da Filial/Porto/Estação/ Aeroporto de " + "Origem\nObservações para o modal " + "aéreo:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t- Preenchimento obrigatório " + "para o modal aéreo.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t- O código de " + "três letras IATA do aeroporto de partida deverá ser incluído como" + " primeira anotação. Quando não for possível, utilizar a sigla " + "OACI." + ), + ) + + cte40_pass = fields.One2many( + "cte.40.tctesimp_pass", "cte40_pass_fluxo_id", string="pass" + ) + + cte40_xDest = fields.Char( + string="Sigla ou código interno (xDest)", + help=( + "Sigla ou código interno da Filial/Porto/Estação/Aeroporto de " + "Destino\nObservações para o modal " + "aéreo:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t- Preenchimento obrigatório " + "para o modal aéreo.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t- Deverá ser " + "incluído o código de três letras IATA do aeroporto de destino. " + "Quando não for possível, utilizar a sigla OACI." + ), + ) + + cte40_xRota = fields.Char(string="Código da Rota de Entrega") + + +class TcteSimpPass(models.AbstractModel): + _description = "pass" + _name = "cte.40.tctesimp_pass" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl.Fluxo.Pass" + + cte40_pass_fluxo_id = fields.Many2one( + comodel_name="cte.40.tctesimp_fluxo", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_xPass = fields.Char( + string="Sigla ou código interno", + help=( + "Sigla ou código interno da Filial/Porto/Estação/Aeroporto de " + "Passagem\nObservação para o modal " + "aéreo:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t- O código de três " + "letras IATA, referente ao aeroporto de transferência, deverá ser " + "incluído, quando for o caso. Quando não for possível, utilizar a" + " sigla OACI. Qualquer solicitação de itinerário deverá ser " + "incluída." + ), + ) + + +class TcteSimpObsCont(models.AbstractModel): + """Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_obscont" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl.ObsCont" + + cte40_ObsCont_compl_id = fields.Many2one( + comodel_name="cte.40.tctesimp_compl", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) + + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) + + +class TcteSimpObsFisco(models.AbstractModel): + """Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_obsfisco" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Compl.ObsFisco" + + cte40_ObsFisco_compl_id = fields.Many2one( + comodel_name="cte.40.tctesimp_compl", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_xTexto = fields.Char(string="Conteúdo do campo", xsd_required=True) + + cte40_xCampo = fields.Char( + string="Identificação do campo", xsd_required=True + ) + + +class TcteSimpEmit(models.AbstractModel): + "Identificação do Emitente da GTV-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_emit" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Emit" + + cte40_CNPJ = fields.Char( + string="CNPJ do emitente", + xsd_type="TCnpj", + help="CNPJ do emitente\nInformar zeros não significativos", + ) + + cte40_CPF = fields.Char( + string="CPF do emitente", + choice="emit", + xsd_choice_required=True, + xsd_type="TCpf", + help=( + "CPF do emitente\nInformar zeros não significativos.\n\nUsar com " + "série específica 920-969 para emitente pessoa física com " + "inscrição estadual" + ), + ) + + cte40_IE = fields.Char( + string="Inscrição Estadual do Emitente", + help=( + "Inscrição Estadual do Emitente\nA IE do emitente somente ficará " + "sem informação para o caso do Regime Especial da NFF (tpEmis=3)" + ), + ) + + cte40_IEST = fields.Char( + string="Inscrição Estadual", + help="Inscrição Estadual do Substituto Tributário", + ) + + cte40_xNome = fields.Char( + string="Razão social ou Nome do emitente", xsd_required=True + ) + + cte40_xFant = fields.Char(string="Nome fantasia") + + cte40_enderEmit = fields.Many2one( + comodel_name="cte.40.tendeemi", + string="Endereço do emitente", + xsd_required=True, + xsd_type="TEndeEmi", + ) + + cte40_CRT = fields.Selection( + TCRT, + string="Código do Regime Tributário", + xsd_required=True, + xsd_type="TCRT", + help=( + "Código do Regime Tributário\nInformar: 1=Simples Nacional; " + "\n2=Simples Nacional, excesso sublimite de receita " + "bruta;\n3=Regime Normal. \n4=Simples Nacional - Microempreendedor" + " Individual – MEI." + ), + ) + + +class TcteSimpToma(models.AbstractModel): + """Indicador do "papel" do tomador do serviço no GT-e""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_toma" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Toma" + + cte40_toma = fields.Selection( + TOMA_TOMA, + string="Tomador do Serviço", + xsd_required=True, + help=( + "Tomador do Serviço\nPreencher com:\n\n0-Remetente;\n1-" + "Expedidor;\n2-Recebedor;\n3-Destinatário\n4-Terceiro" + ), + ) + + cte40_indIEToma = fields.Selection( + TOMA_INDIETOMA, + string="Indicador do papel do tomador", + xsd_required=True, + help=( + "Indicador do papel do tomador na prestação do serviço:\n1 – " + "Contribuinte ICMS;\n2 – Contribuinte isento de inscrição;\n9 – " + "Não Contribuinte\nAplica-se ao tomador que for indicado no toma3 " + "ou toma4" + ), + ) + + cte40_CNPJ = fields.Char( + string="Número do CNPJ", + choice="toma", + xsd_choice_required=True, + xsd_type="TCnpjOpc", + help=( + "Número do CNPJ\nEm caso de empresa não estabelecida no Brasil, " + "será informado o CNPJ com " + "zeros.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\nInformar os zeros não " + "significativos." + ), + ) + + cte40_CPF = fields.Char( + string="Número do CPF", + choice="toma", + xsd_choice_required=True, + xsd_type="TCpf", + help="Número do CPF\nInformar os zeros não significativos.", + ) + + cte40_IE = fields.Char( + string="Inscrição Estadual", + help=( + "Inscrição Estadual\nInformar a IE do tomador ou ISENTO se tomador" + " é contribuinte do ICMS isento de inscrição no cadastro de " + "contribuintes do ICMS. Caso o tomador não seja contribuinte do " + "ICMS não informar o conteúdo." + ), + ) + + cte40_xNome = fields.Char(string="Razão Social ou Nome", xsd_required=True) + + cte40_ISUF = fields.Char( + string="Inscrição na SUFRAMA", + help=( + "Inscrição na SUFRAMA\n(Obrigatório nas operações com as áreas com" + " benefícios de incentivos fiscais sob controle da SUFRAMA)" + ), + ) + + cte40_fone = fields.Char(string="Telefone", xsd_type="TFone") + + cte40_enderToma = fields.Many2one( + comodel_name="cte.40.tendereco", + string="Dados do endereço", + xsd_required=True, + xsd_type="TEndereco", + ) + + cte40_email = fields.Char(string="Endereço de email", xsd_type="TEmail") + + +class TcteSimpInfCarga(models.AbstractModel): + "Informações da Carga do CT-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infcarga" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfCarga" + + cte40_vCarga = fields.Monetary( + string="Valor total da carga", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + cte40_proPred = fields.Char( + string="Produto predominante", + xsd_required=True, + help=( + "Produto predominante\nInformar a descrição do produto " + "predominante" + ), + ) + + cte40_xOutCat = fields.Char( + string="Outras características da carga", + help=( + "Outras características da carga\n'FRIA', 'GRANEL', 'REFRIGERADA'," + " 'Medidas: 12X12X12'" + ), + ) + + cte40_infQ = fields.One2many( + "cte.40.tctesimp_infq", + "cte40_infQ_infCarga_id", + string="Informações de quantidades da Carga", + help=( + "Informações de quantidades da Carga do CT-e\nPara o Aéreo é " + "obrigatório o preenchimento desse campo da seguinte forma.\n1 - " + "Peso Bruto, sempre em quilogramas (obrigatório);\n2 - Peso " + "Cubado; sempre em quilogramas;\n3 - Quantidade de volumes, sempre" + " em unidades (obrigatório);\n4 - Cubagem, sempre em metros " + "cúbicos (obrigatório apenas quando for impossível preencher as " + "dimensões da(s) embalagem(ens) na tag xDime do leiaute do Aéreo)." + ), + ) + + cte40_vCargaAverb = fields.Monetary( + string="Valor da Carga para efeito de averbação", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + help=( + "Valor da Carga para efeito de averbação\nNormalmente igual ao " + "valor declarado da mercadoria, diferente por exemplo, quando a " + "mercadoria transportada é isenta de tributos nacionais para " + "exportação, onde é preciso averbar um valor maior, pois no caso " + "de indenização, o valor a ser pago será maior" + ), + ) + + +class TcteSimpInfQ(models.AbstractModel): + """Informações de quantidades da Carga do CT-e + Para o Aéreo é obrigatório o preenchimento desse campo da seguinte forma. + 1 - Peso Bruto, sempre em quilogramas (obrigatório); + 2 - Peso Cubado; sempre em quilogramas; + 3 - Quantidade de volumes, sempre em unidades (obrigatório); + 4 - Cubagem, sempre em metros cúbicos (obrigatório apenas quando for impossível + preencher as dimensões da(s) embalagem(ens) na tag xDime do leiaute do + Aéreo).""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infq" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfCarga.InfQ" + + cte40_infQ_infCarga_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infcarga", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_cUnid = fields.Selection( + INFQ_CUNID, + string="Código da Unidade de Medida", + xsd_required=True, + help=( + "Código da Unidade de Medida\nPreencher " + "com:\n00-M3;\n01-KG;\n02-TON;\n03-UNIDADE;\n04-LITROS;\n05-MMBTU" + ), + ) + + cte40_tpMed = fields.Selection( + INFQ_TPMED, + string="Tipo da Medida", + xsd_required=True, + help=( + "Tipo da Medida\nInformar com:\n00-Cubagem da NF-e\n01-Cubagem " + "Aferida pelo Transportador\n02-Peso Bruto da NF-e\n03-Peso Bruto " + "Aferido pelo Transportador\n04-Peso Cubado\n05-Peso Base do " + "Cálculo do Frete\n06-Peso para uso Operacional\n07-Caixas\n08-" + "Paletes\n09-Sacas\n10-Containers\n11-Rolos\n12-Bombonas\n13-" + "Latas\n14-Litragem\n15-Milhão de BTU (British Thermal " + "Units)\n99-Outros" + ), + ) + + cte40_qCarga = fields.Float( + string="Quantidade", + xsd_required=True, + xsd_type="TDec_1104", + digits=( + 11, + 4, + ), + ) + + +class Det(models.AbstractModel): + "Detalhamento das entregas / prestações do CTe Simplificado" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.det" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det" + + cte40_det_infCte_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infcte", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_cMunIni = fields.Char( + string="Código do Município de início", + xsd_required=True, + xsd_type="TCodMunIBGE", + help=( + "Código do Município de início da prestação\nUtilizar a tabela do " + "IBGE. Informar 9999999 para operações com o exterior." + ), + ) + + cte40_xMunIni = fields.Char( + string="Nome do Município do início da prestação", + xsd_required=True, + help=( + "Nome do Município do início da prestação\nInformar 'EXTERIOR' " + "para operações com o exterior." + ), + ) + + cte40_cMunFim = fields.Char( + string="Código do Município de término", + xsd_required=True, + xsd_type="TCodMunIBGE", + help=( + "Código do Município de término da prestação\nUtilizar a tabela do" + " IBGE. Informar 9999999 para operações com o exterior." + ), + ) + + cte40_xMunFim = fields.Char( + string="Nome do Município do término", + xsd_required=True, + help=( + "Nome do Município do término da prestação\nInformar 'EXTERIOR' " + "para operações com o exterior." + ), + ) + + cte40_vPrest = fields.Monetary( + string="Valorl da Prestação do Serviço", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + help=( + "Valorl da Prestação do Serviço\nPode conter zeros quando o CT-e " + "for de complemento de ICMS" + ), + ) + + cte40_vRec = fields.Monetary( + string="Valor a Receber", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + cte40_comp = fields.One2many( + "cte.40.tctesimp_det_comp", + "cte40_Comp_det_id", + string="Componentes do Valor da Prestação", + ) + + cte40_infNFe = fields.One2many( + "cte.40.tctesimp_infnfe", + "cte40_infNFe_det_id", + string="Informações das NF-e", + choice="det", + xsd_choice_required=True, + ) + + cte40_infDocAnt = fields.One2many( + "cte.40.infdocant", + "cte40_infDocAnt_det_id", + string="Documentos anteriores", + choice="det", + xsd_choice_required=True, + ) + + cte40_nItem = fields.Char( + string="Número identificador do item agrupador", + xsd_required=True, + help="Número identificador do item agrupador da prestação", + ) + + +class TcteSimpDetComp(models.AbstractModel): + "Componentes do Valor da Prestação" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_det_comp" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det.Comp" + + cte40_Comp_det_id = fields.Many2one( + comodel_name="cte.40.det", xsd_implicit=True, ondelete="cascade" + ) + cte40_xNome = fields.Char( + string="Nome do componente", + xsd_required=True, + help=( + "Nome do componente\nExxemplos: FRETE PESO, FRETE VALOR, SEC/CAT, " + "ADEME, AGENDAMENTO, etc" + ), + ) + + cte40_vComp = fields.Monetary( + string="Valor do componente", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + +class TcteSimpInfNfe(models.AbstractModel): + "Informações das NF-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infnfe" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det.InfNfe" + + cte40_infNFe_det_id = fields.Many2one( + comodel_name="cte.40.det", xsd_implicit=True, ondelete="cascade" + ) + cte40_chNFe = fields.Char( + string="Chave de acesso da NF-e", xsd_required=True, xsd_type="TChDFe" + ) + + cte40_PIN = fields.Char( + string="PIN SUFRAMA", + help="PIN SUFRAMA\nPIN atribuído pela SUFRAMA para a operação.", + ) + + cte40_dPrev = fields.Date( + string="Data prevista de entrega", + xsd_type="TData", + help="Data prevista de entrega\nFormato AAAA-MM-DD", + ) + + cte40_infUnidCarga = fields.One2many( + "cte.40.tunidcarga", + "cte40_infUnidCarga_infNFe_id", + string="Informações das Unidades de Carga", + choice="infnfe", + xsd_choice_required=True, + xsd_type="TUnidCarga", + help=( + "Informações das Unidades de Carga " + "(Containeres/ULD/Outros)\nDispositivo de carga utilizada (Unit " + "Load Device - ULD) significa todo tipo de contêiner de carga, " + "vagão, contêiner de avião, palete de aeronave com rede ou palete " + "de aeronave com rede sobre um iglu." + ), + ) + + cte40_infUnidTransp = fields.One2many( + "cte.40.tunidadetransp", + "cte40_infUnidTransp_infNFe_id", + string="Informações das Unidades de Transporte", + choice="infnfe", + xsd_choice_required=True, + xsd_type="TUnidadeTransp", + help=( + "Informações das Unidades de Transporte " + "(Carreta/Reboque/Vagão)\nDeve ser preenchido com as informações " + "das unidades de transporte utilizadas." + ), + ) + + +class InfDocAnt(models.AbstractModel): + "Documentos anteriores" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.infdocant" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det.InfDocAnt" + + cte40_infDocAnt_det_id = fields.Many2one( + comodel_name="cte.40.det", xsd_implicit=True, ondelete="cascade" + ) + cte40_chCTe = fields.Char( + string="Chave de acesso do CT-e", xsd_required=True, xsd_type="TChDFe" + ) + + cte40_tpPrest = fields.Selection( + INFDOCANT_TPPREST, + string="indica se a prestação é total ou parcial", + xsd_required=True, + help=( + "indica se a prestação é total ou parcial em relação as notas do " + "documento anterior\nPreencher com:\n\n1 - Total\n2 - Parcial" + ), + ) + + cte40_infNFeTranspParcial = fields.One2many( + "cte.40.infnfetranspparcial", + "cte40_infNFeTranspParcial_infDocAnt_id", + string="infNFeTranspParcial", + ) + + +class InfNfeTranspParcial(models.AbstractModel): + _description = "infNFeTranspParcial" + _name = "cte.40.infnfetranspparcial" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Det.InfDocAnt.InfNfeTranspParcial" + + cte40_infNFeTranspParcial_infDocAnt_id = fields.Many2one( + comodel_name="cte.40.infdocant", xsd_implicit=True, ondelete="cascade" + ) + cte40_chNFe = fields.Char( + string="Chave de acesso da NF-e", + xsd_required=True, + xsd_type="TChDFe", + help=( + "Chave de acesso da NF-e\nInformando o tpPrest com “2 – Parcial” " + "deve-se informar as chaves de acesso das NF-e que acobertam a " + "carga transportada." + ), + ) + + +class TcteSimpInfModal(models.AbstractModel): + "Informações do modal" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infmodal" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfModal" + + cte40_versaoModal = fields.Char( + string="Versão do leiaute específico", + xsd_required=True, + help="Versão do leiaute específico para o Modal", + ) + + +class TcteSimpCobr(models.AbstractModel): + "Dados da cobrança do CT-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_cobr" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Cobr" + + cte40_fat = fields.Many2one( + comodel_name="cte.40.tctesimp_fat", string="Dados da fatura" + ) + + cte40_dup = fields.One2many( + "cte.40.tctesimp_dup", + "cte40_dup_cobr_id", + string="Dados das duplicatas", + ) + + +class TcteSimpFat(models.AbstractModel): + "Dados da fatura" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_fat" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Cobr.Fat" + + cte40_nFat = fields.Char(string="Número da fatura") + + cte40_vOrig = fields.Monetary( + string="Valor original da fatura", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + ) + + cte40_vDesc = fields.Monetary( + string="Valor do desconto da fatura", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + ) + + cte40_vLiq = fields.Monetary( + string="Valor líquido da fatura", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + ) + + +class TcteSimpDup(models.AbstractModel): + "Dados das duplicatas" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_dup" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Cobr.Dup" + + cte40_dup_cobr_id = fields.Many2one( + comodel_name="cte.40.tctesimp_cobr", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_nDup = fields.Char(string="Número da duplicata") + + cte40_dVenc = fields.Date( + string="Data de vencimento da duplicata", + xsd_type="TData", + help="Data de vencimento da duplicata (AAAA-MM-DD)", + ) + + cte40_vDup = fields.Monetary( + string="Valor da duplicata", + xsd_type="TDec_1302Opc", + currency_field="brl_currency_id", + ) + + +class TcteSimpInfCteSub(models.AbstractModel): + "Informações do CT-e de substituição" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infctesub" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfCteSub" + + cte40_chCte = fields.Char( + string="Chave de acesso do CT-e", + xsd_required=True, + help="Chave de acesso do CT-e a ser substituído (original)", + ) + + cte40_indAlteraToma = fields.Selection( + INFCTESUB_INDALTERATOMA, + string="Indicador de CT-e Alteração de Tomador", + ) + + +class TcteSimpImp(models.AbstractModel): + "Informações relativas aos Impostos" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_imp" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Imp" + + cte40_ICMS = fields.Many2one( + comodel_name="cte.40.timp", + string="Informações relativas ao ICMS", + xsd_required=True, + xsd_type="TImp", + ) + + cte40_vTotTrib = fields.Monetary( + string="Valor Total dos Tributos", + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + cte40_infAdFisco = fields.Char( + string="Informações adicionais de interesse", + help=( + "Informações adicionais de interesse do Fisco\nNorma referenciada," + " informações complementares, etc" + ), + ) + + +class Total(models.AbstractModel): + "Valores Totais do CTe" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.total" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.Total" + + cte40_vTPrest = fields.Monetary( + string="Valor Total da Prestação do Serviço", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + help=( + "Valor Total da Prestação do Serviço\nPode conter zeros quando o " + "CT-e for de complemento de ICMS" + ), + ) + + cte40_vTRec = fields.Monetary( + string="Valor total a Receber", + xsd_required=True, + xsd_type="TDec_1302", + currency_field="brl_currency_id", + ) + + +class TcteSimpAutXml(models.AbstractModel): + """Autorizados para download do XML do DF-e + Informar CNPJ ou CPF. Preencher os zeros não significativos.""" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_autxml" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.AutXml" + + cte40_autXML_infCte_id = fields.Many2one( + comodel_name="cte.40.tctesimp_infcte", + xsd_implicit=True, + ondelete="cascade", + ) + cte40_CNPJ = fields.Char( + string="CNPJ do autorizado", + choice="autxml", + xsd_choice_required=True, + xsd_type="TCnpj", + help="CNPJ do autorizado\nInformar zeros não significativos", + ) + + cte40_CPF = fields.Char( + string="CPF do autorizado", + choice="autxml", + xsd_choice_required=True, + xsd_type="TCpf", + help="CPF do autorizado\nInformar zeros não significativos", + ) + + +class TcteSimpInfSolicNff(models.AbstractModel): + "Grupo de informações do pedido de emissão da Nota Fiscal Fácil" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infsolicnff" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfSolicNff" + + cte40_xSolic = fields.Char( + string="Solicitação do pedido de emissão da NFF", + xsd_required=True, + help=( + "Solicitação do pedido de emissão da NFF.\nSerá preenchido com a " + "totalidade de campos informados no aplicativo emissor " + "serializado." + ), + ) + + +class TcteSimpInfPaa(models.AbstractModel): + "Grupo de Informação do Provedor de Assinatura e Autorização" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infpaa" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfPaa" + + cte40_CNPJPAA = fields.Char( + string="CNPJ do Provedor de Assinatura", + xsd_required=True, + xsd_type="TCnpj", + help="CNPJ do Provedor de Assinatura e Autorização", + ) + + cte40_PAASignature = fields.Many2one( + comodel_name="cte.40.tctesimp_paasignature", + string="Assinatura RSA do Emitente", + xsd_required=True, + help="Assinatura RSA do Emitente para DFe gerados por PAA", + ) + + +class TcteSimpPaasignature(models.AbstractModel): + "Assinatura RSA do Emitente para DFe gerados por PAA" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_paasignature" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCte.InfPaa.Paasignature" + + cte40_signatureValue = fields.Char( + string="Assinatura digital padrão RSA", + xsd_required=True, + xsd_type="xs:base64Binary", + help=( + "Assinatura digital padrão RSA\nConverter o atributo Id do DFe " + "para array de bytes e assinar com a chave privada do RSA com " + "algoritmo SHA1 gerando um valor no formato base64." + ), + ) + + cte40_RSAKeyValue = fields.Many2one( + comodel_name="cte.40.trsakeyvaluetype", + string="Chave Publica no padrão XML RSA Key", + xsd_required=True, + xsd_type="TRSAKeyValueType", + ) + + +class TcteSimpInfCteSupl(models.AbstractModel): + "Informações suplementares da GTV-e" + + _description = textwrap.dedent(" %s" % (__doc__,)) + _name = "cte.40.tctesimp_infctesupl" + _inherit = "spec.mixin.cte" + _binding_type = "TcteSimp.InfCteSupl" + + cte40_qrCodCTe = fields.Char( + string="Texto com o QR-Code impresso no DACTE", xsd_required=True + ) diff --git a/l10n_br_cte_spec/readme/DESCRIPTION.rst b/l10n_br_cte_spec/readme/DESCRIPTION.rst index 22e40f1403bc..7a050013ff5e 100644 --- a/l10n_br_cte_spec/readme/DESCRIPTION.rst +++ b/l10n_br_cte_spec/readme/DESCRIPTION.rst @@ -2,11 +2,7 @@ Este módulo contem a estrutura de dados do Conhecimento de Transporte Eletrôni Este módulo não faz nada sozinho, ele precisaria de um modulo `l10n_br_cte` que mapearia esses mixins nos documentos fiscais Odoo de forma semelhante a forma como o módulo `l10n_br_nfe` faz como o módulo `l10n_br_nfe_spec`. -Este módulo inclue os principais leiautes persistantes de CT-e: - -* CT-e (Conhecimento de Transporte Eletrônico) -* CT-e OS (Conhecimento de transporte eletrônico para outros serviço - +Este módulo inclui os principais layouts persistentes do CT-e (Conhecimento de Transporte Eletrônico). Geração ~~~~~~~ @@ -18,5 +14,5 @@ https://github.com/akretion/xsdata-odoo O comando usado foi:: - export XSDATA_SCHEMA=cte; export XSDATA_VERSION=30; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese" + export XSDATA_SCHEMA=cte; export XSDATA_VERSION=40; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese" xsdata generate nfelib/cte/schemas/v4_0 --package nfelib.cte.odoo.v4_0 --output=odoo From 7c9d7f32351419b597bd0d27d9d200887d63e11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Felipe=20Mil=C3=A9o?= Date: Thu, 24 Oct 2024 17:20:36 -0300 Subject: [PATCH 03/27] [ADD] l10n_br_cte: add new module --- l10n_br_cte/README.rst | 113 ++ l10n_br_cte/__init__.py | 2 + l10n_br_cte/__manifest__.py | 42 + l10n_br_cte/constants/modal.py | 32 + l10n_br_cte/hooks.py | 21 + l10n_br_cte/modal/modal_aereo.xml | 21 + l10n_br_cte/modal/modal_aquaviario.xml | 18 + l10n_br_cte/modal/modal_ferroviario.xml | 51 + l10n_br_cte/modal/modal_rodoviario.xml | 25 + l10n_br_cte/models/__init__.py | 15 + l10n_br_cte/models/aereo.py | 69 ++ l10n_br_cte/models/aquaviario.py | 52 + l10n_br_cte/models/document.py | 982 ++++++++++++++++++ .../models/document_cargo_quantity_infos.py | 27 + l10n_br_cte/models/document_line.py | 216 ++++ l10n_br_cte/models/document_related.py | 128 +++ l10n_br_cte/models/document_supplement.py | 22 + .../models/document_transported_vehicles.py | 43 + l10n_br_cte/models/dutoviario.py | 25 + l10n_br_cte/models/ferroviario.py | 55 + l10n_br_cte/models/normal_cte_infos.py | 153 +++ l10n_br_cte/models/res_company.py | 105 ++ l10n_br_cte/models/res_config_settings.py | 12 + l10n_br_cte/models/res_partner.py | 156 +++ l10n_br_cte/models/rodoviario.py | 76 ++ l10n_br_cte/readme/CONFIGURE.rst | 10 + l10n_br_cte/readme/CONTRIBUTORS.rst | 1 + l10n_br_cte/readme/DESCRIPTION.rst | 4 + l10n_br_cte/readme/USAGE.rst | 11 + l10n_br_cte/security/ir.model.access.csv | 17 + l10n_br_cte/static/description/icon.png | Bin 0 -> 30809 bytes l10n_br_cte/static/description/index.html | 463 +++++++++ l10n_br_cte/views/cte_document.xml | 138 +++ l10n_br_cte/views/document_line.xml | 13 + l10n_br_cte/views/document_related.xml | 17 + l10n_br_cte/views/res_company.xml | 33 + l10n_br_cte/views/res_partner.xml | 17 + setup/l10n_br_cte/odoo/addons/l10n_br_cte | 1 + setup/l10n_br_cte/setup.py | 6 + 39 files changed, 3192 insertions(+) create mode 100644 l10n_br_cte/README.rst create mode 100644 l10n_br_cte/__init__.py create mode 100644 l10n_br_cte/__manifest__.py create mode 100644 l10n_br_cte/constants/modal.py create mode 100644 l10n_br_cte/hooks.py create mode 100644 l10n_br_cte/modal/modal_aereo.xml create mode 100644 l10n_br_cte/modal/modal_aquaviario.xml create mode 100644 l10n_br_cte/modal/modal_ferroviario.xml create mode 100644 l10n_br_cte/modal/modal_rodoviario.xml create mode 100644 l10n_br_cte/models/__init__.py create mode 100644 l10n_br_cte/models/aereo.py create mode 100644 l10n_br_cte/models/aquaviario.py create mode 100644 l10n_br_cte/models/document.py create mode 100644 l10n_br_cte/models/document_cargo_quantity_infos.py create mode 100644 l10n_br_cte/models/document_line.py create mode 100644 l10n_br_cte/models/document_related.py create mode 100644 l10n_br_cte/models/document_supplement.py create mode 100644 l10n_br_cte/models/document_transported_vehicles.py create mode 100644 l10n_br_cte/models/dutoviario.py create mode 100644 l10n_br_cte/models/ferroviario.py create mode 100644 l10n_br_cte/models/normal_cte_infos.py create mode 100644 l10n_br_cte/models/res_company.py create mode 100644 l10n_br_cte/models/res_config_settings.py create mode 100644 l10n_br_cte/models/res_partner.py create mode 100644 l10n_br_cte/models/rodoviario.py create mode 100644 l10n_br_cte/readme/CONFIGURE.rst create mode 100644 l10n_br_cte/readme/CONTRIBUTORS.rst create mode 100644 l10n_br_cte/readme/DESCRIPTION.rst create mode 100644 l10n_br_cte/readme/USAGE.rst create mode 100644 l10n_br_cte/security/ir.model.access.csv create mode 100644 l10n_br_cte/static/description/icon.png create mode 100644 l10n_br_cte/static/description/index.html create mode 100644 l10n_br_cte/views/cte_document.xml create mode 100644 l10n_br_cte/views/document_line.xml create mode 100644 l10n_br_cte/views/document_related.xml create mode 100644 l10n_br_cte/views/res_company.xml create mode 100644 l10n_br_cte/views/res_partner.xml create mode 120000 setup/l10n_br_cte/odoo/addons/l10n_br_cte create mode 100644 setup/l10n_br_cte/setup.py diff --git a/l10n_br_cte/README.rst b/l10n_br_cte/README.rst new file mode 100644 index 000000000000..caafff26c57b --- /dev/null +++ b/l10n_br_cte/README.rst @@ -0,0 +1,113 @@ +==== +CT-e +==== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c91616235e33e68d0115aa3807f25142a45f5013a23492f240cf507a41d41340 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--brazil-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-brazil/tree/14.0/l10n_br_cte + :alt: OCA/l10n-brazil +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-brazil-14-0/l10n-brazil-14-0-l10n_br_cte + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-brazil&target_branch=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +[ 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_cte/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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* KMEE + +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. + +This module is part of the `OCA/l10n-brazil `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_br_cte/__init__.py b/l10n_br_cte/__init__.py new file mode 100644 index 000000000000..cc6b6354ad8f --- /dev/null +++ b/l10n_br_cte/__init__.py @@ -0,0 +1,2 @@ +from . import models +from .hooks import post_init_hook diff --git a/l10n_br_cte/__manifest__.py b/l10n_br_cte/__manifest__.py new file mode 100644 index 000000000000..23e83cc880dc --- /dev/null +++ b/l10n_br_cte/__manifest__.py @@ -0,0 +1,42 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "CT-e", + "summary": """Brazilian Electronic Invoice CT-e""", + "version": "14.0.1.0.0", + "category": "Localisation", + "license": "AGPL-3", + "author": "KMEE, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/l10n-brazil", + "development_status": "Alpha", + "depends": [ + "l10n_br_fiscal", + "l10n_br_cte_spec", + "l10n_br_fiscal_certificate", + "spec_driven_model", + ], + "data": [ + "security/ir.model.access.csv", + # "views/document_line.xml", + # 'views/document_related.xml', + # 'views/res_partner.xml', + "modal/modal_rodoviario.xml", + "modal/modal_aquaviario.xml", + "modal/modal_ferroviario.xml", + "modal/modal_aereo.xml", + "views/res_company.xml", + "views/cte_document.xml", + ], + "post_init_hook": "post_init_hook", + "installable": True, + "auto_install": False, + "external_dependencies": { + "python": [ + "nfelib", + "erpbrasil.transmissao", + "erpbrasil.edoc", + "erpbrasil.assinatura", + ] + }, +} diff --git a/l10n_br_cte/constants/modal.py b/l10n_br_cte/constants/modal.py new file mode 100644 index 000000000000..42dcd1d85a53 --- /dev/null +++ b/l10n_br_cte/constants/modal.py @@ -0,0 +1,32 @@ +CTE_MODAL_VERSION_DEFAULT = "4.00" + +TUF = [ + ("AC", "AC"), + ("AL", "AL"), + ("AM", "AM"), + ("AP", "AP"), + ("BA", "BA"), + ("CE", "CE"), + ("DF", "DF"), + ("ES", "ES"), + ("GO", "GO"), + ("MA", "MA"), + ("MG", "MG"), + ("MS", "MS"), + ("MT", "MT"), + ("PA", "PA"), + ("PB", "PB"), + ("PE", "PE"), + ("PI", "PI"), + ("PR", "PR"), + ("RJ", "RJ"), + ("RN", "RN"), + ("RO", "RO"), + ("RR", "RR"), + ("RS", "RS"), + ("SC", "SC"), + ("SE", "SE"), + ("SP", "SP"), + ("TO", "TO"), + ("EX", "EX"), +] diff --git a/l10n_br_cte/hooks.py b/l10n_br_cte/hooks.py new file mode 100644 index 000000000000..c01e14a97c2b --- /dev/null +++ b/l10n_br_cte/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_cte", + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00", + ) + + hooks.post_init_hook( + cr, + registry, + "l10n_br_cte", + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00", + ) diff --git a/l10n_br_cte/modal/modal_aereo.xml b/l10n_br_cte/modal/modal_aereo.xml new file mode 100644 index 000000000000..eaa86fccea46 --- /dev/null +++ b/l10n_br_cte/modal/modal_aereo.xml @@ -0,0 +1,21 @@ + + + + + modal.aereo.peri.form.view (in l10n_br_cte) + l10n_br_cte.modal.aereo.peri + +
+ + + + + + + + +
+
+
+
diff --git a/l10n_br_cte/modal/modal_aquaviario.xml b/l10n_br_cte/modal/modal_aquaviario.xml new file mode 100644 index 000000000000..28933d4a09eb --- /dev/null +++ b/l10n_br_cte/modal/modal_aquaviario.xml @@ -0,0 +1,18 @@ + + + + + modal.aquaviario.balsa.form.view (in l10n_br_cte) + l10n_br_cte.modal.aquav.balsa + +
+ + + + + +
+
+
+
diff --git a/l10n_br_cte/modal/modal_ferroviario.xml b/l10n_br_cte/modal/modal_ferroviario.xml new file mode 100644 index 000000000000..2c11907ab735 --- /dev/null +++ b/l10n_br_cte/modal/modal_ferroviario.xml @@ -0,0 +1,51 @@ + + + + + res.partner.ferroenv.form.view (in l10n_br_cte) + res.partner + +
+ + + + + + + + + +
+
+
+ + + res.partner.tenderfer.form.view + res.partner + +
+ + + + + + + + + + + + +
+
+
+
diff --git a/l10n_br_cte/modal/modal_rodoviario.xml b/l10n_br_cte/modal/modal_rodoviario.xml new file mode 100644 index 000000000000..338957199239 --- /dev/null +++ b/l10n_br_cte/modal/modal_rodoviario.xml @@ -0,0 +1,25 @@ + + + + + modal.rodoviario.occ.form.view (in l10n_br_cte) + l10n_br_cte.modal.rodo.occ + +
+ + + + + + + + + + + + +
+
+
+
diff --git a/l10n_br_cte/models/__init__.py b/l10n_br_cte/models/__init__.py new file mode 100644 index 000000000000..3aac89ccbd32 --- /dev/null +++ b/l10n_br_cte/models/__init__.py @@ -0,0 +1,15 @@ +from . import document +from . import res_company +from . import res_partner +from . import document_related +from . import document_line +from . import res_config_settings +from . import ferroviario +from . import rodoviario +from . import aereo +from . import dutoviario +from . import aquaviario +from . import document_cargo_quantity_infos +from . import document_supplement +from . import document_transported_vehicles +from . import normal_cte_infos diff --git a/l10n_br_cte/models/aereo.py b/l10n_br_cte/models/aereo.py new file mode 100644 index 000000000000..f155c5b7230a --- /dev/null +++ b/l10n_br_cte/models/aereo.py @@ -0,0 +1,69 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class Aereo(spec_models.StackedModel): + _name = "l10n_br_cte.modal.aereo" + _inherit = "cte.40.aereo" + _stacked = "cte.40.aereo" + _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aereo_v4_00" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" + _spec_tab_name = "CTe" + _description = "Modal Aereo CTe" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_nMinu = fields.Char(related="document_id.cte40_nMinu") + + cte40_nOCA = fields.Char(related="document_id.cte40_nOCA") + + cte40_dPrevAereo = fields.Date(related="document_id.cte40_dPrevAereo") + + cte40_CL = fields.Char(related="document_id.cte40_CL") + + cte40_cTar = fields.Char(related="document_id.cte40_cTar") + + cte40_vTar = fields.Monetary(related="document_id.cte40_aereo_vTar") + + cte40_xDime = fields.Char(related="document_id.cte40_xDime") + + cte40_peri = fields.One2many(related="document_id.cte40_peri") + + def _prepare_dacte_values(self): + if not self: + return {} + + +class Peri(spec_models.StackedModel): + _name = "l10n_br_cte.modal.aereo.peri" + _inherit = "cte.40.peri" + _stacked = "cte.40.peri" + _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aereo_v4_00" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" + _spec_tab_name = "CTe" + _description = """Preenchido quando for transporte de produtos classificados pela ONU como + perigosos. O preenchimento desses campos não desobriga a empresa aérea de emitir os demais + documentos que constam na legislação vigente.""" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_nONU = fields.Char(required=True) + + cte40_qTotEmb = fields.Char(required=True) + + cte40_qTotProd = fields.Float(required=True) + + cte40_uniAP = fields.Selection(required=True) diff --git a/l10n_br_cte/models/aquaviario.py b/l10n_br_cte/models/aquaviario.py new file mode 100644 index 000000000000..3e5e7107c957 --- /dev/null +++ b/l10n_br_cte/models/aquaviario.py @@ -0,0 +1,52 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class Aquav(spec_models.StackedModel): + _name = "l10n_br_cte.modal.aquav" + _inherit = "cte.40.aquav" + _stacked = "cte.40.aquav" + _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aquaviario_v4_00" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aquaviario_v4_00" + _spec_tab_name = "CTe" + _description = "Modal Aquaviário CTe" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_vAFRMM = fields.Monetary(related="document_id.cte40_vAFRMM") + + cte40_vPrest = fields.Monetary(related="document_id.cte40_vPrest") + + cte40_xNavio = fields.Char(related="document_id.cte40_xNavio") + + cte40_nViag = fields.Char(related="document_id.cte40_nViag") + + cte40_direc = fields.Selection(related="document_id.cte40_direc") + + cte40_irin = fields.Char(related="document_id.cte40_irin") + + cte40_tpNav = fields.Selection(related="document_id.cte40_tpNav") + + cte40_balsa = fields.One2many(related="document_id.cte40_balsa") + + def _prepare_dacte_values(self): + if not self: + return {} + + +class Balsa(spec_models.SpecModel): + _name = "l10n_br_cte.modal.aquav.balsa" + _inherit = "cte.40.balsa" + _description = "Grupo de informações das balsas" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_xBalsa = fields.Char(string="Identificador da Balsa") diff --git a/l10n_br_cte/models/document.py b/l10n_br_cte/models/document.py new file mode 100644 index 000000000000..e0fdaec4bb92 --- /dev/null +++ b/l10n_br_cte/models/document.py @@ -0,0 +1,982 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging +import re +from datetime import datetime + +from erpbrasil.assinatura import certificado as cert +from erpbrasil.transmissao import TransmissaoSOAP +from nfelib.cte.bindings.v4_0.cte_v4_00 import Cte +from nfelib.nfe.ws.edoc_legacy import CTeAdapter as edoc_cte +from requests import Session + +from odoo import _, api, fields +from odoo.exceptions import UserError + +from odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_ferroviario_v4_00 import ( + FERROV_TPTRAF, + TRAFMUT_FERREMI, + TRAFMUT_RESPFAT, +) +from odoo.addons.l10n_br_fiscal.constants.fiscal import ( + AUTORIZADO, + CANCELADO, + CANCELADO_DENTRO_PRAZO, + CANCELADO_FORA_PRAZO, + DENEGADO, + EVENT_ENV_HML, + EVENT_ENV_PROD, + LOTE_PROCESSADO, + SITUACAO_EDOC_AUTORIZADA, + SITUACAO_EDOC_CANCELADA, + SITUACAO_EDOC_DENEGADA, + SITUACAO_EDOC_REJEITADA, + SITUACAO_FISCAL_CANCELADO, + SITUACAO_FISCAL_CANCELADO_EXTEMPORANEO, +) +from odoo.addons.spec_driven_model.models import spec_models + +from ..constants.modal import CTE_MODAL_VERSION_DEFAULT + +_logger = logging.getLogger(__name__) +try: + pass +except ImportError: + _logger.error("Biblioteca erpbrasil.base não in stalada") + + +def filter_processador_edoc_cte(record): + if record.processador_edoc == "oca" and record.document_type_id.code in [ + "57", + "67", + ]: + return True + return False + + +class CTe(spec_models.StackedModel): + + _name = "l10n_br_fiscal.document" + _inherit = ["l10n_br_fiscal.document", "cte.40.tcte_infcte", "cte.40.tcte_fat"] + _stacked = "cte.40.tcte_infcte" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + _cte_search_keys = ["cte40_Id"] + + INFCTE_TREE = """ + > infCte + > + - res.partner + > res.company + > res.partner + > + > + - + - + > + - + - """ + + ########################## + # CT-e spec related fields + ########################## + + ########################## + # CT-e tag: infCte + ########################## + + cte40_versao = fields.Char(related="document_version") + + cte40_Id = fields.Char( + compute="_compute_cte40_Id", + inverse="_inverse_cte40_Id", + ) + + ########################## + # CT-e tag: Id + # Methods + ########################## + + @api.depends("document_type_id", "document_key") + def _compute_cte40_Id(self): + for record in self.filtered(filter_processador_edoc_cte): + if ( + record.document_type_id + and record.document_type_id.prefix + and record.document_key + ): + record.cte40_Id = "{}{}".format( + record.document_type_id.prefix, record.document_key + ) + else: + record.cte40_Id = False + + def _inverse_cte40_Id(self): + for record in self: + if record.cte40_Id: + record.document_key = re.findall(r"\d+", str(record.cte40_Id))[0] + + ########################## + # CT-e tag: ide + ########################## + + cte40_cUF = fields.Char( + related="company_id.partner_id.state_id.ibge_code", + string="cte40_cUF", + ) + + cte40_cCT = fields.Char(compute="_compute_cct") + + cfop_id = fields.Many2one( + comodel_name="l10n_br_fiscal.cfop", + string="CFOP", + ) + + cte40_CFOP = fields.Char(related="cfop_id.code") + + cte40_natOp = fields.Char(related="operation_name") + + cte40_mod = fields.Char(related="document_type_id.code", string="cte40_mod") + + cte40_serie = fields.Char(related="document_serie") + + cte40_nCT = fields.Char(related="document_number") + + cte40_dhEmi = fields.Datetime(related="document_date") + + cte40_cDV = fields.Char(compute="_compute_cDV", store=True) + + cte40_procEmi = fields.Selection(default="0") + + cte40_verProc = fields.Char( + copy=False, + default=lambda s: s.env["ir.config_parameter"] + .sudo() + .get_param("l10n_br_cte.version.name", default="Odoo Brasil OCA v14"), + ) + + cte40_cMunEnv = fields.Char(compute="_compute_cte40_data", store=True) + + cte40_xMunEnv = fields.Char(compute="_compute_cte40_data", store=True) + + cte40_UFEnv = fields.Char( + compute="_compute_cte40_data", string="cte40_UFEnv", store=True + ) + + # cte40_indIEToma = fields.Char(related="partner_id.incr_est", store=True) + + cte40_cMunIni = fields.Char(compute="_compute_cte40_data", store=True) + + cte40_xMunIni = fields.Char(compute="_compute_cte40_data", store=True) + + cte40_UFIni = fields.Char(compute="_compute_cte40_data", store=True) + + cte40_cMunFim = fields.Char( + compute="_compute_cte40_data", + related="partner_id.city_id.ibge_code", + store=True, + ) + + cte40_xMunFim = fields.Char( + compute="_compute_cte40_data", related="partner_id.city_id.name", store=True + ) + + cte40_UFFim = fields.Char( + compute="_compute_cte40_data", string="cte40_cUF", store=True + ) + + cte40_retira = fields.Selection(selection=[("0", "Sim"), ("1", "Não")], default="1") + + cte40_tpServ = fields.Selection( + selection=[ + ("6", "Transporte de Pessoas"), + ("7", "Transporte de Valores"), + ("8", "Excesso de Bagagem"), + ], + default="6", + ) + + cte40_tpCTe = fields.Selection( + selection=[ + ("0", "CTe Normal"), + ("1", "CTe Complementar"), + ("3", "CTe Substituição"), + ], + default="0", + ) + + cte40_tpAmb = fields.Selection( + selection=[("1", "Produção"), ("2", "Homologação")], + string="CTe Environment", + copy=False, + default="2", + ) + + cte40_tpEmis = fields.Selection( + selection=[ + ("1", "Normal"), + ("3", "Regime Especial NFF"), + ("4", "EPEC pela SVC"), + ], + default="1", + ) + + cte40_tpImp = fields.Selection( + selection=[("1", "Retrato"), ("2", "Paisagem")], default="1" + ) + + def _export_fields_cte_40_toma3(self, xsd_fields, class_obj, export_dict): + if self.cte40_choice_toma == "cte40_toma4": + xsd_fields.remove("cte40_toma") + + def _export_fields_cte_40_tcte_toma4(self, xsd_fields, class_obj, export_dict): + if self.cte40_choice_toma == "cte40_toma3": + xsd_fields.remove("cte40_toma") + xsd_fields.remove("cte40_CNPJ") + xsd_fields.remove("cte40_CPF") + xsd_fields.remove("cte40_IE") + xsd_fields.remove("cte40_xNome") + xsd_fields.remove("cte40_xFant") + xsd_fields.remove("cte40_enderToma") + + # toma + cte40_choice_toma = fields.Selection( + selection=[ + ("cte40_toma3", "toma3"), + ("cte40_toma4", "toma4"), + ], + compute="_compute_toma", + store=True, + ) + + cte40_toma = fields.Selection(related="service_provider") + + cte40_CNPJ = fields.Char( + related="partner_id.cte40_CNPJ", + ) + cte40_CPF = fields.Char( + related="partner_id.cte40_CPF", + ) + cte40_IE = fields.Char( + related="partner_id.cte40_IE", + ) + cte40_xNome = fields.Char( + related="partner_id.legal_name", + ) + cte40_xFant = fields.Char( + related="partner_id.name", + ) + + cte40_enderToma = fields.Many2one(comodel_name="res.partner", related="partner_id") + + ########################## + # CT-e tag: ide + # Compute Methods + ########################## + + @api.depends("service_provider") + def _compute_toma(self): + for doc in self: + if doc.service_provider in ["0", "1", "2", "3"]: + doc.cte40_choice_toma = "cte40_toma3" + else: + doc.cte40_choice_toma = "cte40_toma4" + + def _compute_cDV(self): + for rec in self: + if rec.document_key: + rec.cte40_cDV = rec.document_key[:-1] + + def _compute_cct(self): + for rec in self: + if rec.document_key: + rec.cte40_cCT = rec.document_key[35:43] + + @api.depends("partner_id", "company_id") + def _compute_cte40_data(self): + for doc in self: + if doc.company_id.partner_id.country_id == doc.partner_id.country_id: + doc.cte40_xMunIni = doc.company_id.partner_id.city_id.name + doc.cte40_cMunIni = doc.company_id.partner_id.city_id.ibge_code + doc.cte40_xMunEnv = doc.company_id.partner_id.city_id.name + doc.cte40_cMunEnv = doc.company_id.partner_id.city_id.ibge_code + doc.cte40_UFEnv = doc.company_id.partner_id.state_id.code + doc.cte40_UFIni = doc.company_id.partner_id.state_id.ibge_code + doc.cte40_cMunFim = doc.partner_id.city_id.ibge_code + doc.cte40_xMunFim = doc.partner_id.city_id.name + doc.cte40_UFFim = doc.partner_id.state_id.code + else: + doc.cte40_UFIni = "EX" + doc.cte40_UFEnv = "EX" + doc.cte40_xMunIni = "EXTERIOR" + doc.cte40_cMunIni = "9999999" + doc.cte40_xMunEnv = ( + doc.company_id.partner_id.country_id.name + + "/" + + doc.company_id.partner_id.city_id.name + ) + doc.cte40_cMunEnv = "9999999" + doc.cte40_cMunFim = "9999999" + doc.cte40_xMunFim = "EXTERIOR" + doc.cte40_UFFim = "EX" + + ########################## + # CT-e tag: emit + ########################## + + cte40_emit = fields.Many2one( + comodel_name="res.company", + compute="_compute_emit_data", + readonly=True, + string="Emit", + ) + + cte40_CRT = fields.Selection( + related="company_tax_framework", + string="Código de Regime Tributário (NFe)", + ) + + ########################## + # CT-e tag: emit + # Compute Methods + ########################## + + def _compute_emit_data(self): + for doc in self: # TODO if out + doc.cte40_emit = doc.company_id + + ########################## + # CT-e tag: rem + ########################## + + cte40_rem = fields.Many2one( + comodel_name="res.partner", + compute="_compute_rem_data", + readonly=True, + string="Rem", + ) + + ########################## + # CT-e tag: rem + # Compute Methods + ########################## + + def _compute_rem_data(self): + for doc in self: # TODO if out + doc.cte40_rem = doc.partner_id + + ########################## + # CT-e tag: exped + ########################## + + cte40_exped = fields.Many2one( + comodel_name="res.company", + compute="_compute_exped_data", + readonly=True, + string="Exped", + ) + + ########################## + # CT-e tag: exped + # Compute Methods + ########################## + + def _compute_exped_data(self): + for doc in self: # TODO if out + doc.cte40_exped = doc.company_id + + ########################## + # CT-e tag: dest + ########################## + + cte40_dest = fields.Many2one( + comodel_name="res.partner", + compute="_compute_dest_data", + readonly=True, + string="Dest", + ) + + ########################## + # CT-e tag: dest + # Compute Methods + ########################## + + def _compute_dest_data(self): + for doc in self: # TODO if out + doc.cte40_dest = doc.partner_shipping_id + + ########################## + # CT-e tag: imp TODO + ########################## + + cte40_imp = fields.One2many( + comodel_name="l10n_br_fiscal.document.line", + inverse_name="document_id", + related="fiscal_line_ids", + ) + + ########################## + # CT-e tag: imp + # Compute Methods + ########################## + + def _compute_imp(self): + for doc in self: + doc.cte40_ICMS = doc.fiscal_line_ids + + ##################################### + # CT-e tag: infCTeNorm and infCteComp + ##################################### + + cte40_choice_infcteNorm_infcteComp = fields.Selection( + selection=[ + ("cte40_infCTeComp", "infCTeComp"), + ("cte40_infCTeNorm", "infCTeNorm"), + ], + default="cte40_infCTeNorm", + ) + + cte40_infCTeNorm = fields.One2many( + comodel_name="l10n_br_cte.normal.infos", + inverse_name="document_id", + ) + + # cte40_infCTeComp = fields.One2many( + # comodel_name="l10n_br_fiscal.document.related", + # inverse_name="document_id", + # ) + + ########################## + # CT-e tag: infCarga + ########################## + + cte40_vCarga = fields.Monetary( + string="Valor total da carga", + ) + + cte40_proPred = fields.Char( + string="Produto predominante", + required=True, + ) + + cte40_xOutCat = fields.Char( + string="Outras características da carga", + ) + + cte40_infQ = fields.One2many( + comodel_name="l10n_br_cte.cargo.quantity.infos", + inverse_name="document_id", + ) + + cte40_vCargaAverb = fields.Monetary( + string="Valor da Carga para efeito de averbação", + ) + + ########################## + # CT-e tag: veicNovos + ########################## + + cte40_veicNovos = fields.One2many( + comodel_name="l10n_br_cte.transported.vehicles", + inverse_name="document_id", + ) + + ########################## + # CT-e tag: autXML + # Compute Methods + ########################## + + def _default_cte40_autxml(self): + company = self.env.company + authorized_partners = [] + if company.accountant_id: + authorized_partners.append(company.accountant_id.id) + if company.technical_support_id: + authorized_partners.append(company.technical_support_id.id) + return authorized_partners + + ########################## + # CT-e tag: autXML + ########################## + + cte40_autXML = fields.One2many(default=_default_cte40_autxml) + + ########################## + # NF-e tag: infCTeSupl + ########################## + + cte40_infCTeSupl = fields.Many2one( + comodel_name="l10n_br_fiscal.document.supplement", + ) + + ########################## + # MDF-e tag: infRespTec + ########################## + + cte40_infRespTec = fields.Many2one( + comodel_name="res.partner", + compute="_compute_infresptec", + string="Responsável Técnico CTe", + ) + + ########################## + # MDF-e tag: infRespTec + # Methods + ########################## + + @api.depends("company_id.technical_support_id") + def _compute_infresptec(self): + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_infRespTec = record.company_id.technical_support_id + + ########################## + # CT-e tag: infmodal + ########################## + + cte40_modal = fields.Selection(related="transport_modal") + + cte40_versaoModal = fields.Char(default=CTE_MODAL_VERSION_DEFAULT) + + # Campos do Modal Aereo + modal_aereo_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aereo") + + cte40_nMinu = fields.Char( + string="Número da Minuta", + help=( + "Número da Minuta\nDocumento que precede o CT-e, assinado pelo " + "expedidor, espécie de pedido de serviço" + ), + ) + + cte40_nOCA = fields.Char( + string="Número Operacional do Conhecimento Aéreo", + help=( + "Número Operacional do Conhecimento Aéreo\nRepresenta o número de " + "controle comumente utilizado pelo conhecimento aéreo composto por" + " uma sequência numérica de onze dígitos. Os três primeiros " + "dígitos representam um código que os operadores de transporte " + "aéreo associados à IATA possuem. Em seguida um número de série de" + " sete dígitos determinados pelo operador de transporte aéreo. " + "Para finalizar, um dígito verificador, que é um sistema de módulo" + " sete imponderado o qual divide o número de série do conhecimento" + " aéreo por sete e usa o resto como dígito de verificação." + ), + ) + + cte40_dPrevAereo = fields.Date( + string="Data prevista da entrega", + help="Data prevista da entrega\nFormato AAAA-MM-DD", + ) + + cte40_xDime = fields.Char( + string="Dimensão", + help=( + "Dimensão\nFormato:1234X1234X1234 (cm). Esse campo deve sempre que" + " possível ser preenchido. Entretanto, quando for impossível o " + "preenchimento das dimensões, fica obrigatório o preenchimento da " + "cubagem em metro cúbico do leiaute do CT-e da estrutura genérica " + "(infQ)." + ), + ) + + cte40_CL = fields.Char( + string="Classe", + help=( + "Classe\nPreencher com:\n\t\t\t\t\t\t\t\t\tM - Tarifa " + "Mínima;\n\t\t\t\t\t\t\t\t\tG - Tarifa Geral;\n\t\t\t\t\t\t\t\t\tE" + " - Tarifa Específica" + ), + ) + + cte40_cTar = fields.Char( + string="Código da Tarifa", + help=( + "Código da Tarifa\nDeverão ser incluídos os códigos de três " + "dígitos, correspondentes à tarifa." + ), + ) + # Existem dois vTar no spec, um float e um monetary, por isso a mudança de nome + cte40_aereo_vTar = fields.Monetary( + string="Valor da Tarifa", + currency_field="brl_currency_id", + help="Valor da Tarifa\nValor da tarifa por kg quando for o caso.", + ) + + cte40_peri = fields.One2many( + comodel_name="l10n_br_cte.modal.aereo.peri", + inverse_name="document_id", + string="Dados de carga perigosa", + ) + + # Campos do Modal Aquaviario + modal_aquaviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aquav") + + cte40_vPrest = fields.Monetary( + compute="_compute_cte40_vPrest", # FIX + store=True, + string="Valor da Prestação Base de Cálculo", + ) + + cte40_vAFRMM = fields.Monetary( + string="AFRMM", + currency_field="brl_currency_id", + help=("AFRMM (Adicional de Frete para Renovação da Marinha Mercante)"), + ) + + cte40_xNavio = fields.Char(string="Identificação do Navio") + + cte40_nViag = fields.Char(string="Número da Viagem") + + cte40_direc = fields.Selection( + selection=[ + ("N", "Norte, L-Leste, S-Sul, O-Oeste"), + ("S", "Sul, O-Oeste"), + ("L", "Leste, S-Sul, O-Oeste"), + ("O", "Oeste"), + ], + string="Direção", + help="Direção\nPreencher com: N-Norte, L-Leste, S-Sul, O-Oeste", + ) + + cte40_irin = fields.Char( + string="Irin do navio", + help="Irin do navio sempre deverá ser informado", + ) + + cte40_tpNav = fields.Selection( + selection=[ + ("0", "Interior"), + ("1", "Cabotagem"), + ], + string="Tipo de Navegação", + help=( + "Tipo de Navegação\nPreencher com: \n\t\t\t\t\t\t0 - " + "Interior;\n\t\t\t\t\t\t1 - Cabotagem" + ), + ) + + cte40_balsa = fields.One2many( + comodel_name="l10n_br_cte.modal.aquav.balsa", + inverse_name="document_id", + string="Grupo de informações das balsas", + ) + + # Campos do Modal Dutoviario + modal_dutoviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.duto") + + cte40_dIni = fields.Date(string="Data de Início da prestação do serviço") + + cte40_dFim = fields.Date(string="Data de Fim da prestação do serviço") + + cte40_vTar = fields.Float(string="Valor da tarifa") + + # Campos do Modal Ferroviario + modal_ferroviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.ferrov") + + cte40_tpTraf = fields.Selection( + selection=FERROV_TPTRAF, + default="0", + string="Tipo de Tráfego", + ) + + cte40_fluxo = fields.Char( + string="Fluxo Ferroviário", + help=( + "Fluxo Ferroviário\nTrata-se de um número identificador do " + "contrato firmado com o cliente" + ), + ) + + cte40_vFrete = fields.Monetary( + related="amount_freight_value", + string="Valor do Frete do Tráfego Mútuo", + currency_field="brl_currency_id", + ) + + cte40_respFat = fields.Selection( + TRAFMUT_RESPFAT, + string="Responsável pelo Faturamento", + ) + + cte40_ferrEmi = fields.Selection( + TRAFMUT_FERREMI, + string="Ferrovia Emitente do CTe", + help=( + "Ferrovia Emitente do CTe\nPreencher com: " + "\n\t\t\t\t\t\t\t\t\t1-Ferrovia de origem; " + "\n\t\t\t\t\t\t\t\t\t2-Ferrovia de destino" + ), + ) + + cte40_chCTeFerroOrigem = fields.Char( + string="Chave de acesso do CT-e emitido", + help="Chave de acesso do CT-e emitido pelo ferrovia de origem", + ) + + cte40_ferroEnv = fields.Many2many( + comodel_name="res.partner", + string="Informações das Ferrovias Envolvidas", + ) + + # Campos do Modal rodoviario + modal_rodoviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.rodo") + + cte40_RNTRC = fields.Char( + string="RNTRC", + help="Registro Nacional de Transportadores Rodoviários de Carga", + ) + + cte40_occ = fields.One2many( + comodel_name="l10n_br_cte.modal.rodo.occ", + inverse_name="document_id", + string="Ordens de Coleta associados", + ) + + ########################## + # CT-e tag: infmodal + # Compute Methods + ########################## + + def _compute_cte40_vPrest(self): + vPrest = 0 + for record in self.fiscal_line_ids: + vPrest += record.cte40_vTPrest + self.cte40_vPrest = vPrest + + def _export_fields_cte_40_tcte_infmodal(self, xsd_fields, class_obj, export_dict): + self = self.with_context(module="l10n_br_cte") + if self.cte40_modal == "1": + export_dict["any_element"] = self._export_modal_rodoviario() + elif self.cte40_modal == "2": + export_dict["any_element"] = self._export_modal_aereo() + elif self.cte40_modal == "3": + export_dict["any_element"] = self._export_modal_aquaviario() + elif self.cte40_modal == "4": + export_dict["any_element"] = self._export_modal_ferroviario() + elif self.cte40_modal == "5": + export_dict["any_element"] = self._export_modal_dutoviario() + + def _export_modal_aereo(self): + if not self.modal_aereo_id: + self.modal_aereo_id = self.modal_aereo_id.create({"document_id": self.id}) + + return self.modal_aereo_id.export_ds()[0] + + def _export_modal_ferroviario(self): + if not self.modal_ferroviario_id: + self.modal_ferroviario_id = self.modal_ferroviario_id.create( + {"document_id": self.id} + ) + + return self.modal_ferroviario_id.export_ds()[0] + + def _export_modal_aquaviario(self): + if not self.modal_aquaviario_id: + self.modal_aquaviario_id = self.modal_aquaviario_id.create( + {"document_id": self.id} + ) + + return self.modal_aquaviario_id.export_ds()[0] + + def _export_modal_rodoviario(self): + if not self.modal_rodoviario_id: + self.modal_rodoviario_id = self.modal_rodoviario_id.create( + {"document_id": self.id} + ) + + return self.modal_rodoviario_id.export_ds()[0] + + def _export_modal_dutoviario(self): + if not self.modal_dutoviario_id: + self.modal_dutoviario_id = self.modal_dutoviario_id.create( + {"document_id": self.id} + ) + + return self.modal_dutoviario_id.export_ds()[0] + + ################################ + # Business Model Methods + ################################ + + def _serialize(self, edocs): + edocs = super()._serialize(edocs) + for record in self.with_context(lang="pt_BR").filtered( + filter_processador_edoc_cte + ): + inf_cte = record.export_ds()[0] + cte = Cte(infCte=inf_cte, infCTeSupl=None, signature=None) + edocs.append(cte) + return edocs + + def _processador(self): + if not self.company_id.certificate_nfe_id: + raise UserError(_("Certificado não encontrado")) + + certificado = cert.Certificado( + arquivo=self.company_id.certificate_nfe_id.file, + senha=self.company_id.certificate_nfe_id.password, + ) + session = Session() + session.verify = False + transmissao = TransmissaoSOAP(certificado, session) + return edoc_cte( + transmissao, + self.company_id.state_id.id, + self.cte40_versao, + self.cte40_tpAmb, + ) + + def _document_export(self, pretty_print=True): + result = super()._document_export() + for record in self.filtered(filter_processador_edoc_cte): + edoc = record.serialize()[0] + processador = record._processador() + xml_file = edoc.to_xml() + event_id = self.event_ids.create_event_save_xml( + company_id=self.company_id, + environment=( + EVENT_ENV_PROD if self.cte40_tpAmb == "1" else EVENT_ENV_HML + ), + event_type="0", + xml_file=xml_file, + document_id=self, + ) + record.authorization_event_id = event_id + xml_assinado = processador.assina_raiz(edoc, edoc.infCte.Id) + self._valida_xml(xml_assinado) + return result + + def _valida_xml(self, xml_file): + self.ensure_one() + erros = Cte.schema_validation(xml_file) + erros = "\n".join(erros) + self.write({"xml_error_message": erros or False}) + + def atualiza_status_cte(self, infProt, xml_file): + self.ensure_one() + 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=xml_file, + ) + self.write( + { + "status_code": infProt.cStat, + "status_name": infProt.xMotivo, + } + ) + self._change_state(state) + + def _eletronic_document_send(self): + super(CTe, self)._eletronic_document_send() + for record in self.filtered(filter_processador_edoc_cte): + if record.xml_error_message: + return + processador = record._processador() + for edoc in record.serialize(): + processo = None + for p in processador.processar_documento(edoc): + processo = p + if processo.webservice == "cteRecepcaoLote": + record.authorization_event_id._save_event_file( + processo.envio_xml, "xml" + ) + + if processo.resposta.cStat in LOTE_PROCESSADO + ["100"]: + record.atualiza_status_cte(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 _document_cancel(self, justificative): + result = super(CTe, self)._document_cancel(justificative) + online_event = self.filtered(filter_processador_edoc_cte) + if online_event: + online_event._cte_cancel() + return result + + def _cte_cancel(self): + self.ensure_one() + processador = self._processador() + + if not self.authorization_protocol: + raise UserError(_("Authorization Protocol Not Found!")) + + evento = processador.cancela_documento( + chave=self.document_key, + protocolo_autorizacao=self.authorization_protocol, + justificativa=self.cancel_reason.replace("\n", "\\n"), + ) + processo = processador.enviar_lote_evento(lista_eventos=[evento]) + + self.cancel_event_id = self.event_ids.create_event_save_xml( + company_id=self.company_id, + environment=( + EVENT_ENV_PROD if self.cte_environment == "1" else EVENT_ENV_HML + ), + event_type="2", + xml_file=processo.envio_xml.decode("utf-8"), + document_id=self, + ) + + for retevento in processo.resposta.retEvento: + if not retevento.infEvento.chCte == self.document_key: + continue + + if retevento.infEvento.cStat not in CANCELADO: + mensagem = "Erro no cancelamento" + mensagem += "\nCódigo: " + retevento.infEvento.cStat + mensagem += "\nMotivo: " + retevento.infEvento.xMotivo + raise UserError(mensagem) + + if retevento.infEvento.cStat == CANCELADO_FORA_PRAZO: + self.state_fiscal = SITUACAO_FISCAL_CANCELADO_EXTEMPORANEO + elif retevento.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=retevento.infEvento.cStat, + response=retevento.infEvento.xMotivo, + protocol_date=fields.Datetime.to_string( + datetime.fromisoformat(retevento.infEvento.dhRegEvento) + ), + protocol_number=retevento.infEvento.nProt, + file_response_xml=processo.retorno.content.decode("utf-8"), + ) diff --git a/l10n_br_cte/models/document_cargo_quantity_infos.py b/l10n_br_cte/models/document_cargo_quantity_infos.py new file mode 100644 index 000000000000..44ea62fba70b --- /dev/null +++ b/l10n_br_cte/models/document_cargo_quantity_infos.py @@ -0,0 +1,27 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeCargoQuantityInfos(spec_models.SpecModel): + _name = "l10n_br_cte.cargo.quantity.infos" + _inherit = "cte.40.tcte_infq" + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + _description = "Informações de quantidades da Carga do CT-e" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_cUnid = fields.Selection( + required=True, + ) + + cte40_tpMed = fields.Char( + required=True, + ) + + cte40_qCarga = fields.Float( + required=True, + ) diff --git a/l10n_br_cte/models/document_line.py b/l10n_br_cte/models/document_line.py new file mode 100644 index 000000000000..22d2393668cc --- /dev/null +++ b/l10n_br_cte/models/document_line.py @@ -0,0 +1,216 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import sys + +from odoo import api, fields + +from odoo.addons.l10n_br_fiscal.constants.icms import ICMS_CST, ICMS_SN_CST +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeLine(spec_models.StackedModel): + _name = "l10n_br_fiscal.document.line" + _inherit = ["l10n_br_fiscal.document.line", "cte.40.tcte_imp"] + _stacked = "cte.40.tcte_imp" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + _spec_tab_name = "CTe" + _stacking_points = {} + _force_stack_paths = "tcte_imp.timp" + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + + ########################## + # CT-e tag: vPrest + ########################## + + cte40_vTPrest = fields.Monetary(string="vTPrest", related="amount_total") + + cte40_vRec = fields.Monetary( + related="price_gross", + string="vRec", + ) + + ################################################## + # CT-e tag: ICMS + # Grupo N01. Grupo Tributação do ICMS= 00 + # Grupo N02. Grupo Tributação do ICMS= 20 + # Grupo N03. Grupo Tributação do ICMS= 45 (40, 41 e 51) + # Grupo N04. Grupo Tributação do ICMS= 60 + # Grupo N05. Grupo Tributação do ICMS= 90 - ICMS outros + # Grupo N06. Grupo Tributação do ICMS= 90 - ICMS Outra UF + # Grupo N06. Grupo Tributação do ICMS= 01 - ISSN + ################################################# + + cte40_ICMS = fields.Many2one( + comodel_name="l10n_br_fiscal.document.line", compute="_compute_icms", store=True + ) + + def _compute_icms(self): + for doc in self: + doc.cte40_ICMS = doc + + cte40_choice_icms = fields.Selection( + selection=[ + ("cte40_ICMS00", "ICMS00"), + ("cte40_ICMS20", "ICMS20"), + ("cte40_ICMS45", "ICMS45"), + ("cte40_ICMS60", "ICMS60"), + ("cte40_ICMS90", "ICMS90"), + ("cte40_ICMSOutraUF", "ICMSOutraUF"), + ("cte40_ICMSSN", "ICMSSN"), + ], + string="Tipo de ICMS", + compute="_compute_choice_icms", + store=True, + ) + + cte40_CST = fields.Selection( + selection=[ + ("00", "00 - Tributação normal ICMS"), + ("20", "20 - Tributação com BC reduzida do ICMS"), + ("45", "45 - ICMS Isento, não Tributado ou diferido"), + ("60", "60 - ICMS cobrado por substituição tributária"), + ("90", "90 - ICMS outros"), + ("90", "90 - ICMS Outra UF"), + ("01", "01 - Simples Nacional"), + ], + string="Classificação Tributária do Serviço", + compute="_compute_choice_icms", + store=True, + ) + + cte40_vTotTrib = fields.Monetary(related="estimate_tax") + + cte40_pICMS = fields.Float(related="icms_percent", string="pICMS") + + cte40_vICMS = fields.Monetary(related="icms_value") + + # ICMS20 - ICMS90 + cte40_pRedBC = fields.Float( + related="icms_reduction", + ) + + cte40_vBC = fields.Monetary(related="icms_base") + + # ICMS60 + cte40_vBCSTRet = fields.Monetary(related="icmsst_wh_base") + + cte40_vICMSSTRet = fields.Monetary(related="icmsst_wh_value") + + # TODO cte40_pICMSTRet = fields.Monetary(related="") + + # ICMSSN + cte40_indSN = fields.Float(default=1) + + # ICMS NF + cte40_vBCST = fields.Monetary(related="icmsst_base") + + # ICMSOutraUF + # TODO + + ########################## + # CT-e tag: ICMS + # Compute Methods + ########################## + + @api.depends("icms_cst_id") + def _compute_choice_icms(self): + for record in self: + record.cte40_choice_icms = None + record.cte40_CST = None + if record.icms_cst_id.code in ICMS_CST: + if record.icms_cst_id.code in ["40", "41", "50"]: + record.cte40_choice_icms = "cte40_ICMS45" + record.cte40_CST = "45" + elif ( + record.icms_cst_id.code == "90" + and self.partner_id.state_id != self.company_id.state_id + ): + record.cte40_choice_icms = "cte40_ICMSOutraUF" + else: + record.cte40_choice_icms = "{}{}".format( + "cte40_ICMS", record.icms_cst_id.code + ) + record.cte40_CST = record.icms_cst_id.code + elif record.icms_cst_id.code in ICMS_SN_CST: + record.cte40_choice_icms = "cte40_ICMSSN" + record.cte40_CST = "90" + + def _export_fields_icms(self): + icms = { + "CST": self.cte40_CST, + "vBC": str("%.02f" % self.icms_base), + "pRedBC": str("%.04f" % self.icms_reduction), + "pICMS": str("%.04f" % self.icms_percent), + "vICMS": str("%.02f" % self.icms_value), + "vICMSSubstituto": str("%.02f" % self.icms_substitute), + "indSN": int(self.cte40_indSN), + "vBCSTRet": str("%.02f" % self.icmsst_wh_base), + "vICMSSTRet": str("%.02f" % self.icmsst_wh_value), + "pICMSSTRet": str("%.02f" % self.icmsst_wh_percent), + } + return icms + + def _export_fields_cte_40_timp(self, xsd_fields, class_obj, export_dict): + # TODO Not Implemented + if "cte40_ICMSOutraUF" in xsd_fields: + xsd_fields.remove("cte40_ICMSOutraUF") + + xsd_fields = [self.cte40_choice_icms] + icms_tag = ( + self.cte40_choice_icms.replace("cte40_", "") + .replace("ICMSSN", "Icmssn") + .replace("ICMS", "Icms") + ) + binding_module = sys.modules[self._binding_module] + icms = binding_module.Timp + icms_binding = getattr(icms, icms_tag) + icms_dict = self._export_fields_icms() + sliced_icms_dict = { + key: icms_dict.get(key) + for key in icms_binding.__dataclass_fields__.keys() + if icms_dict.get(key) + } + export_dict[icms_tag.upper()] = icms_binding(**sliced_icms_dict) + + ########################## + # CT-e tag: ICMSUFFim + ########################## + + cte40_vBCUFFim = fields.Monetary(related="icms_destination_base") + cte40_pFCPUFFim = fields.Monetary(compute="_compute_cte40_ICMSUFFim", store=True) + cte40_pICMSUFFim = fields.Monetary(compute="_compute_cte40_ICMSUFFim", store=True) + # TODO + # cte40_pICMSInter = fields.Selection( + # selection=[("0", "Teste")], + # compute="_compute_cte40_ICMSUFFim") + + def _compute_cte40_ICMSUFFim(self): + for record in self: + # if record.icms_origin_percent: + # record.cte40_pICMSInter = str("%.02f" % record.icms_origin_percent) + # else: + # record.cte40_pICMSInter = False + + record.cte40_pFCPUFFim = record.icmsfcp_percent + record.cte40_pICMSUFFim = record.icms_destination_percent + + cte40_vFCPUFfim = fields.Monetary(related="icmsfcp_value") + cte40_vICMSUFFim = fields.Monetary(related="icms_destination_value") + cte40_vICMSUFIni = fields.Monetary(related="icms_origin_value") + + ########################## + # CT-e tag: natCarga + ########################## + + cte40_xDime = fields.Char(compute="_compute_dime", store=True) + + def _compute_dime(self): + for record in self: + for package in record.product_id.packaging_ids: + record.cte40_xDime = ( + package.width + "X" + package.packaging_length + "X" + package.width + ) diff --git a/l10n_br_cte/models/document_related.py b/l10n_br_cte/models/document_related.py new file mode 100644 index 000000000000..f41e364f7686 --- /dev/null +++ b/l10n_br_cte/models/document_related.py @@ -0,0 +1,128 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeRelated(spec_models.StackedModel): + + _name = "l10n_br_fiscal.document.related" + _inherit = [ + "l10n_br_fiscal.document.related", + "cte.40.tcte_infnfe", + "cte.40.tcte_infnf", + "cte.40.tcte_infq", + ] + _stacked = "cte.40.tcte_infnfe" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + _spec_tab_name = "CTe" + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + + # infQ TODO computes/relateds + + cte40_tpMed = fields.Char() + + cte40_qCarga = fields.Float() + + cte40_cUnid = fields.Selection( + selection=[ + ("00", "M3"), + ("01", "KG"), + ("02", "TON"), + ("03", "UNIDADE"), + ("04", "LITROS"), + ("05", "MMBTU"), + ], + ) + + # infCarga + cte40_prodPred = fields.Char(string="prodPred") + + cte40_vCarga = fields.Monetary( + currency_field="currency_id", compute="_compute_vCarga", store=True + ) + + currency_id = fields.Many2one( + comodel_name="res.currency", related="company_id.currency_id", readonly=True + ) + + company_id = fields.Many2one( + comodel_name="res.company", + default=lambda self: self.env.company, + ) + + # InfNFe + cte40_chave = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_chave", + ) + + cte40_tpDoc = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_tpDoc", + ) + + cte40_infDoc = fields.Selection(related="cte40_choice_infNF_infNFE_infOutros") + + # infCteNorm + cte40_chCTe = fields.Char(compute="_compute_chCte", string="chCte") + + ########################## + # CT-e tag: infCTeComp + # Compute Methods + ########################## + + def _compute_chCTe(self): + records = "" + for rec in self: + if rec.cte40_Id: + records += rec.document_key + self.cte40_chCTe = records + + cte40_choice_infNF_infNFE_infOutros = fields.Selection( + selection=[ + ("cte40_infNF", "infNF"), # TODO + ("cte40_infNFe", "infNFe"), + ("cte40_infOutros", "Outros"), + ], + compute="_compute_cte_data", + inverse="_inverse_cte40_choice_infNF_infNFE_infOutros", + ) + + def _compute_vCarga(self): + for rec in self: + if rec.document_related_id: + rec.cte40_vCarga += rec.document_related_id.amount_price_gross + + @api.depends("document_type_id") + def _compute_cte_data(self): + """Set schema data which are not just related fields""" + for rec in self: + if rec.document_type_id: + if rec.document_type_id.code in ("55",): + rec.cte40_choice_infNF_infNFE_infOutros = "cte40_infNFe" + rec.cte40_chave = rec.document_key + elif rec.document_type_id.code in ("00", "10", "59", "65", "99"): + rec.cte40_choice_infNF_infNFE_infOutros = "cte40_infOutros" + rec.cte40_tpDoc = rec.document_type_id.code + + def _inverse_cte40_chave(self): + for rec in self: + if rec.cte40_chave: + rec.document_key = rec.cte40_chave + + def _inverse_cte40_tpDoc(self): + for rec in self: + if rec.cte40_tpDoc: + rec.document_type_id = rec.cte40_tpDoc + + def _inverse_cte40_choice_infNF_infNFE_infOutros(self): + for rec in self: + if rec.cte40_choice_infNF_infNFE_infOutros == "cte40_infNFe": + rec.document_type_id = self.env.ref("l10n_br_fiscal.document_55") diff --git a/l10n_br_cte/models/document_supplement.py b/l10n_br_cte/models/document_supplement.py new file mode 100644 index 000000000000..d4202faf2e78 --- /dev/null +++ b/l10n_br_cte/models/document_supplement.py @@ -0,0 +1,22 @@ +# Copyright 2023 KMEE (Luiz Felipe do Divino ) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeSupplement(spec_models.StackedModel): + _name = "l10n_br_fiscal.document.supplement" + _inherit = ["l10n_br_fiscal.document.supplement", "cte.40.tcte_infctesupl"] + _stacked = "cte.40.tcte_infctesupl" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + _field_prefix = "cte40_" + _spec_tab_name = "CTe" + _description = "Informações Complementares do Documento Fiscal" + + cte40_qrCodCTe = fields.Char(related="qrcode") diff --git a/l10n_br_cte/models/document_transported_vehicles.py b/l10n_br_cte/models/document_transported_vehicles.py new file mode 100644 index 000000000000..bbb6af6bfd07 --- /dev/null +++ b/l10n_br_cte/models/document_transported_vehicles.py @@ -0,0 +1,43 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeCargoQuantityInfos(spec_models.SpecModel): + _name = "l10n_br_cte.transported.vehicles" + _inherit = "cte.40.veicnovos" + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + _description = "Informações dos veículos transportados" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + currency_id = fields.Many2one( + comodel_name="res.currency", + related="document_id.company_id.currency_id", + ) + + cte40_chassi = fields.Char(string="Chassi do veículo", required=True, size=17) + + cte40_cCor = fields.Char(string="Cor do veículo", required=True, size=4) + + cte40_xCor = fields.Char(string="Descrição da cor", required=True) + + cte40_cMod = fields.Char( + string="Código Marca Modelo", + required=True, + ) + + cte40_vUnit = fields.Monetary( + string="Valor Unitário do Veículo", + required=True, + currency_field="currency_id", + ) + + cte40_vFrete = fields.Monetary( + string="Frete Unitário", + required=True, + currency_field="currency_id", + ) diff --git a/l10n_br_cte/models/dutoviario.py b/l10n_br_cte/models/dutoviario.py new file mode 100644 index 000000000000..8d980571c62c --- /dev/null +++ b/l10n_br_cte/models/dutoviario.py @@ -0,0 +1,25 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class Duto(spec_models.SpecModel): + _name = "l10n_br_cte.modal.duto" + _inherit = "cte.40.duto" + _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_dutoviario_v4_00" + _description = "Modal Dutoviario CTe" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_dIni = fields.Date(related="document_id.cte40_dIni") + + cte40_dFim = fields.Date(related="document_id.cte40_dFim") + + cte40_vTar = fields.Float(related="document_id.cte40_vTar") + + def _prepare_dacte_values(self): + if not self: + return {} diff --git a/l10n_br_cte/models/ferroviario.py b/l10n_br_cte/models/ferroviario.py new file mode 100644 index 000000000000..d18932706e1f --- /dev/null +++ b/l10n_br_cte/models/ferroviario.py @@ -0,0 +1,55 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class Ferrov(spec_models.StackedModel): + _name = "l10n_br_cte.modal.ferrov" + _inherit = "cte.40.ferrov" + _stacked = "cte.40.ferrov" + _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_ferroviario_v4_00" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_ferroviario_v4_00" + ) + _spec_tab_name = "CTe" + _description = "Modal Ferroviario CTe" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_tpTraf = fields.Selection(related="document_id.cte40_tpTraf") + + cte40_fluxo = fields.Char(related="document_id.cte40_fluxo") + + cte40_vFrete = fields.Monetary( + related="document_id.cte40_vFrete", currency_field="currency_id" + ) + + currency_id = fields.Many2one( + comodel_name="res.currency", + default=lambda self: self.env.company.currency_id, + ) + + cte40_chCTeFerroOrigem = fields.Char(related="document_id.cte40_chCTeFerroOrigem") + + cte40_respFat = fields.Selection(related="document_id.cte40_respFat") + + cte40_ferrEmi = fields.Selection(related="document_id.cte40_ferrEmi") + + cte40_ferroEnv = fields.One2many(compute="_compute_railroad") + + @api.depends("document_id.cte40_ferroEnv") + def _compute_railroad(self): + for record in self: + record.cte40_ferroEnv = [(6, 0, record.document_id.cte40_ferroEnv.ids)] + + def _prepare_dacte_values(self): + if not self: + return {} diff --git a/l10n_br_cte/models/normal_cte_infos.py b/l10n_br_cte/models/normal_cte_infos.py new file mode 100644 index 000000000000..59c470e866f8 --- /dev/null +++ b/l10n_br_cte/models/normal_cte_infos.py @@ -0,0 +1,153 @@ +# 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 CTeNormalInfos(spec_models.StackedModel): + _name = "l10n_br_cte.normal.infos" + _inherit = ["cte.40.tcte_infctenorm"] + _stacked = "cte.40.tcte_infctenorm" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + _spec_tab_name = "CTe" + _description = "Grupo de informações do CTe Normal e Substituto" + _force_stack_paths = "infctenorm.infdoc" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + currency_id = fields.Many2one( + comodel_name="res.currency", + related="document_id.company_id.currency_id", + ) + + cte40_vCarga = fields.Monetary( + related="document_id.cte40_vCarga", + currency_field="currency_id", + ) + + cte40_proPred = fields.Char( + related="document_id.cte40_proPred", + ) + + cte40_xOutCat = fields.Char( + related="document_id.cte40_xOutCat", + ) + + cte40_infQ = fields.One2many( + comodel_name="l10n_br_cte.cargo.quantity.infos", + related="document_id.cte40_infQ", + ) + + cte40_vCargaAverb = fields.Monetary( + related="document_id.cte40_vCargaAverb", + ) + + cte40_veicNovos = fields.One2many( + comodel_name="l10n_br_cte.transported.vehicles", + related="document_id.cte40_veicNovos", + ) + + cte40_infNFe = fields.One2many( + comodel_name="l10n_br_fiscal.document.related", + related="document_id.document_related_ids", + ) + + cte40_versaoModal = fields.Char(related="document_id.cte40_versaoModal") + + # Campos do Modal Aereo + modal_aereo_id = fields.Many2one( + comodel_name="l10n_br_cte.modal.aereo", related="document_id.modal_aereo_id" + ) + + cte40_nMinu = fields.Char(related="document_id.cte40_nMinu") + + cte40_nOCA = fields.Char(related="document_id.cte40_nOCA") + + cte40_dPrevAereo = fields.Date(related="document_id.cte40_dPrevAereo") + + cte40_xDime = fields.Char(related="document_id.cte40_xDime") + + cte40_CL = fields.Char(related="document_id.cte40_CL") + + cte40_cTar = fields.Char(related="document_id.cte40_cTar") + + # Existem dois vTar no spec, um float e um monetary, por isso a mudança de nome + cte40_aereo_vTar = fields.Monetary(related="document_id.cte40_aereo_vTar") + + cte40_peri = fields.One2many( + comodel_name="l10n_br_cte.modal.aereo.peri", related="document_id.cte40_peri" + ) + + # Campos do Modal Aquaviario + modal_aquaviario_id = fields.Many2one( + comodel_name="l10n_br_cte.modal.aquav", + related="document_id.modal_aquaviario_id", + ) + + cte40_vPrest = fields.Monetary(related="document_id.cte40_vPrest") + + cte40_vAFRMM = fields.Monetary(related="document_id.cte40_vAFRMM") + + cte40_xNavio = fields.Char(related="document_id.cte40_xNavio") + + cte40_nViag = fields.Char(related="document_id.cte40_nViag") + + cte40_direc = fields.Selection(related="document_id.cte40_direc") + + cte40_irin = fields.Char(related="document_id.cte40_irin") + + cte40_tpNav = fields.Selection(related="document_id.cte40_tpNav") + + cte40_balsa = fields.One2many( + comodel_name="l10n_br_cte.modal.aquav.balsa", + related="document_id.cte40_balsa", + ) + + # Campos do Modal Dutoviario + modal_dutoviario_id = fields.Many2one( + comodel_name="l10n_br_cte.modal.duto", + related="document_id.modal_dutoviario_id", + ) + + cte40_dIni = fields.Date(related="document_id.cte40_dIni") + + cte40_dFim = fields.Date(related="document_id.cte40_dFim") + + cte40_vTar = fields.Float(related="document_id.cte40_vTar") + + # Campos do Modal Ferroviario + modal_ferroviario_id = fields.Many2one( + comodel_name="l10n_br_cte.modal.ferrov", + related="document_id.modal_ferroviario_id", + ) + + cte40_tpTraf = fields.Selection(related="document_id.cte40_tpTraf") + + cte40_fluxo = fields.Char(related="document_id.cte40_fluxo") + + cte40_vFrete = fields.Monetary(related="document_id.cte40_vFrete") + + cte40_respFat = fields.Selection(related="document_id.cte40_respFat") + + cte40_ferrEmi = fields.Selection(related="document_id.cte40_ferrEmi") + + cte40_ferroEnv = fields.Many2many(related="document_id.cte40_ferroEnv") + + # Campos do Modal rodoviario + modal_rodoviario_id = fields.Many2one( + comodel_name="l10n_br_cte.modal.rodo", + related="document_id.modal_rodoviario_id", + ) + + cte40_RNTRC = fields.Char(related="document_id.cte40_RNTRC") + + cte40_occ = fields.One2many( + comodel_name="l10n_br_cte.modal.rodo.occ", related="document_id.cte40_occ" + ) diff --git a/l10n_br_cte/models/res_company.py b/l10n_br_cte/models/res_company.py new file mode 100644 index 000000000000..cc465aa6f043 --- /dev/null +++ b/l10n_br_cte/models/res_company.py @@ -0,0 +1,105 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class ResCompany(spec_models.SpecModel): + + _name = "res.company" + _inherit = ["res.company", "cte.40.tcte_emit", "cte.40.tendeemi", "cte.40.ferroenv"] + _cte_search_keys = ["cte40_CNPJ", "cte40_xNome", "cte40_xFant"] + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + _field_prefix = "cte40_" + + ########################## + # CT-e spec fields + ########################## + + cte40_CNPJ = fields.Char( + related="partner_id.cte40_CNPJ", + ) + cte40_CPF = fields.Char( + related="partner_id.cte40_CPF", + ) + cte40_IE = fields.Char( + related="partner_id.cte40_IE", + ) + cte40_xNome = fields.Char( + related="partner_id.legal_name", + ) + cte40_xFant = fields.Char( + related="partner_id.name", + ) + cte40_CRT = fields.Selection( + related="tax_framework", + ) + + cte40_enderEmit = fields.Many2one( + comodel_name="res.partner", + related="partner_id", + ) + + cte40_enderToma = fields.Many2one(comodel_name="res.partner", related="partner_id") + + ########################## + # CT-e models fields + ########################## + + cte_default_serie_id = fields.Many2one( + comodel_name="l10n_br_fiscal.document.serie", + string="CT-e Default Serie", + ) + + cte_dacte_layout = fields.Selection( + selection=[("1", "Paisagem"), ("2", "Retrato")], + string="CT-e DACTE Layout", + default="1", + ) + + cte_transmission = fields.Selection( + selection=[ + ("1", "Normal"), + ("2", "Regime Especial NFF"), + ("4", "EPEC pela SVC"), + ("5", "Contingência FSDA"), + ("7", "Contingência SVC-RS"), + ("8", "Contingência SVC-SP"), + ], + string="CT-e Transmission Type", + default="1", + ) + + cte_type = fields.Selection( + selection=[ + ("0", "CT-e Normal"), + ("1", "CT-e de Complemento de Valores"), + ("3", "CT-e de Substituição"), + ], + string="CT-e Type", + default="0", + ) + + cte_environment = fields.Selection( + selection=[("1", "Produção"), ("2", "Homologação")], + string="CT-e Environment", + default="2", + ) + + cte_version = fields.Selection( + selection=[("3.00", "3.00"), ("4.00", "4.00")], + string="CT-e Version", + default="4.00", + ) + + processador_edoc = fields.Selection( + selection_add=[("erpbrasil.edoc", "erpbrasil.edoc")], + ) + + cte_authorize_accountant_download_xml = fields.Boolean( + string="Include Accountant Partner data in persons authorized to " + "download CTe XML", + default=False, + ) diff --git a/l10n_br_cte/models/res_config_settings.py b/l10n_br_cte/models/res_config_settings.py new file mode 100644 index 000000000000..278b43cb80ac --- /dev/null +++ b/l10n_br_cte/models/res_config_settings.py @@ -0,0 +1,12 @@ +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + cte_authorize_accountant_download_xml = fields.Boolean( + string="Include Accountant Partner data in persons authorized to " + "download CTe XML", + related="company_id.cte_authorize_accountant_download_xml", + readonly=False, + ) diff --git a/l10n_br_cte/models/res_partner.py b/l10n_br_cte/models/res_partner.py new file mode 100644 index 000000000000..2d636ae6bf29 --- /dev/null +++ b/l10n_br_cte/models/res_partner.py @@ -0,0 +1,156 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging + +from odoo import api, fields + +from odoo.addons.spec_driven_model.models import spec_models + +_logger = logging.getLogger(__name__) + +try: + from erpbrasil.base.misc import format_zipcode, punctuation_rm +except ImportError: + _logger.error("Biblioteca erpbrasil.base não instalada") + + +class ResPartner(spec_models.SpecModel): + + _name = "res.partner" + _inherit = [ + "res.partner", + "cte.40.tendereco", + "cte.40.tlocal", + "cte.40.tendeemi", + "cte.40.tcte_dest", + "cte.40.tresptec", + "cte.40.tcte_autxml", + "cte.40.tenderfer", + ] + _cte_search_keys = ["cte40_CNPJ", "cte40_CPF", "cte40_xNome"] + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + _field_prefix = "cte40_" + + cte40_choice_cnpj_cpf = fields.Selection( + selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ/CPF do Parceiro", + ) + + cte40_CNPJ = fields.Char( + compute="_compute_cte_data", + store=True, + ) + + cte40_cInt = fields.Char( + string="Código interno da Ferrovia envolvida", + help="Código interno da Ferrovia envolvida\nUso da transportadora", + ) + + cte40_CPF = fields.Char( + compute="_compute_cte_data", + store=True, + ) + + # Same problem with Tendereco that NFE has, it has to use m2o fields + cte40_enderToma = fields.Many2one( + comodel_name="res.partner", compute="_compute_cte40_ender" + ) + + cte40_enderReme = fields.Many2one( + comodel_name="res.partner", compute="_compute_cte40_ender" + ) + + cte40_enderDest = fields.Many2one( + comodel_name="res.partner", compute="_compute_cte40_ender" + ) + + cte40_enderExped = fields.Many2one( + comodel_name="res.partner", compute="_compute_cte40_ender" + ) + + cte40_enderFerro = fields.Many2one( + comodel_name="res.partner", compute="_compute_cte40_ender" + ) + + # enderToma/enderEmit/enderReme + cte40_xLgr = fields.Char(related="street_name", readonly=True) + cte40_nro = fields.Char(related="street_number", readonly=True) + cte40_xCpl = fields.Char(related="street2", readonly=True) + cte40_xBairro = fields.Char(related="district", readonly=True) + cte40_cMun = fields.Char(related="city_id.ibge_code", readonly=True) + cte40_xMun = fields.Char(related="city_id.name", readonly=True) + cte40_UF = fields.Char(related="state_id.code") + cte40_CEP = fields.Char( + compute="_compute_cep", + inverse="_inverse_cte40_CEP", + compute_sudo=True, + store=True, + ) + cte40_cPais = fields.Char( + related="country_id.bc_code", + ) + cte40_xPais = fields.Char( + related="country_id.name", + ) + + cte40_IE = fields.Char(compute="_compute_cte40_IE") + + cte40_xNome = fields.Char(related="legal_name") + + def _compute_cte40_IE(self): + for rec in self: + rec.cte40_IE = str(rec.inscr_est).replace(".", "") + + def _compute_cte40_ender(self): + for rec in self: + rec.cte40_enderToma = rec.id + rec.cte40_enderReme = rec.id + rec.cte40_enderDest = rec.id + rec.cte40_enderExped = rec.id + rec.cte40_enderFerro = rec.id + + @api.depends("company_type", "inscr_est", "cnpj_cpf", "country_id") + def _compute_cte_data(self): + for rec in self: + cnpj_cpf = punctuation_rm(rec.cnpj_cpf) + if cnpj_cpf: + if rec.is_company: + rec.cte40_CNPJ = cnpj_cpf + rec.cte40_CPF = None + else: + rec.cte40_CNPJ = None + rec.cte40_CPF = cnpj_cpf + + def _inverse_cte40_CEP(self): + for rec in self: + if rec.cte40_CEP: + country_code = rec.country_id.code if rec.country_id else "BR" + rec.zip = format_zipcode(rec.cte40_CEP, country_code) + + def _compute_cep(self): + for rec in self: + rec.cte40_CEP = punctuation_rm(rec.zip) + + def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + if not self.cnpj_cpf and self.parent_id: + cnpj_cpf = punctuation_rm(self.parent_id.cnpj_cpf) + else: + cnpj_cpf = punctuation_rm(self.cnpj_cpf) + + if xsd_field == self.cte40_choice_cnpj_cpf: + return cnpj_cpf + + if self.country_id.code != "BR": + if xsd_field == "cte40_xBairro": + return "EX" + + if xsd_field == "cte40_xMun": + return "EXTERIOR" + + if xsd_field == "cte40_cMun": + return "9999999" + + if xsd_field == "cte40_UF": + return "EX" + return super()._export_field(xsd_field, class_obj, member_spec, export_value) diff --git a/l10n_br_cte/models/rodoviario.py b/l10n_br_cte/models/rodoviario.py new file mode 100644 index 000000000000..7a52647dc089 --- /dev/null +++ b/l10n_br_cte/models/rodoviario.py @@ -0,0 +1,76 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + +from ..constants.modal import TUF + + +class Rodo(spec_models.StackedModel): + _name = "l10n_br_cte.modal.rodo" + _inherit = "cte.40.rodo" + _stacked = "cte.40.rodo" + _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_rodoviario_v4_00" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" + _spec_tab_name = "CTe" + _description = "Modal Rodoviario CTe" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_RNTRC = fields.Char(related="document_id.cte40_RNTRC") + + cte40_occ = fields.One2many(related="document_id.cte40_occ") + + +class Occ(spec_models.StackedModel): + _name = "l10n_br_cte.modal.rodo.occ" + _inherit = "cte.40.occ" + _stacked = "cte.40.occ" + _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_rodoviario_v4_00" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" + _spec_tab_name = "CTe" + _description = "Ordens de Coleta associados" + + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") + + cte40_serie = fields.Char(string="Série da OCC") + + cte40_nOcc = fields.Char(string="Número da Ordem de coleta") + + cte40_dEmi = fields.Date( + string="Data de emissão da ordem de coleta", + help="Data de emissão da ordem de coleta\nFormato AAAA-MM-DD", + ) + + cte40_CNPJ = fields.Char( + string="Número do CNPJ", + help="Número do CNPJ\nInformar os zeros não significativos.", + ) + + cte40_cInt = fields.Char( + string="Código interno de uso da transportadora", + help=( + "Código interno de uso da transportadora\nUso intermo das " + "transportadoras." + ), + ) + + cte40_IE = fields.Char(string="Inscrição Estadual") + + cte40_UF = fields.Selection( + TUF, + string="Sigla da UF", + help="Sigla da UF\nInformar EX para operações com o exterior.", + ) + + cte40_fone = fields.Char(string="Telefone") diff --git a/l10n_br_cte/readme/CONFIGURE.rst b/l10n_br_cte/readme/CONFIGURE.rst new file mode 100644 index 000000000000..754e51aeff53 --- /dev/null +++ b/l10n_br_cte/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_cte/readme/CONTRIBUTORS.rst b/l10n_br_cte/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..957041454b69 --- /dev/null +++ b/l10n_br_cte/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Ygor Carvalho diff --git a/l10n_br_cte/readme/DESCRIPTION.rst b/l10n_br_cte/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..43cf54b776e0 --- /dev/null +++ b/l10n_br_cte/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_cte/readme/USAGE.rst b/l10n_br_cte/readme/USAGE.rst new file mode 100644 index 000000000000..f4629c3d548a --- /dev/null +++ b/l10n_br_cte/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_cte/security/ir.model.access.csv b/l10n_br_cte/security/ir.model.access.csv new file mode 100644 index 000000000000..a7ed316bbe7a --- /dev/null +++ b/l10n_br_cte/security/ir.model.access.csv @@ -0,0 +1,17 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +l10n_br_cte_modal_aereo_user,l10n_br_cte_modal_aereo_user,model_l10n_br_cte_modal_aereo,base.group_user,1,1,1,1 +l10n_br_cte_modal_aereo_peri_user,l10n_br_cte_modal_aereo_peri_user,model_l10n_br_cte_modal_aereo_peri,base.group_user,1,1,1,1 + +l10n_br_cte_modal_rodoviario_user,l10n_br_cte_modal_rodoviario_user,model_l10n_br_cte_modal_rodo,base.group_user,1,1,1,1 +l10n_br_cte_modal_rodoviario_occ_user,l10n_br_cte_modal_rodoviario_occ_user,model_l10n_br_cte_modal_rodo_occ,base.group_user,1,1,1,1 + +l10n_br_cte_modal_ferrov_user,l10n_br_cte_modal_ferrov_user,model_l10n_br_cte_modal_ferrov,base.group_user,1,1,1,1 + +l10n_br_cte_modal_aquaviario_user,l10n_br_cte_modal_aquav_user,model_l10n_br_cte_modal_aquav,base.group_user,1,1,1,1 +l10n_br_cte_modal_aquaviario_balsa_user,l10n_br_cte_modal_aquav_balsa_user,model_l10n_br_cte_modal_aquav_balsa,base.group_user,1,1,1,1 + +l10n_br_cte_modal_duto_user,l10n_br_cte_modal_duto_user,model_l10n_br_cte_modal_duto,base.group_user,1,1,1,1 + +l10n_br_cte_normal_infos_user,l10n_br_cte_normal_infos_user,model_l10n_br_cte_normal_infos,base.group_user,1,1,1,1 +l10n_br_cte_cargo_quantity_infos_user,l10n_br_cte_cargo_quantity_infos_user,model_l10n_br_cte_cargo_quantity_infos,base.group_user,1,1,1,1 +l10n_br_cte_transported_vehicles_user,l10n_br_cte_transported_vehicles_user,model_l10n_br_cte_transported_vehicles,base.group_user,1,1,1,1 diff --git a/l10n_br_cte/static/description/icon.png b/l10n_br_cte/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4515bffd318de5f2fc48e095121da295ddffce72 GIT binary patch literal 30809 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 literal 0 HcmV?d00001 diff --git a/l10n_br_cte/static/description/index.html b/l10n_br_cte/static/description/index.html new file mode 100644 index 000000000000..e1d718ef2fd5 --- /dev/null +++ b/l10n_br_cte/static/description/index.html @@ -0,0 +1,463 @@ + + + + + +CT-e + + + +
+

CT-e

+ + +

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

+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • KMEE
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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

+

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

+

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

+
+
+
+ + diff --git a/l10n_br_cte/views/cte_document.xml b/l10n_br_cte/views/cte_document.xml new file mode 100644 index 000000000000..26a63c9c33be --- /dev/null +++ b/l10n_br_cte/views/cte_document.xml @@ -0,0 +1,138 @@ + + + + + + l10n_br_cte.document.form.inherit + l10n_br_fiscal.document + 10 + + + + [('document_type_id.code', 'in', ['57', '08', '09', '10', '11', '26', '67', '8B'])] + + + [('document_type_id.code', 'in', ['57', '08', '09', '10', '11', '26', '67', '8B'])] + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+
+ + + + + + + + + + + +
+ + + + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
diff --git a/l10n_br_cte/views/document_line.xml b/l10n_br_cte/views/document_line.xml new file mode 100644 index 000000000000..e2da706e8b22 --- /dev/null +++ b/l10n_br_cte/views/document_line.xml @@ -0,0 +1,13 @@ + + + + + + document.line + + + + + + diff --git a/l10n_br_cte/views/document_related.xml b/l10n_br_cte/views/document_related.xml new file mode 100644 index 000000000000..1a66fbe13e15 --- /dev/null +++ b/l10n_br_cte/views/document_related.xml @@ -0,0 +1,17 @@ + + + + + + document.related.form (in l10n_br_cte) + document.related + + + + + + + + + diff --git a/l10n_br_cte/views/res_company.xml b/l10n_br_cte/views/res_company.xml new file mode 100644 index 000000000000..8907285e0e3a --- /dev/null +++ b/l10n_br_cte/views/res_company.xml @@ -0,0 +1,33 @@ + + + + + + cte.res.company.form + res.company + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/l10n_br_cte/views/res_partner.xml b/l10n_br_cte/views/res_partner.xml new file mode 100644 index 000000000000..9d738eaa174c --- /dev/null +++ b/l10n_br_cte/views/res_partner.xml @@ -0,0 +1,17 @@ + + + + + + res.partner.form (in l10n_br_cte) + res.partner + + + + + + + + + diff --git a/setup/l10n_br_cte/odoo/addons/l10n_br_cte b/setup/l10n_br_cte/odoo/addons/l10n_br_cte new file mode 120000 index 000000000000..a76696a2ca0b --- /dev/null +++ b/setup/l10n_br_cte/odoo/addons/l10n_br_cte @@ -0,0 +1 @@ +../../../../l10n_br_cte \ No newline at end of file diff --git a/setup/l10n_br_cte/setup.py b/setup/l10n_br_cte/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/l10n_br_cte/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 813810d6e1d2fd2d9e174a1a1e930b362257d185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Felipe=20Mil=C3=A9o?= Date: Thu, 24 Oct 2024 17:22:14 -0300 Subject: [PATCH 04/27] [RFC] l10n_br_fiscal: add service provider and transport model --- l10n_br_fiscal/models/document.py | 23 +++++++++++++++++++++++ l10n_br_fiscal/views/document_view.xml | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/l10n_br_fiscal/models/document.py b/l10n_br_fiscal/models/document.py index dfe625f30ca3..dddb2056a8d7 100644 --- a/l10n_br_fiscal/models/document.py +++ b/l10n_br_fiscal/models/document.py @@ -198,6 +198,29 @@ class Document(models.Model): default=False, ) + transport_modal = fields.Selection( + selection=[ + ("1", "Rodoviário"), + ("2", "Aéreo"), + ("3", "Aquaviário"), + ("4", "Ferroviário"), + ("5", "Dutoviário"), + ("6", "Multimodal"), + ], + string="Modal de Transporte", + ) + + service_provider = fields.Selection( + selection=[ + ("0", "Remetente"), + ("1", "Expedidor"), + ("2", "Recebedor"), + ("3", "Destinatário"), + ("4", "Outros"), + ], + string="Tomador do Serviço", + ) + @api.constrains("document_key") def _check_key(self): for record in self: diff --git a/l10n_br_fiscal/views/document_view.xml b/l10n_br_fiscal/views/document_view.xml index 48a91d4597eb..00b7a16dd2f6 100644 --- a/l10n_br_fiscal/views/document_view.xml +++ b/l10n_br_fiscal/views/document_view.xml @@ -285,6 +285,10 @@ + + + + From fc8b96988bf5f32b4ee5cc01c4fd8942894d30cd Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 24 Oct 2024 17:34:10 -0300 Subject: [PATCH 05/27] [RFC] l10n_br_cte: escodoo refactor --- l10n_br_cte/__manifest__.py | 13 +- l10n_br_cte/hooks.py | 20 +- l10n_br_cte/models/__init__.py | 1 + l10n_br_cte/models/aquaviario.py | 4 +- l10n_br_cte/models/comment.py | 39 + l10n_br_cte/models/document.py | 887 +++++++++++++++--- l10n_br_cte/models/document_line.py | 201 +--- l10n_br_cte/models/document_related.py | 70 +- l10n_br_cte/models/document_supplement.py | 5 +- l10n_br_cte/models/normal_cte_infos.py | 11 +- l10n_br_cte/models/res_company.py | 42 +- l10n_br_cte/models/res_partner.py | 29 +- l10n_br_cte/views/cte_document.xml | 23 +- l10n_br_cte/views/res_company.xml | 12 +- .../wizards/document_correction_wizard.xml | 30 + 15 files changed, 914 insertions(+), 473 deletions(-) create mode 100644 l10n_br_cte/models/comment.py create mode 100644 l10n_br_cte/wizards/document_correction_wizard.xml diff --git a/l10n_br_cte/__manifest__.py b/l10n_br_cte/__manifest__.py index 23e83cc880dc..3e7d3b73fc09 100644 --- a/l10n_br_cte/__manifest__.py +++ b/l10n_br_cte/__manifest__.py @@ -11,7 +11,7 @@ "website": "https://github.com/OCA/l10n-brazil", "development_status": "Alpha", "depends": [ - "l10n_br_fiscal", + "l10n_br_fiscal_edi", "l10n_br_cte_spec", "l10n_br_fiscal_certificate", "spec_driven_model", @@ -27,16 +27,17 @@ "modal/modal_aereo.xml", "views/res_company.xml", "views/cte_document.xml", + "wizards/document_correction_wizard.xml", ], "post_init_hook": "post_init_hook", "installable": True, "auto_install": False, "external_dependencies": { "python": [ - "nfelib", - "erpbrasil.transmissao", - "erpbrasil.edoc", - "erpbrasil.assinatura", - ] + "nfelib<=2.0.7", + "erpbrasil.assinatura>=1.7.0", + "erpbrasil.transmissao>=1.1.0", + "erpbrasil.edoc>=2.5.2", + ], }, } diff --git a/l10n_br_cte/hooks.py b/l10n_br_cte/hooks.py index c01e14a97c2b..572921aaf117 100644 --- a/l10n_br_cte/hooks.py +++ b/l10n_br_cte/hooks.py @@ -2,20 +2,12 @@ # 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_cte", - "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00", - ) - - hooks.post_init_hook( - cr, - registry, - "l10n_br_cte", - "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00", - ) + env["cte.40.tcte_infcte"]._register_hook() + # hooks.register_hook( + # env, + # "l10n_br_cte", + # "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00", + # ) diff --git a/l10n_br_cte/models/__init__.py b/l10n_br_cte/models/__init__.py index 3aac89ccbd32..40014e44edb5 100644 --- a/l10n_br_cte/models/__init__.py +++ b/l10n_br_cte/models/__init__.py @@ -13,3 +13,4 @@ from . import document_supplement from . import document_transported_vehicles from . import normal_cte_infos +from . import comment diff --git a/l10n_br_cte/models/aquaviario.py b/l10n_br_cte/models/aquaviario.py index 3e5e7107c957..54709b03fba0 100644 --- a/l10n_br_cte/models/aquaviario.py +++ b/l10n_br_cte/models/aquaviario.py @@ -23,7 +23,9 @@ class Aquav(spec_models.StackedModel): cte40_vAFRMM = fields.Monetary(related="document_id.cte40_vAFRMM") - cte40_vPrest = fields.Monetary(related="document_id.cte40_vPrest") + cte40_vPrest = fields.Monetary( + related="document_id.cte40_vTPrest" + ) # TODO: avaliar melhor cte40_xNavio = fields.Char(related="document_id.cte40_xNavio") diff --git a/l10n_br_cte/models/comment.py b/l10n_br_cte/models/comment.py new file mode 100644 index 000000000000..dba4685a3278 --- /dev/null +++ b/l10n_br_cte/models/comment.py @@ -0,0 +1,39 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields + +from odoo.addons.spec_driven_model.models import spec_models + + +class CTeComment(spec_models.StackedModel): + _name = "l10n_br_fiscal.comment" + _inherit = ["l10n_br_fiscal.comment", "cte.40.tcte_obscont", "cte.40.tcte_obsfisco"] + _stacked = "cte.40.tcte_obscont" + _field_prefix = "cte40_" + _schema_name = "cte" + _schema_version = "4.0.0" + _odoo_module = "l10n_br_cte" + _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + _spec_tab_name = "CTe" + _stacking_points = {} + _stack_skip = ("cte40_ObsCont_compl_id", "cte40_ObsFisco_compl_id") + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + + cte40_xCampo = fields.Char() + + cte40_xTexto = fields.Text() + + def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + if xsd_field == "cte40_xCampo": + return self.name[:20].strip() + if xsd_field == "cte40_xTexto": + if "doc" in self.env.context: + doc_id = self.env.context["doc"] + doc = self.env["l10n_br_fiscal.document"].browse(doc_id) + vals = {"user": self.env.user, "ctx": self._context, "doc": doc} + message = self.compute_message(vals).strip() + if self.comment_type == "fiscal": + return message[:60] + return message[:160] + return super()._export_field(xsd_field, class_obj, member_spec, export_value) diff --git a/l10n_br_cte/models/document.py b/l10n_br_cte/models/document.py index e0fdaec4bb92..66c6b4b11a9a 100644 --- a/l10n_br_cte/models/document.py +++ b/l10n_br_cte/models/document.py @@ -1,15 +1,19 @@ # Copyright 2023 KMEE INFORMATICA LTDA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import base64 import logging import re +import sys from datetime import datetime -from erpbrasil.assinatura import certificado as cert -from erpbrasil.transmissao import TransmissaoSOAP +from erpbrasil.edoc.cte import TransmissaoCTE +from lxml import etree from nfelib.cte.bindings.v4_0.cte_v4_00 import Cte +from nfelib.cte.bindings.v4_0.proc_cte_v4_00 import CteProc from nfelib.nfe.ws.edoc_legacy import CTeAdapter as edoc_cte from requests import Session +from xsdata.formats.dataclass.parsers import XmlParser from odoo import _, api, fields from odoo.exceptions import UserError @@ -19,26 +23,42 @@ TRAFMUT_FERREMI, TRAFMUT_RESPFAT, ) +from odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00 import ( + COMDATA_TPPER, + SEMHORA_TPHOR, +) from odoo.addons.l10n_br_fiscal.constants.fiscal import ( AUTORIZADO, CANCELADO, CANCELADO_DENTRO_PRAZO, CANCELADO_FORA_PRAZO, DENEGADO, + DOCUMENT_ISSUER_COMPANY, EVENT_ENV_HML, EVENT_ENV_PROD, + EVENTO_RECEBIDO, LOTE_PROCESSADO, + PROCESSADOR_OCA, + SITUACAO_EDOC_A_ENVIAR, SITUACAO_EDOC_AUTORIZADA, SITUACAO_EDOC_CANCELADA, SITUACAO_EDOC_DENEGADA, + SITUACAO_EDOC_EM_DIGITACAO, SITUACAO_EDOC_REJEITADA, SITUACAO_FISCAL_CANCELADO, SITUACAO_FISCAL_CANCELADO_EXTEMPORANEO, ) +from odoo.addons.l10n_br_fiscal.constants.icms import ICMS_CST, ICMS_SN_CST from odoo.addons.spec_driven_model.models import spec_models from ..constants.modal import CTE_MODAL_VERSION_DEFAULT +CTE_XML_NAMESPACE = {"cte": "http://www.portalfiscal.inf.br/cte"} + +# TODO: https://github.com/Engenere/BrazilFiscalReport/pull/23 +# from brazilfiscalreport.dacte import Dacte + + _logger = logging.getLogger(__name__) try: pass @@ -56,31 +76,67 @@ def filter_processador_edoc_cte(record): class CTe(spec_models.StackedModel): - _name = "l10n_br_fiscal.document" - _inherit = ["l10n_br_fiscal.document", "cte.40.tcte_infcte", "cte.40.tcte_fat"] + _inherit = [ + "l10n_br_fiscal.document", + "cte.40.tcte_infcte", + "cte.40.tcte_imp", + "cte.40.tcte_fat", + ] _stacked = "cte.40.tcte_infcte" _field_prefix = "cte40_" _schema_name = "cte" _schema_version = "4.0.0" _odoo_module = "l10n_br_cte" _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + _spec_tab_name = "CTe" _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + _stack_skip = ( + "cte40_fluxo", + "cte40_semData", + "cte40_noInter", + "cte40_comHora", + "cte40_noPeriodo", + ) _cte_search_keys = ["cte40_Id"] + # all m2o at this level will be stacked even if not required: + _force_stack_paths = ( + "infcte.compl", + "infcte.compl.entrega" "infcte.vprest", + "infcte.imp", + ) + INFCTE_TREE = """ > infCte > - - res.partner + > res.partner + > + > + - + ≡ + ≡ + > res.company - > res.partner + > res.company + > res.partner + > res.partner > + ≡ > - - - - + > + > > - - - - """ + > + ≡ + > + ≡ + ≡ + ≡ + - > + > + > + > """ ########################## # CT-e spec related fields @@ -132,12 +188,7 @@ def _inverse_cte40_Id(self): cte40_cCT = fields.Char(compute="_compute_cct") - cfop_id = fields.Many2one( - comodel_name="l10n_br_fiscal.cfop", - string="CFOP", - ) - - cte40_CFOP = fields.Char(related="cfop_id.code") + cte40_CFOP = fields.Char(compute="_compute_CFOP", store=True) cte40_natOp = fields.Char(related="operation_name") @@ -164,41 +215,39 @@ def _inverse_cte40_Id(self): cte40_xMunEnv = fields.Char(compute="_compute_cte40_data", store=True) - cte40_UFEnv = fields.Char( - compute="_compute_cte40_data", string="cte40_UFEnv", store=True - ) + cte40_UFEnv = fields.Char(compute="_compute_cte40_data", store=True) - # cte40_indIEToma = fields.Char(related="partner_id.incr_est", store=True) + cte40_indIEToma = fields.Selection( + selection=[ + ("1", "Contribuinte ICMS"), + ("2", "Contribuinte isento de inscrição"), + ("9", "Não Contribuinte"), + ], + default="1", + ) - cte40_cMunIni = fields.Char(compute="_compute_cte40_data", store=True) + cte40_cMunIni = fields.Char(compute="_compute_cte40_data") - cte40_xMunIni = fields.Char(compute="_compute_cte40_data", store=True) + cte40_xMunIni = fields.Char(compute="_compute_cte40_data") - cte40_UFIni = fields.Char(compute="_compute_cte40_data", store=True) + cte40_UFIni = fields.Char(compute="_compute_cte40_data") - cte40_cMunFim = fields.Char( - compute="_compute_cte40_data", - related="partner_id.city_id.ibge_code", - store=True, - ) + cte40_cMunFim = fields.Char(compute="_compute_cte40_data") - cte40_xMunFim = fields.Char( - compute="_compute_cte40_data", related="partner_id.city_id.name", store=True - ) + cte40_xMunFim = fields.Char(compute="_compute_cte40_data") - cte40_UFFim = fields.Char( - compute="_compute_cte40_data", string="cte40_cUF", store=True - ) + cte40_UFFim = fields.Char(compute="_compute_cte40_data") cte40_retira = fields.Selection(selection=[("0", "Sim"), ("1", "Não")], default="1") cte40_tpServ = fields.Selection( selection=[ - ("6", "Transporte de Pessoas"), - ("7", "Transporte de Valores"), - ("8", "Excesso de Bagagem"), + ("0", "Normal"), + ("1", "Subcontratação"), + ("2", "Redespacho"), + ("3", "Redespacho Intermediário"), ], - default="6", + default="0", ) cte40_tpCTe = fields.Selection( @@ -256,22 +305,6 @@ def _export_fields_cte_40_tcte_toma4(self, xsd_fields, class_obj, export_dict): cte40_toma = fields.Selection(related="service_provider") - cte40_CNPJ = fields.Char( - related="partner_id.cte40_CNPJ", - ) - cte40_CPF = fields.Char( - related="partner_id.cte40_CPF", - ) - cte40_IE = fields.Char( - related="partner_id.cte40_IE", - ) - cte40_xNome = fields.Char( - related="partner_id.legal_name", - ) - cte40_xFant = fields.Char( - related="partner_id.name", - ) - cte40_enderToma = fields.Many2one(comodel_name="res.partner", related="partner_id") ########################## @@ -287,29 +320,58 @@ def _compute_toma(self): else: doc.cte40_choice_toma = "cte40_toma4" + @api.depends("fiscal_line_ids") + def _compute_CFOP(self): + for rec in self: + if rec.fiscal_line_ids: + rec.cte40_CFOP = rec.fiscal_line_ids[0].cfop_id.code + + @api.depends("document_key") def _compute_cDV(self): for rec in self: if rec.document_key: - rec.cte40_cDV = rec.document_key[:-1] + rec.cte40_cDV = rec.document_key[-1] def _compute_cct(self): for rec in self: if rec.document_key: rec.cte40_cCT = rec.document_key[35:43] - @api.depends("partner_id", "company_id") + @api.depends( + "partner_id", + "company_id", + "cte40_rem", + "cte40_dest", + "cte40_exped", + "cte40_receb", + ) def _compute_cte40_data(self): for doc in self: if doc.company_id.partner_id.country_id == doc.partner_id.country_id: - doc.cte40_xMunIni = doc.company_id.partner_id.city_id.name - doc.cte40_cMunIni = doc.company_id.partner_id.city_id.ibge_code - doc.cte40_xMunEnv = doc.company_id.partner_id.city_id.name + doc.cte40_xMunEnv = ( + doc.company_id.partner_id.city_id.name + ) # TODO: provavelmente vai depender de quem é o emissor doc.cte40_cMunEnv = doc.company_id.partner_id.city_id.ibge_code doc.cte40_UFEnv = doc.company_id.partner_id.state_id.code - doc.cte40_UFIni = doc.company_id.partner_id.state_id.ibge_code - doc.cte40_cMunFim = doc.partner_id.city_id.ibge_code - doc.cte40_xMunFim = doc.partner_id.city_id.name - doc.cte40_UFFim = doc.partner_id.state_id.code + doc.cte40_xMunIni = ( + doc.cte40_exped.city_id.name or doc.cte40_rem.city_id.name + ) + doc.cte40_cMunIni = ( + doc.cte40_exped.city_id.ibge_code or doc.cte40_rem.city_id.ibge_code + ) + doc.cte40_UFIni = ( + doc.cte40_exped.state_id.code or doc.cte40_rem.state_id.code + ) + doc.cte40_xMunFim = ( + doc.cte40_receb.city_id.name or doc.cte40_dest.city_id.name + ) + doc.cte40_cMunFim = ( + doc.cte40_receb.city_id.ibge_code + or doc.cte40_dest.city_id.ibge_code + ) + doc.cte40_UFFim = ( + doc.cte40_receb.state_id.code or doc.cte40_dest.state_id.code + ) else: doc.cte40_UFIni = "EX" doc.cte40_UFEnv = "EX" @@ -325,20 +387,82 @@ def _compute_cte40_data(self): doc.cte40_xMunFim = "EXTERIOR" doc.cte40_UFFim = "EX" + ########################## + # CT-e tag: compl + ########################## + + cte40_xObs = fields.Text(compute="_compute_cte40_compl") + cte40_obsCont = fields.One2many( + "l10n_br_fiscal.comment", compute="_compute_cte40_obsCont" + ) + + cte40_obsFisco = fields.One2many( + "l10n_br_fiscal.comment", compute="_compute_cte40_obsCont" + ) + + ########################## + # CT-e tag: compl + # Methods + ########################## + + @api.depends("comment_ids") + def _compute_cte40_obsCont(self): + for doc in self: + doc.cte40_obsCont = doc.comment_ids.filtered( + lambda c: c.comment_type == "commercial" + ) + doc.cte40_obsFisco = doc.comment_ids.filtered( + lambda c: c.comment_type == "fiscal" + ) + + def _compute_cte40_compl(self): + for doc in self: + fiscal_data = ( + doc.fiscal_additional_data if doc.fiscal_additional_data else "" + ) + customer_data = ( + doc.customer_additional_data if doc.customer_additional_data else "" + ) + doc.cte40_xObs = (fiscal_data + " " + customer_data)[:256].strip() + + ########################## + # CT-e tag: entrega + ########################## + + # TODO: pensar em algo genericoom base nisso decidir quais tags + # puxar (comData,semData,noPeriodo...) + cte40_tpPer = fields.Selection( + selection=COMDATA_TPPER, string="Tipo de data/período programado", default="2" + ) + cte40_dProg = fields.Date("Data Programada", default=fields.Date.today) + + cte40_tpHor = fields.Selection(SEMHORA_TPHOR, string="Tipo de hora", default="0") + ########################## # CT-e tag: emit ########################## - cte40_emit = fields.Many2one( - comodel_name="res.company", + cte40_CNPJ = fields.Char( + compute="_compute_emit_data", + ) + cte40_CPF = fields.Char( + compute="_compute_emit_data", + ) + cte40_IE = fields.Char( + compute="_compute_emit_data", + ) + cte40_xNome = fields.Char( + compute="_compute_emit_data", + ) + cte40_xFant = fields.Char( compute="_compute_emit_data", - readonly=True, - string="Emit", + ) + cte40_enderEmit = fields.Many2one( + comodel_name="res.partner", compute="_compute_emit_data" ) cte40_CRT = fields.Selection( - related="company_tax_framework", - string="Código de Regime Tributário (NFe)", + compute="_compute_emit_data", ) ########################## @@ -346,9 +470,25 @@ def _compute_cte40_data(self): # Compute Methods ########################## + @api.depends("company_id", "partner_id", "issuer") def _compute_emit_data(self): - for doc in self: # TODO if out - doc.cte40_emit = doc.company_id + for doc in self: + if doc.issuer == DOCUMENT_ISSUER_COMPANY: + doc.cte40_CNPJ = doc.company_id.partner_id.cte40_CNPJ + doc.cte40_CPF = doc.company_id.partner_id.cte40_CPF + doc.cte40_IE = doc.company_id.partner_id.cte40_IE + doc.cte40_xNome = doc.company_id.partner_id.legal_name + doc.cte40_xFant = doc.company_id.partner_id.name + doc.cte40_enderEmit = doc.company_id.partner_id + doc.cte40_CRT = doc.company_tax_framework + else: + doc.cte40_CNPJ = doc.partner_id.cte40_CNPJ + doc.cte40_CPF = doc.partner_id.cte40_CPF + doc.cte40_IE = doc.partner_id.cte40_IE + doc.cte40_xNome = doc.partner_id.legal_name + doc.cte40_xFant = doc.partner_id.name + doc.cte40_enderEmit = doc.partner_id + doc.cte40_CRT = doc.partner_tax_framework ########################## # CT-e tag: rem @@ -356,78 +496,241 @@ def _compute_emit_data(self): cte40_rem = fields.Many2one( comodel_name="res.partner", - compute="_compute_rem_data", - readonly=True, - string="Rem", + string="Remetente", ) - ########################## - # CT-e tag: rem - # Compute Methods - ########################## - - def _compute_rem_data(self): - for doc in self: # TODO if out - doc.cte40_rem = doc.partner_id - ########################## # CT-e tag: exped ########################## cte40_exped = fields.Many2one( - comodel_name="res.company", - compute="_compute_exped_data", - readonly=True, - string="Exped", + comodel_name="res.partner", + string="Expedidor", ) ########################## - # CT-e tag: exped - # Compute Methods + # CT-e tag: dest ########################## - def _compute_exped_data(self): - for doc in self: # TODO if out - doc.cte40_exped = doc.company_id + cte40_dest = fields.Many2one( + comodel_name="res.partner", string="Destinatário", related="partner_shipping_id" + ) ########################## - # CT-e tag: dest + # CT-e tag: receb ########################## - cte40_dest = fields.Many2one( + cte40_receb = fields.Many2one( comodel_name="res.partner", - compute="_compute_dest_data", - readonly=True, - string="Dest", + string="Recebedor", ) ########################## - # CT-e tag: dest - # Compute Methods + # CT-e tag: vPrest + # Methods ########################## - def _compute_dest_data(self): - for doc in self: # TODO if out - doc.cte40_dest = doc.partner_shipping_id + cte40_vTPrest = fields.Monetary( + compute="_compute_cte40_vPrest", + string="Valor da Total Prestação Base de Cálculo", + ) - ########################## - # CT-e tag: imp TODO - ########################## + cte40_vRec = fields.Monetary( + compute="_compute_cte40_vPrest", + string="Valor Recebido", + ) - cte40_imp = fields.One2many( + cte40_comp = fields.One2many( comodel_name="l10n_br_fiscal.document.line", inverse_name="document_id", related="fiscal_line_ids", ) + def _compute_cte40_vPrest(self): + vTPrest = 0 + vRec = 0 + for doc in self: + for line in self.fiscal_line_ids: + vTPrest += line.amount_total + vRec += line.price_gross + doc.cte40_vTPrest = vTPrest + doc.cte40_vRec = vRec + + ################################################## + # CT-e tag: ICMS + # Grupo N01. Grupo Tributação do ICMS= 00 + # Grupo N02. Grupo Tributação do ICMS= 20 + # Grupo N03. Grupo Tributação do ICMS= 45 (40, 41 e 51) + # Grupo N04. Grupo Tributação do ICMS= 60 + # Grupo N05. Grupo Tributação do ICMS= 90 - ICMS outros + # Grupo N06. Grupo Tributação do ICMS= 90 - ICMS Outra UF + # Grupo N06. Grupo Tributação do ICMS= 01 - ISSN + ################################################# + + cte40_vTotTrib = fields.Monetary(related="amount_estimate_tax") + + # cte40_infAdFisco = fields.Text(related="additional_data") + + ################################################## + # CT-e tag: ICMS + # Methods + ################################################## + + cte40_choice_icms = fields.Selection( + selection=[ + ("cte40_ICMS00", "ICMS00"), + ("cte40_ICMS20", "ICMS20"), + ("cte40_ICMS45", "ICMS45"), + ("cte40_ICMS60", "ICMS60"), + ("cte40_ICMS90", "ICMS90"), + ("cte40_ICMSOutraUF", "ICMSOutraUF"), + ("cte40_ICMSSN", "ICMSSN"), + ], + string="Tipo de ICMS", + compute="_compute_choice_icms", + store=True, + ) + + cte40_CST = fields.Selection( + selection=[ + ("00", "00 - Tributação normal ICMS"), + ("20", "20 - Tributação com BC reduzida do ICMS"), + ("45", "45 - ICMS Isento, não Tributado ou diferido"), + ("60", "60 - ICMS cobrado por substituição tributária"), + ("90", "90 - ICMS outros"), + ("90", "90 - ICMS Outra UF"), + ("01", "01 - Simples Nacional"), + ], + string="Classificação Tributária do Serviço", + compute="_compute_choice_icms", + store=True, + ) + + # ICMSSN + cte40_indSN = fields.Float(default=1) + + # # ICMSOutraUF + # # TODO + ########################## - # CT-e tag: imp + # CT-e tag: ICMS # Compute Methods ########################## - def _compute_imp(self): - for doc in self: - doc.cte40_ICMS = doc.fiscal_line_ids + @api.depends("fiscal_line_ids") + def _compute_choice_icms(self): + for record in self: + record.cte40_choice_icms = None + record.cte40_CST = None + if not record.fiscal_line_ids: + continue + if record.fiscal_line_ids[0].icms_cst_id.code in ICMS_CST: + if record.fiscal_line_ids[0].icms_cst_id.code in ["40", "41", "50"]: + record.cte40_choice_icms = "cte40_ICMS45" + record.cte40_CST = "45" + elif ( + record.fiscal_line_ids[0].icms_cst_id.code == "90" + and record.partner_id.state_id != record.company_id.state_id + ): + record.cte40_choice_icms = "cte40_ICMSOutraUF" + else: + record.cte40_choice_icms = "{}{}".format( + "cte40_ICMS", record.fiscal_line_ids[0].icms_cst_id.code + ) + record.cte40_CST = record.fiscal_line_ids[0].icms_cst_id.code + elif record.fiscal_line_ids[0].icms_cst_id.code in ICMS_SN_CST: + record.cte40_choice_icms = "cte40_ICMSSN" + record.cte40_CST = "90" + + def _export_fields_icms(self): + # Verifica se fiscal_line_ids está vazio para evitar erros + if not self.fiscal_line_ids: + return {} + + # TODO:aprimorar. talvez criar os campos relacionados com os campos e totais + # do documento fiscal e buscar apenas os percentuais da primeira linha + first_line = self.fiscal_line_ids[0] + + icms = { + "CST": self.cte40_CST, + "vBC": 0.0, + "pRedBC": first_line.icms_reduction, + "pICMS": first_line.icms_percent, + "vICMS": 0.0, + "vICMSSubstituto": 0.0, + "indSN": int(self.cte40_indSN), + "vBCSTRet": 0.0, + "vICMSSTRet": 0.0, + "pICMSSTRet": first_line.icmsst_wh_percent, + } + + for line in self.fiscal_line_ids: + icms["vBC"] += line.icms_base + icms["vICMS"] += line.icms_value + icms["vICMSSubstituto"] += line.icms_substitute + icms["vBCSTRet"] += line.icmsst_wh_base + icms["vICMSSTRet"] += line.icmsst_wh_value + + # Formatar os valores acumulados + icms["vBC"] = str("%.02f" % icms["vBC"]) + icms["vICMS"] = str("%.02f" % icms["vICMS"]) + icms["vICMSSubstituto"] = str("%.02f" % icms["vICMSSubstituto"]) + icms["vBCSTRet"] = str("%.02f" % icms["vBCSTRet"]) + icms["vICMSSTRet"] = str("%.02f" % icms["vICMSSTRet"]) + icms["pRedBC"] = str("%.04f" % icms["pRedBC"]) + icms["pICMS"] = str("%.02f" % icms["pICMS"]) + icms["pICMSSTRet"] = str("%.02f" % icms["pICMSSTRet"]) + + return icms + + def _export_fields_cte_40_timp(self, xsd_fields, class_obj, export_dict): + # TODO Not Implemented + if "cte40_ICMSOutraUF" in xsd_fields: + xsd_fields.remove("cte40_ICMSOutraUF") + + xsd_fields = [self.cte40_choice_icms] + icms_tag = ( + self.cte40_choice_icms.replace("cte40_", "") + .replace("ICMSSN", "Icmssn") + .replace("ICMS", "Icms") + ) + binding_module = sys.modules[self._binding_module] + icms = binding_module.Timp + icms_binding = getattr(icms, icms_tag) + icms_dict = self._export_fields_icms() + sliced_icms_dict = { + key: icms_dict.get(key) + for key in icms_binding.__dataclass_fields__.keys() + if icms_dict.get(key) + } + export_dict[icms_tag.upper()] = icms_binding(**sliced_icms_dict) + + # ########################## + # # CT-e tag: ICMSUFFim + # ########################## + + # cte40_vBCUFFim = fields.Monetary(related="icms_destination_base") + # cte40_pFCPUFFim = fields.Monetary(compute="_compute_cte40_ICMSUFFim", store=True) + # cte40_pICMSUFFim = fields.Monetary(compute="_compute_cte40_ICMSUFFim", store=True) + # # TODO + # # cte40_pICMSInter = fields.Selection( + # # selection=[("0", "Teste")], + # # compute="_compute_cte40_ICMSUFFim") + + # def _compute_cte40_ICMSUFFim(self): + # for record in self: + # # if record.icms_origin_percent: + # # record.cte40_pICMSInter = + # str("%.02f" % record.icms_origin_percent) + # # else: + # # record.cte40_pICMSInter = False + + # record.cte40_pFCPUFFim = record.icmsfcp_percent + # record.cte40_pICMSUFFim = record.icms_destination_percent + + # cte40_vFCPUFfim = fields.Monetary(related="icmsfcp_value") + # cte40_vICMSUFFim = fields.Monetary(related="icms_destination_value") + # cte40_vICMSUFIni = fields.Monetary(related="icms_origin_value") ##################################### # CT-e tag: infCTeNorm and infCteComp @@ -461,7 +764,6 @@ def _compute_imp(self): cte40_proPred = fields.Char( string="Produto predominante", - required=True, ) cte40_xOutCat = fields.Char( @@ -471,12 +773,55 @@ def _compute_imp(self): cte40_infQ = fields.One2many( comodel_name="l10n_br_cte.cargo.quantity.infos", inverse_name="document_id", + compute="_compute_cte40_infQ", + readonly=False, + store=True, ) cte40_vCargaAverb = fields.Monetary( string="Valor da Carga para efeito de averbação", ) + ########################## + # CT-e tag: infDoc + ########################## + + cte40_infDoc = fields.Many2one( + comodel_name="l10n_br_fiscal.document", + compute="_compute_cte40_infDoc", + string="Informações dos documentos transportados", + ) + + def _compute_cte40_infDoc(self): + for doc in self: + doc.cte40_infDoc = doc + + def _compute_cte40_infNFe(self): + for record in self: + record.cte40_infNFe = record.document_related_ids.filtered( + lambda r: r.cte40_infDoc == "cte40_infNFe" + ) + + def _compute_cte40_infOutros(self): + for record in self: + record.cte40_infOutros = record.document_related_ids.filtered( + lambda r: r.cte40_infDoc == "cte40_infOutros" + ) + + cte40_infNFe = fields.One2many( + comodel_name="l10n_br_fiscal.document.related", + inverse_name="document_id", + string="Informações das NF-e DOCS (Cte)", + compute="_compute_cte40_infNFe", + ) + + cte40_infOutros = fields.One2many( + comodel_name="l10n_br_fiscal.document.related", + inverse_name="document_id", + string="Informações dos Outros DOCS (Cte)", + compute="_compute_cte40_infOutros", + ) + ########################## # CT-e tag: veicNovos ########################## @@ -507,7 +852,7 @@ def _default_cte40_autxml(self): cte40_autXML = fields.One2many(default=_default_cte40_autxml) ########################## - # NF-e tag: infCTeSupl + # CT-e tag: infCTeSupl ########################## cte40_infCTeSupl = fields.Many2one( @@ -515,7 +860,7 @@ def _default_cte40_autxml(self): ) ########################## - # MDF-e tag: infRespTec + # CT-e tag: infRespTec ########################## cte40_infRespTec = fields.Many2one( @@ -584,6 +929,15 @@ def _compute_infresptec(self): ), ) + # TODO: avaliar + # def _compute_dime(self): + # for record in self: + # for package in record.product_id.packaging_ids: + # record.cte40_xDime = ( + # package.width + "X" + package.packaging_length + + # "X" + package.width + # ) + cte40_CL = fields.Char( string="Classe", help=( @@ -616,11 +970,12 @@ def _compute_infresptec(self): # Campos do Modal Aquaviario modal_aquaviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aquav") - cte40_vPrest = fields.Monetary( - compute="_compute_cte40_vPrest", # FIX - store=True, - string="Valor da Prestação Base de Cálculo", - ) + # TODO: fix + # cte40_vPrest = fields.Monetary( + # compute="_compute_cte40_vPrest", # FIX + # store=True, + # string="Valor da Prestação Base de Cálculo", + # ) cte40_vAFRMM = fields.Monetary( string="AFRMM", @@ -729,12 +1084,21 @@ def _compute_infresptec(self): cte40_RNTRC = fields.Char( string="RNTRC", help="Registro Nacional de Transportadores Rodoviários de Carga", + compute="_compute_cte40_RNTRC", ) + def _compute_cte40_RNTRC(self): + for record in self: + if record.issuer == DOCUMENT_ISSUER_COMPANY: + record.cte40_RNTRC = record.company_id.partner_id.rntrc_code + else: + record.cte40_RNTRC = record.partner_id.rntrc_code + cte40_occ = fields.One2many( comodel_name="l10n_br_cte.modal.rodo.occ", inverse_name="document_id", string="Ordens de Coleta associados", + copy=False, ) ########################## @@ -742,23 +1106,17 @@ def _compute_infresptec(self): # Compute Methods ########################## - def _compute_cte40_vPrest(self): - vPrest = 0 - for record in self.fiscal_line_ids: - vPrest += record.cte40_vTPrest - self.cte40_vPrest = vPrest - def _export_fields_cte_40_tcte_infmodal(self, xsd_fields, class_obj, export_dict): self = self.with_context(module="l10n_br_cte") - if self.cte40_modal == "1": + if self.cte40_modal == "01": export_dict["any_element"] = self._export_modal_rodoviario() - elif self.cte40_modal == "2": + elif self.cte40_modal == "02": export_dict["any_element"] = self._export_modal_aereo() - elif self.cte40_modal == "3": + elif self.cte40_modal == "03": export_dict["any_element"] = self._export_modal_aquaviario() - elif self.cte40_modal == "4": + elif self.cte40_modal == "04": export_dict["any_element"] = self._export_modal_ferroviario() - elif self.cte40_modal == "5": + elif self.cte40_modal == "05": export_dict["any_element"] = self._export_modal_dutoviario() def _export_modal_aereo(self): @@ -799,6 +1157,22 @@ def _export_modal_dutoviario(self): return self.modal_dutoviario_id.export_ds()[0] + ################################ + # Framework Spec model's methods + ################################ + + def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + if xsd_field == "cte40_tpAmb": + self.env.context = dict(self.env.context) + self.env.context.update({"tpAmb": self[xsd_field]}) + self.env.context.update({"doc": self.id}) + + # TODO: Força a remoção da tag infGlobalizado já que o + # campo xObs está no l10n_br_fiscal.document + if xsd_field == "cte40_infGlobalizado": + return False + return super()._export_field(xsd_field, class_obj, member_spec, export_value) + ################################ # Business Model Methods ################################ @@ -809,7 +1183,10 @@ def _serialize(self, edocs): filter_processador_edoc_cte ): inf_cte = record.export_ds()[0] - cte = Cte(infCte=inf_cte, infCTeSupl=None, signature=None) + inf_cte_supl = None + if record.cte40_infCTeSupl: + inf_cte_supl = record.cte40_infCTeSupl.export_ds()[0] + cte = Cte(infCte=inf_cte, infCTeSupl=inf_cte_supl, signature=None) edocs.append(cte) return edocs @@ -817,16 +1194,13 @@ def _processador(self): if not self.company_id.certificate_nfe_id: raise UserError(_("Certificado não encontrado")) - certificado = cert.Certificado( - arquivo=self.company_id.certificate_nfe_id.file, - senha=self.company_id.certificate_nfe_id.password, - ) + certificado = self.env.company._get_br_ecertificate() session = Session() session.verify = False - transmissao = TransmissaoSOAP(certificado, session) + transmissao = TransmissaoCTE(certificado, session) return edoc_cte( transmissao, - self.company_id.state_id.id, + self.company_id.state_id.ibge_code, self.cte40_versao, self.cte40_tpAmb, ) @@ -857,10 +1231,17 @@ def _valida_xml(self, xml_file): erros = "\n".join(erros) self.write({"xml_error_message": erros or False}) - def atualiza_status_cte(self, infProt, xml_file): + def atualiza_status_cte(self, processo): self.ensure_one() + + if hasattr(processo, "protocolo"): + infProt = processo.protocolo.infProt + else: + infProt = processo.resposta.protCTe.infProt + if infProt.cStat in AUTORIZADO: state = SITUACAO_EDOC_AUTORIZADA + self._cte_response_add_proc(processo) elif infProt.cStat in DENEGADO: state = SITUACAO_EDOC_DENEGADA else: @@ -878,7 +1259,7 @@ def atualiza_status_cte(self, infProt, xml_file): response=infProt.xMotivo, protocol_date=protocol_date, protocol_number=infProt.nProt, - file_response_xml=xml_file, + file_response_xml=processo.processo_xml.decode("utf-8"), ) self.write( { @@ -947,36 +1328,230 @@ def _cte_cancel(self): self.cancel_event_id = self.event_ids.create_event_save_xml( company_id=self.company_id, - environment=( - EVENT_ENV_PROD if self.cte_environment == "1" else EVENT_ENV_HML - ), + environment=(EVENT_ENV_PROD if self.cte40_tpAmb == "1" else EVENT_ENV_HML), event_type="2", - xml_file=processo.envio_xml.decode("utf-8"), + xml_file=processo.envio_xml, document_id=self, ) - for retevento in processo.resposta.retEvento: - if not retevento.infEvento.chCte == self.document_key: - continue + resposta = processo.resposta.infEvento - if retevento.infEvento.cStat not in CANCELADO: - mensagem = "Erro no cancelamento" - mensagem += "\nCódigo: " + retevento.infEvento.cStat - mensagem += "\nMotivo: " + retevento.infEvento.xMotivo - raise UserError(mensagem) + if resposta.cStat not in CANCELADO: + mensagem = "Erro no cancelamento" + mensagem += "\nCódigo: " + resposta.cStat + mensagem += "\nMotivo: " + resposta.xMotivo + raise UserError(mensagem) - if retevento.infEvento.cStat == CANCELADO_FORA_PRAZO: + if resposta.chCTe == self.document_key: + if resposta.cStat in CANCELADO_FORA_PRAZO: self.state_fiscal = SITUACAO_FISCAL_CANCELADO_EXTEMPORANEO - elif retevento.infEvento.cStat == CANCELADO_DENTRO_PRAZO: + elif resposta.cStat in CANCELADO_DENTRO_PRAZO: self.state_fiscal = SITUACAO_FISCAL_CANCELADO self.state_edoc = SITUACAO_EDOC_CANCELADA self.cancel_event_id.set_done( - status_code=retevento.infEvento.cStat, - response=retevento.infEvento.xMotivo, + status_code=resposta.cStat, + response=resposta.xMotivo, protocol_date=fields.Datetime.to_string( - datetime.fromisoformat(retevento.infEvento.dhRegEvento) + datetime.fromisoformat(resposta.dhRegEvento) ), - protocol_number=retevento.infEvento.nProt, + protocol_number=resposta.nProt, file_response_xml=processo.retorno.content.decode("utf-8"), ) + + def _document_correction(self, justificative): + result = super(CTe, self)._document_correction(justificative) + online_event = self.filtered(filter_processador_edoc_cte) + if online_event: + online_event._cte_correction(justificative) + return result + + def _cte_correction(self, justificative): + self.ensure_one() + processador = self._processador() + + numeros = self.event_ids.filtered( + lambda e: e.type == "14" and e.state == "done" + ).mapped("sequence") + + sequence = str(int(max(numeros)) + 1) if numeros else "1" + + evento = processador.carta_correcao( + chave=self.document_key, + protocolo_autorizacao=self.authorization_protocol, + justificativa=justificative.replace("\n", "\\n"), + sequencia=sequence, + ) + processo = processador.enviar_lote_evento(lista_eventos=[evento]) + # Gravamos o arquivo no disco e no filestore ASAP. + event_id = self.event_ids.create_event_save_xml( + company_id=self.company_id, + environment=(EVENT_ENV_PROD if self.cte40_tpAmb == "1" else EVENT_ENV_HML), + event_type="14", + xml_file=processo.envio_xml, + document_id=self, + sequence=sequence, + justification=justificative, + ) + + resposta = processo.resposta.infEvento + + if resposta.cStat not in EVENTO_RECEBIDO and not ( + resposta.chCTe == self.document_key + ): + mensagem = "Erro na carta de correção" + mensagem += "\nCódigo: " + resposta.cStat + mensagem += "\nMotivo: " + resposta.xMotivo + raise UserError(mensagem) + + event_id.set_done( + status_code=resposta.cStat, + response=resposta.xMotivo, + protocol_date=fields.Datetime.to_string( + datetime.fromisoformat(resposta.dhRegEvento) + ), + protocol_number=resposta.nProt, + file_response_xml=processo.retorno.content.decode("utf-8"), + ) + + def _document_qrcode(self): + super()._document_qrcode() + + for record in self: + record.cte40_infCTeSupl = self.env[ + "l10n_br_fiscal.document.supplement" + ].create( + { + "qrcode": record.get_cte_qrcode(), + } + ) + + def get_cte_qrcode(self): + # if self.document_type != MODELO_FISCAL_CTE: + # return + + processador = self._processador() + # if self.nfe_transmission == "1": + # return processador.monta_qrcode(self.document_key) + return processador.monta_qrcode(self.document_key) + + # serialized_doc = self.serialize()[0] + # xml = processador.assina_raiz(serialized_doc, serialized_doc.infNFe.Id) + # return processador._generate_qrcode_contingency(serialized_doc, xml) + + # TODO: nao esta rodando direto.. corrigir + def _compute_cte40_infQ(self): + for record in self: + cargo_info_vals = [ + {"cte40_cUnid": "01", "cte40_tpMed": "Peso Bruto", "cte40_qCarga": 0}, + { + "cte40_cUnid": "01", + "cte40_tpMed": "Peso Base Calculado", + "cte40_qCarga": 0, + }, + {"cte40_cUnid": "01", "cte40_tpMed": "Peso Aferido", "cte40_qCarga": 0}, + {"cte40_cUnid": "00", "cte40_tpMed": "Cubagem", "cte40_qCarga": 0}, + {"cte40_cUnid": "03", "cte40_tpMed": "Unidade", "cte40_qCarga": 0}, + ] + + record.cte40_infQ = self.env["l10n_br_cte.cargo.quantity.infos"].create( + cargo_info_vals + ) + + def _need_compute_cte_tags(self): + if ( + self.state_edoc in [SITUACAO_EDOC_EM_DIGITACAO, SITUACAO_EDOC_A_ENVIAR] + and self.processador_edoc == PROCESSADOR_OCA + and self.document_type_id.code in ["57"] + and self.issuer == DOCUMENT_ISSUER_COMPANY + ): + return True + else: + return False + + # cte40_infAdFisco = fields.Text(related="additional_data") + + # def make_pdf(self): + # if not self.filtered(filter_processador_edoc_cte): + # return super().make_pdf() + + # file_pdf = self.file_report_id + # self.file_report_id = False + # file_pdf.unlink() + + # if self.authorization_file_id: + # arquivo = self.authorization_file_id + # xml_string = base64.b64decode(arquivo.datas).decode() + # else: + # arquivo = self.send_file_id + # xml_string = base64.b64decode(arquivo.datas).decode() + # # TODO: implementar temp_xml_autorizacao igual nfe ? + # # xml_string = self.temp_xml_autorizacao(xml_string) + + # pdf = Dacte(xml=xml_string).output() + + # self.file_report_id = self.env["ir.attachment"].create( + # { + # "name": self.document_key + ".pdf", + # "res_model": self._name, + # "res_id": self.id, + # "datas": base64.b64encode(pdf), + # "mimetype": "application/pdf", + # "type": "binary", + # } + # ) + + def _cte_response_add_proc(self, ws_response_process): + """ + Inject the final NF-e, tag `cteProc`, into the response. + """ + xml_soap = ws_response_process.retorno.content + tree_soap = etree.fromstring(xml_soap) + prot_element = tree_soap.xpath("//cte:protCTe", namespaces=CTE_XML_NAMESPACE)[0] + proc_xml = self._cte_create_proc(prot_element) + if proc_xml: + # it is not always possible to create cteProc. + parser = XmlParser() + proc = parser.from_string(proc_xml.decode(), CteProc) + ws_response_process.processo = proc + ws_response_process.processo_xml = proc_xml + + def _cte_create_proc(self, prot_element): + """ + Create the `cteProc` XML by combining the CT-e and the authorization protocol. + + This method decodes the saved `enviCTe` message, extracts the CTe> tag, + and combines it with the provided authorization protocol element to create + the `cteProc` XML, which represents the finalized CT-e document. + + Args: + prot_element: The XML element containing the authorization protocol. + + Returns: + The assembled `cteProc` XML, or None if the `send_file_id` data is not + found. + + Note: + Useful for recreating the final CT-e XML, as SEFAZ does not provide the + complete XML upon consultation, only the authorization protocol. + """ + self.ensure_one() + + if not self.send_file_id.datas: + _logger.info( + "CT-e data not found when trying to assemble the " + "xml with the authorization protocol (cteProc)" + ) + return None + + processor = self._processador() + + # Extract the tag from the `enviCTe` message, which represents the CT-e + xml_send = base64.b64decode(self.send_file_id.datas) + tree_send = etree.fromstring(xml_send) + doc_element = tree_send.xpath("//cte:CTe", namespaces=CTE_XML_NAMESPACE)[0] + + # Assemble the `cteProc` using the erpbrasil.edoc library. + proc_xml = processor.monta_cte_proc(doc=doc_element, prot=prot_element) + + return proc_xml diff --git a/l10n_br_cte/models/document_line.py b/l10n_br_cte/models/document_line.py index 22d2393668cc..df7ef721f09b 100644 --- a/l10n_br_cte/models/document_line.py +++ b/l10n_br_cte/models/document_line.py @@ -1,17 +1,15 @@ # Copyright 2023 KMEE # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -import sys -from odoo import api, fields +from odoo import fields -from odoo.addons.l10n_br_fiscal.constants.icms import ICMS_CST, ICMS_SN_CST from odoo.addons.spec_driven_model.models import spec_models class CTeLine(spec_models.StackedModel): _name = "l10n_br_fiscal.document.line" - _inherit = ["l10n_br_fiscal.document.line", "cte.40.tcte_imp"] - _stacked = "cte.40.tcte_imp" + _inherit = ["l10n_br_fiscal.document.line", "cte.40.tcte_vprest_comp"] + _stacked = "cte.40.tcte_vprest_comp" _field_prefix = "cte40_" _schema_name = "cte" _schema_version = "4.0.0" @@ -19,198 +17,13 @@ class CTeLine(spec_models.StackedModel): _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" _spec_tab_name = "CTe" _stacking_points = {} - _force_stack_paths = "tcte_imp.timp" + _stack_skip = ("cte40_Comp_vPrest_id",) _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" ########################## - # CT-e tag: vPrest + # CT-e tag: comp ########################## - cte40_vTPrest = fields.Monetary(string="vTPrest", related="amount_total") + cte40_xNome = fields.Text(related="name") - cte40_vRec = fields.Monetary( - related="price_gross", - string="vRec", - ) - - ################################################## - # CT-e tag: ICMS - # Grupo N01. Grupo Tributação do ICMS= 00 - # Grupo N02. Grupo Tributação do ICMS= 20 - # Grupo N03. Grupo Tributação do ICMS= 45 (40, 41 e 51) - # Grupo N04. Grupo Tributação do ICMS= 60 - # Grupo N05. Grupo Tributação do ICMS= 90 - ICMS outros - # Grupo N06. Grupo Tributação do ICMS= 90 - ICMS Outra UF - # Grupo N06. Grupo Tributação do ICMS= 01 - ISSN - ################################################# - - cte40_ICMS = fields.Many2one( - comodel_name="l10n_br_fiscal.document.line", compute="_compute_icms", store=True - ) - - def _compute_icms(self): - for doc in self: - doc.cte40_ICMS = doc - - cte40_choice_icms = fields.Selection( - selection=[ - ("cte40_ICMS00", "ICMS00"), - ("cte40_ICMS20", "ICMS20"), - ("cte40_ICMS45", "ICMS45"), - ("cte40_ICMS60", "ICMS60"), - ("cte40_ICMS90", "ICMS90"), - ("cte40_ICMSOutraUF", "ICMSOutraUF"), - ("cte40_ICMSSN", "ICMSSN"), - ], - string="Tipo de ICMS", - compute="_compute_choice_icms", - store=True, - ) - - cte40_CST = fields.Selection( - selection=[ - ("00", "00 - Tributação normal ICMS"), - ("20", "20 - Tributação com BC reduzida do ICMS"), - ("45", "45 - ICMS Isento, não Tributado ou diferido"), - ("60", "60 - ICMS cobrado por substituição tributária"), - ("90", "90 - ICMS outros"), - ("90", "90 - ICMS Outra UF"), - ("01", "01 - Simples Nacional"), - ], - string="Classificação Tributária do Serviço", - compute="_compute_choice_icms", - store=True, - ) - - cte40_vTotTrib = fields.Monetary(related="estimate_tax") - - cte40_pICMS = fields.Float(related="icms_percent", string="pICMS") - - cte40_vICMS = fields.Monetary(related="icms_value") - - # ICMS20 - ICMS90 - cte40_pRedBC = fields.Float( - related="icms_reduction", - ) - - cte40_vBC = fields.Monetary(related="icms_base") - - # ICMS60 - cte40_vBCSTRet = fields.Monetary(related="icmsst_wh_base") - - cte40_vICMSSTRet = fields.Monetary(related="icmsst_wh_value") - - # TODO cte40_pICMSTRet = fields.Monetary(related="") - - # ICMSSN - cte40_indSN = fields.Float(default=1) - - # ICMS NF - cte40_vBCST = fields.Monetary(related="icmsst_base") - - # ICMSOutraUF - # TODO - - ########################## - # CT-e tag: ICMS - # Compute Methods - ########################## - - @api.depends("icms_cst_id") - def _compute_choice_icms(self): - for record in self: - record.cte40_choice_icms = None - record.cte40_CST = None - if record.icms_cst_id.code in ICMS_CST: - if record.icms_cst_id.code in ["40", "41", "50"]: - record.cte40_choice_icms = "cte40_ICMS45" - record.cte40_CST = "45" - elif ( - record.icms_cst_id.code == "90" - and self.partner_id.state_id != self.company_id.state_id - ): - record.cte40_choice_icms = "cte40_ICMSOutraUF" - else: - record.cte40_choice_icms = "{}{}".format( - "cte40_ICMS", record.icms_cst_id.code - ) - record.cte40_CST = record.icms_cst_id.code - elif record.icms_cst_id.code in ICMS_SN_CST: - record.cte40_choice_icms = "cte40_ICMSSN" - record.cte40_CST = "90" - - def _export_fields_icms(self): - icms = { - "CST": self.cte40_CST, - "vBC": str("%.02f" % self.icms_base), - "pRedBC": str("%.04f" % self.icms_reduction), - "pICMS": str("%.04f" % self.icms_percent), - "vICMS": str("%.02f" % self.icms_value), - "vICMSSubstituto": str("%.02f" % self.icms_substitute), - "indSN": int(self.cte40_indSN), - "vBCSTRet": str("%.02f" % self.icmsst_wh_base), - "vICMSSTRet": str("%.02f" % self.icmsst_wh_value), - "pICMSSTRet": str("%.02f" % self.icmsst_wh_percent), - } - return icms - - def _export_fields_cte_40_timp(self, xsd_fields, class_obj, export_dict): - # TODO Not Implemented - if "cte40_ICMSOutraUF" in xsd_fields: - xsd_fields.remove("cte40_ICMSOutraUF") - - xsd_fields = [self.cte40_choice_icms] - icms_tag = ( - self.cte40_choice_icms.replace("cte40_", "") - .replace("ICMSSN", "Icmssn") - .replace("ICMS", "Icms") - ) - binding_module = sys.modules[self._binding_module] - icms = binding_module.Timp - icms_binding = getattr(icms, icms_tag) - icms_dict = self._export_fields_icms() - sliced_icms_dict = { - key: icms_dict.get(key) - for key in icms_binding.__dataclass_fields__.keys() - if icms_dict.get(key) - } - export_dict[icms_tag.upper()] = icms_binding(**sliced_icms_dict) - - ########################## - # CT-e tag: ICMSUFFim - ########################## - - cte40_vBCUFFim = fields.Monetary(related="icms_destination_base") - cte40_pFCPUFFim = fields.Monetary(compute="_compute_cte40_ICMSUFFim", store=True) - cte40_pICMSUFFim = fields.Monetary(compute="_compute_cte40_ICMSUFFim", store=True) - # TODO - # cte40_pICMSInter = fields.Selection( - # selection=[("0", "Teste")], - # compute="_compute_cte40_ICMSUFFim") - - def _compute_cte40_ICMSUFFim(self): - for record in self: - # if record.icms_origin_percent: - # record.cte40_pICMSInter = str("%.02f" % record.icms_origin_percent) - # else: - # record.cte40_pICMSInter = False - - record.cte40_pFCPUFFim = record.icmsfcp_percent - record.cte40_pICMSUFFim = record.icms_destination_percent - - cte40_vFCPUFfim = fields.Monetary(related="icmsfcp_value") - cte40_vICMSUFFim = fields.Monetary(related="icms_destination_value") - cte40_vICMSUFIni = fields.Monetary(related="icms_origin_value") - - ########################## - # CT-e tag: natCarga - ########################## - - cte40_xDime = fields.Char(compute="_compute_dime", store=True) - - def _compute_dime(self): - for record in self: - for package in record.product_id.packaging_ids: - record.cte40_xDime = ( - package.width + "X" + package.packaging_length + "X" + package.width - ) + cte40_vComp = fields.Monetary(related="amount_total") diff --git a/l10n_br_cte/models/document_related.py b/l10n_br_cte/models/document_related.py index f41e364f7686..5ab80e398d57 100644 --- a/l10n_br_cte/models/document_related.py +++ b/l10n_br_cte/models/document_related.py @@ -7,15 +7,12 @@ class CTeRelated(spec_models.StackedModel): - _name = "l10n_br_fiscal.document.related" _inherit = [ "l10n_br_fiscal.document.related", - "cte.40.tcte_infnfe", - "cte.40.tcte_infnf", - "cte.40.tcte_infq", + "cte.40.tcte_infdoc", ] - _stacked = "cte.40.tcte_infnfe" + _stacked = "cte.40.tcte_infdoc" _field_prefix = "cte40_" _schema_name = "cte" _schema_version = "4.0.0" @@ -24,51 +21,42 @@ class CTeRelated(spec_models.StackedModel): _spec_tab_name = "CTe" _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" - # infQ TODO computes/relateds - - cte40_tpMed = fields.Char() - - cte40_qCarga = fields.Float() + # InfNFe + cte40_chave = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_chave", + ) - cte40_cUnid = fields.Selection( - selection=[ - ("00", "M3"), - ("01", "KG"), - ("02", "TON"), - ("03", "UNIDADE"), - ("04", "LITROS"), - ("05", "MMBTU"), - ], + cte40_tpDoc = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_tpDoc", ) - # infCarga - cte40_prodPred = fields.Char(string="prodPred") + # infOutros - cte40_vCarga = fields.Monetary( - currency_field="currency_id", compute="_compute_vCarga", store=True - ) + cte40_descOutros = fields.Char(string="Descrição do documento") - currency_id = fields.Many2one( - comodel_name="res.currency", related="company_id.currency_id", readonly=True - ) + cte40_nDoc = fields.Char(string="Número", default="123123") - company_id = fields.Many2one( - comodel_name="res.company", - default=lambda self: self.env.company, + cte40_dEmi = fields.Date( + string="Data de Emissão", + help="Data de Emissão\nFormato AAAA-MM-DD", ) - # InfNFe - cte40_chave = fields.Char( - compute="_compute_cte_data", - inverse="_inverse_cte40_chave", + cte40_vDocFisc = fields.Monetary( + string="Valor do documento", + default=1000.0, + currency_field="brl_currency_id", ) - cte40_tpDoc = fields.Char( - compute="_compute_cte_data", - inverse="_inverse_cte40_tpDoc", + cte40_dPrev = fields.Date( + string="Data prevista de entrega", + help="Data prevista de entrega\nFormato AAAA-MM-DD", ) - cte40_infDoc = fields.Selection(related="cte40_choice_infNF_infNFE_infOutros") + cte40_infDoc = fields.Selection( + related="cte40_choice_infNF_infNFE_infOutros", string="infDoc" + ) # infCteNorm cte40_chCTe = fields.Char(compute="_compute_chCte", string="chCte") @@ -93,13 +81,9 @@ def _compute_chCTe(self): ], compute="_compute_cte_data", inverse="_inverse_cte40_choice_infNF_infNFE_infOutros", + string="CHOICE", ) - def _compute_vCarga(self): - for rec in self: - if rec.document_related_id: - rec.cte40_vCarga += rec.document_related_id.amount_price_gross - @api.depends("document_type_id") def _compute_cte_data(self): """Set schema data which are not just related fields""" diff --git a/l10n_br_cte/models/document_supplement.py b/l10n_br_cte/models/document_supplement.py index d4202faf2e78..10fd555c1514 100644 --- a/l10n_br_cte/models/document_supplement.py +++ b/l10n_br_cte/models/document_supplement.py @@ -10,13 +10,12 @@ class CTeSupplement(spec_models.StackedModel): _name = "l10n_br_fiscal.document.supplement" _inherit = ["l10n_br_fiscal.document.supplement", "cte.40.tcte_infctesupl"] _stacked = "cte.40.tcte_infctesupl" + _field_prefix = "cte40_" _schema_name = "cte" _schema_version = "4.0.0" _odoo_module = "l10n_br_cte" _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" - _field_prefix = "cte40_" _spec_tab_name = "CTe" - _description = "Informações Complementares do Documento Fiscal" + _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" cte40_qrCodCTe = fields.Char(related="qrcode") diff --git a/l10n_br_cte/models/normal_cte_infos.py b/l10n_br_cte/models/normal_cte_infos.py index 59c470e866f8..71165a69ad6b 100644 --- a/l10n_br_cte/models/normal_cte_infos.py +++ b/l10n_br_cte/models/normal_cte_infos.py @@ -56,7 +56,12 @@ class CTeNormalInfos(spec_models.StackedModel): cte40_infNFe = fields.One2many( comodel_name="l10n_br_fiscal.document.related", - related="document_id.document_related_ids", + related="document_id.cte40_infNFe", + ) + + cte40_infOutros = fields.One2many( + comodel_name="l10n_br_fiscal.document.related", + related="document_id.cte40_infOutros", ) cte40_versaoModal = fields.Char(related="document_id.cte40_versaoModal") @@ -91,7 +96,9 @@ class CTeNormalInfos(spec_models.StackedModel): related="document_id.modal_aquaviario_id", ) - cte40_vPrest = fields.Monetary(related="document_id.cte40_vPrest") + cte40_vPrest = fields.Monetary( + related="document_id.cte40_vTPrest" + ) # TODO: avaliar melhor cte40_vAFRMM = fields.Monetary(related="document_id.cte40_vAFRMM") diff --git a/l10n_br_cte/models/res_company.py b/l10n_br_cte/models/res_company.py index cc465aa6f043..c07ec7c855f8 100644 --- a/l10n_br_cte/models/res_company.py +++ b/l10n_br_cte/models/res_company.py @@ -1,48 +1,12 @@ # Copyright 2023 KMEE INFORMATICA LTDA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields +from odoo import fields, models -from odoo.addons.spec_driven_model.models import spec_models - - -class ResCompany(spec_models.SpecModel): +class ResCompany(models.Model): _name = "res.company" - _inherit = ["res.company", "cte.40.tcte_emit", "cte.40.tendeemi", "cte.40.ferroenv"] - _cte_search_keys = ["cte40_CNPJ", "cte40_xNome", "cte40_xFant"] - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" - _field_prefix = "cte40_" - - ########################## - # CT-e spec fields - ########################## - - cte40_CNPJ = fields.Char( - related="partner_id.cte40_CNPJ", - ) - cte40_CPF = fields.Char( - related="partner_id.cte40_CPF", - ) - cte40_IE = fields.Char( - related="partner_id.cte40_IE", - ) - cte40_xNome = fields.Char( - related="partner_id.legal_name", - ) - cte40_xFant = fields.Char( - related="partner_id.name", - ) - cte40_CRT = fields.Selection( - related="tax_framework", - ) - - cte40_enderEmit = fields.Many2one( - comodel_name="res.partner", - related="partner_id", - ) - - cte40_enderToma = fields.Many2one(comodel_name="res.partner", related="partner_id") + _inherit = ["res.company"] ########################## # CT-e models fields diff --git a/l10n_br_cte/models/res_partner.py b/l10n_br_cte/models/res_partner.py index 2d636ae6bf29..a669c1e2b692 100644 --- a/l10n_br_cte/models/res_partner.py +++ b/l10n_br_cte/models/res_partner.py @@ -16,7 +16,6 @@ class ResPartner(spec_models.SpecModel): - _name = "res.partner" _inherit = [ "res.partner", @@ -69,6 +68,10 @@ class ResPartner(spec_models.SpecModel): comodel_name="res.partner", compute="_compute_cte40_ender" ) + cte40_enderReceb = fields.Many2one( + comodel_name="res.partner", compute="_compute_cte40_ender" + ) + cte40_enderFerro = fields.Many2one( comodel_name="res.partner", compute="_compute_cte40_ender" ) @@ -85,7 +88,6 @@ class ResPartner(spec_models.SpecModel): compute="_compute_cep", inverse="_inverse_cte40_CEP", compute_sudo=True, - store=True, ) cte40_cPais = fields.Char( related="country_id.bc_code", @@ -98,6 +100,14 @@ class ResPartner(spec_models.SpecModel): cte40_xNome = fields.Char(related="legal_name") + cte40_xContato = fields.Char(related="legal_name") + + cte40_email = fields.Char(related="email") + + cte40_fone = fields.Char( + compute="_compute_cte_data", inverse="_inverse_cte40_fone", compute_sudo=True + ) + def _compute_cte40_IE(self): for rec in self: rec.cte40_IE = str(rec.inscr_est).replace(".", "") @@ -108,6 +118,7 @@ def _compute_cte40_ender(self): rec.cte40_enderReme = rec.id rec.cte40_enderDest = rec.id rec.cte40_enderExped = rec.id + rec.cte40_enderReceb = rec.id rec.cte40_enderFerro = rec.id @api.depends("company_type", "inscr_est", "cnpj_cpf", "country_id") @@ -121,6 +132,7 @@ def _compute_cte_data(self): else: rec.cte40_CNPJ = None rec.cte40_CPF = cnpj_cpf + rec.cte40_fone = punctuation_rm(rec.phone or "").replace(" ", "") def _inverse_cte40_CEP(self): for rec in self: @@ -128,11 +140,24 @@ def _inverse_cte40_CEP(self): country_code = rec.country_id.code if rec.country_id else "BR" rec.zip = format_zipcode(rec.cte40_CEP, country_code) + def _inverse_cte40_fone(self): + for rec in self: + if rec.cte40_fone: + rec.phone = rec.nfe40_fone + def _compute_cep(self): for rec in self: rec.cte40_CEP = punctuation_rm(rec.zip) def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + if ( + xsd_field == "cte40_xNome" + and class_obj._name + in ["cte.40.tcte_rem", "cte.40.tcte_dest", "cte.40.exped", "cte.40.receb"] + and self.env.context.get("tpAmb") == "2" + ): + return "CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL" + if not self.cnpj_cpf and self.parent_id: cnpj_cpf = punctuation_rm(self.parent_id.cnpj_cpf) else: diff --git a/l10n_br_cte/views/cte_document.xml b/l10n_br_cte/views/cte_document.xml index 26a63c9c33be..1f092ef14c5e 100644 --- a/l10n_br_cte/views/cte_document.xml +++ b/l10n_br_cte/views/cte_document.xml @@ -21,6 +21,17 @@ + + + + + + + + + + + @@ -77,13 +88,13 @@ - + - + @@ -95,9 +106,9 @@ - + - + @@ -107,7 +118,7 @@ - + @@ -123,7 +134,7 @@ /> - + diff --git a/l10n_br_cte/views/res_company.xml b/l10n_br_cte/views/res_company.xml index 8907285e0e3a..b4faac397deb 100644 --- a/l10n_br_cte/views/res_company.xml +++ b/l10n_br_cte/views/res_company.xml @@ -12,19 +12,17 @@ - - + + - - - - - + + + diff --git a/l10n_br_cte/wizards/document_correction_wizard.xml b/l10n_br_cte/wizards/document_correction_wizard.xml new file mode 100644 index 000000000000..7b93ccffab13 --- /dev/null +++ b/l10n_br_cte/wizards/document_correction_wizard.xml @@ -0,0 +1,30 @@ + + + + + + l10n_br_fiscal.document.correction.wizard + + + + + + + +
+ Por favor, para carta de correção de CT-e, no campo Justificativa informe por linha (Grupo Alterado;Campo Alterado;Valor Alterado).
+ Exemplo:
+ compl;xObs;Nova Observação
+ ide;cfop;6353 +
+
+
+
+
+ +
From 07652db1f386d9383d877fb1f04006d8dee88126 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 24 Oct 2024 17:34:45 -0300 Subject: [PATCH 06/27] [RFC] l10n_br_fiscal: escodoo refactor --- l10n_br_fiscal/models/document.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/l10n_br_fiscal/models/document.py b/l10n_br_fiscal/models/document.py index dddb2056a8d7..dfe3af9c1755 100644 --- a/l10n_br_fiscal/models/document.py +++ b/l10n_br_fiscal/models/document.py @@ -200,12 +200,12 @@ class Document(models.Model): transport_modal = fields.Selection( selection=[ - ("1", "Rodoviário"), - ("2", "Aéreo"), - ("3", "Aquaviário"), - ("4", "Ferroviário"), - ("5", "Dutoviário"), - ("6", "Multimodal"), + ("01", "Rodoviário"), + ("02", "Aéreo"), + ("03", "Aquaviário"), + ("04", "Ferroviário"), + ("05", "Dutoviário"), + ("06", "Multimodal"), ], string="Modal de Transporte", ) From c0ae5e5de5484f11798592f2a3c20a7f718304e4 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 24 Oct 2024 17:49:42 -0300 Subject: [PATCH 07/27] [FIX] l10n_br_cte: fix pre-commit --- l10n_br_cte/models/aereo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/l10n_br_cte/models/aereo.py b/l10n_br_cte/models/aereo.py index f155c5b7230a..894273a09dd5 100644 --- a/l10n_br_cte/models/aereo.py +++ b/l10n_br_cte/models/aereo.py @@ -54,9 +54,9 @@ class Peri(spec_models.StackedModel): _odoo_module = "l10n_br_cte" _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" _spec_tab_name = "CTe" - _description = """Preenchido quando for transporte de produtos classificados pela ONU como - perigosos. O preenchimento desses campos não desobriga a empresa aérea de emitir os demais - documentos que constam na legislação vigente.""" + _description = """Preenchido quando for transporte de produtos classificados pela + ONU como perigosos. O preenchimento desses campos não desobriga a empresa aérea de + emitir os demais documentos que constam na legislação vigente.""" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") From 541a0256a23a121bfd4831d615fa89c055550d3e Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Fri, 25 Oct 2024 17:27:03 -0300 Subject: [PATCH 08/27] [RFC] l10n_br_cte: multi-schema refactor --- l10n_br_cte/hooks.py | 41 +++++-- l10n_br_cte/models/__init__.py | 5 +- l10n_br_cte/models/aereo.py | 26 ++--- l10n_br_cte/models/aquaviario.py | 13 +-- l10n_br_cte/models/document.py | 102 +++++++++--------- .../models/document_cargo_quantity_infos.py | 1 - .../{comment.py => document_comment.py} | 18 ++-- l10n_br_cte/models/document_line.py | 18 ++-- l10n_br_cte/models/document_related.py | 13 +-- l10n_br_cte/models/document_supplement.py | 13 +-- .../models/document_transported_vehicles.py | 1 - l10n_br_cte/models/dutoviario.py | 6 +- l10n_br_cte/models/ferroviario.py | 13 +-- l10n_br_cte/models/normal_cte_infos.py | 15 ++- l10n_br_cte/models/res_partner.py | 2 - l10n_br_cte/models/rodoviario.py | 26 ++--- 16 files changed, 152 insertions(+), 161 deletions(-) rename l10n_br_cte/models/{comment.py => document_comment.py} (73%) diff --git a/l10n_br_cte/hooks.py b/l10n_br_cte/hooks.py index 572921aaf117..6348b345b35b 100644 --- a/l10n_br_cte/hooks.py +++ b/l10n_br_cte/hooks.py @@ -1,13 +1,42 @@ # Copyright (C) 2019-2020 - Raphael Valyi Akretion # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +import logging + +import nfelib +import pkg_resources +from nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00 import Tcte + from odoo import SUPERUSER_ID, api +from odoo.exceptions import ValidationError + +_logger = logging.getLogger(__name__) def post_init_hook(cr, registry): env = api.Environment(cr, SUPERUSER_ID, {}) - env["cte.40.tcte_infcte"]._register_hook() - # hooks.register_hook( - # env, - # "l10n_br_cte", - # "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00", - # ) + cr.execute("select demo from ir_module_module where name='l10n_br_cte';") + is_demo = cr.fetchone()[0] + if is_demo: + res_items = ( + "cte", + "samples", + "v3_0", + "43120178408960000182570010000000041000000047-cte.xml", + ) + resource_path = "/".join(res_items) + doc_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) + binding = Tcte.from_xml(doc_stream.read().decode()) + document_number = binding.infCte.ide.nCTE + existing_docs = env["l10n_br_fiscal.document"].search( + [("document_number", "=", document_number)] + ) + try: + existing_docs.unlink() + doc = ( + env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type="in") + .build_from_binding("cte", "40", binding.infMDFe) + ) + _logger.info(doc.cte40_emit.cte40_CNPJ) + except ValidationError: + _logger.info(f"CTE-e already {document_number} imported by hooks") diff --git a/l10n_br_cte/models/__init__.py b/l10n_br_cte/models/__init__.py index 40014e44edb5..2082b680910c 100644 --- a/l10n_br_cte/models/__init__.py +++ b/l10n_br_cte/models/__init__.py @@ -13,4 +13,7 @@ from . import document_supplement from . import document_transported_vehicles from . import normal_cte_infos -from . import comment +from . import document_comment + +spec_schema = "cte" +spec_version = "40" diff --git a/l10n_br_cte/models/aereo.py b/l10n_br_cte/models/aereo.py index 894273a09dd5..f5f69c888dba 100644 --- a/l10n_br_cte/models/aereo.py +++ b/l10n_br_cte/models/aereo.py @@ -10,16 +10,13 @@ class Aereo(spec_models.StackedModel): _name = "l10n_br_cte.modal.aereo" _inherit = "cte.40.aereo" - _stacked = "cte.40.aereo" - _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aereo_v4_00" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" - _spec_tab_name = "CTe" _description = "Modal Aereo CTe" + _cte40_stacking_mixin = "cte.40.aereo" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" + ) + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") cte40_nMinu = fields.Char(related="document_id.cte40_nMinu") @@ -46,18 +43,15 @@ def _prepare_dacte_values(self): class Peri(spec_models.StackedModel): _name = "l10n_br_cte.modal.aereo.peri" _inherit = "cte.40.peri" - _stacked = "cte.40.peri" - _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aereo_v4_00" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" - _spec_tab_name = "CTe" _description = """Preenchido quando for transporte de produtos classificados pela ONU como perigosos. O preenchimento desses campos não desobriga a empresa aérea de emitir os demais documentos que constam na legislação vigente.""" + _cte40_stacking_mixin = "cte.40.peri" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" + ) + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") cte40_nONU = fields.Char(required=True) diff --git a/l10n_br_cte/models/aquaviario.py b/l10n_br_cte/models/aquaviario.py index 54709b03fba0..be9f52967d74 100644 --- a/l10n_br_cte/models/aquaviario.py +++ b/l10n_br_cte/models/aquaviario.py @@ -9,16 +9,13 @@ class Aquav(spec_models.StackedModel): _name = "l10n_br_cte.modal.aquav" _inherit = "cte.40.aquav" - _stacked = "cte.40.aquav" - _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aquaviario_v4_00" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aquaviario_v4_00" - _spec_tab_name = "CTe" _description = "Modal Aquaviário CTe" + _cte40_stacking_mixin = "cte.40.aquav" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aquaviario_v4_00" + ) + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") cte40_vAFRMM = fields.Monetary(related="document_id.cte40_vAFRMM") diff --git a/l10n_br_cte/models/document.py b/l10n_br_cte/models/document.py index 66c6b4b11a9a..f2945c66e594 100644 --- a/l10n_br_cte/models/document.py +++ b/l10n_br_cte/models/document.py @@ -83,29 +83,24 @@ class CTe(spec_models.StackedModel): "cte.40.tcte_imp", "cte.40.tcte_fat", ] - _stacked = "cte.40.tcte_infcte" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _spec_tab_name = "CTe" - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" - _stack_skip = ( + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_infcte" + _cte40_stacking_skip_paths = ( "cte40_fluxo", "cte40_semData", "cte40_noInter", "cte40_comHora", "cte40_noPeriodo", ) - _cte_search_keys = ["cte40_Id"] - # all m2o at this level will be stacked even if not required: - _force_stack_paths = ( + _cte40_stacking_force_paths = ( "infcte.compl", "infcte.compl.entrega" "infcte.vprest", "infcte.imp", ) + _cte_search_keys = ["cte40_Id"] INFCTE_TREE = """ > infCte @@ -1107,7 +1102,6 @@ def _compute_cte40_RNTRC(self): ########################## def _export_fields_cte_40_tcte_infmodal(self, xsd_fields, class_obj, export_dict): - self = self.with_context(module="l10n_br_cte") if self.cte40_modal == "01": export_dict["any_element"] = self._export_modal_rodoviario() elif self.cte40_modal == "02": @@ -1123,7 +1117,7 @@ def _export_modal_aereo(self): if not self.modal_aereo_id: self.modal_aereo_id = self.modal_aereo_id.create({"document_id": self.id}) - return self.modal_aereo_id.export_ds()[0] + return self.modal_aereo_id._build_binding("cte", "40") def _export_modal_ferroviario(self): if not self.modal_ferroviario_id: @@ -1131,7 +1125,7 @@ def _export_modal_ferroviario(self): {"document_id": self.id} ) - return self.modal_ferroviario_id.export_ds()[0] + return self.modal_ferroviario_id._build_binding("cte", "40") def _export_modal_aquaviario(self): if not self.modal_aquaviario_id: @@ -1139,7 +1133,7 @@ def _export_modal_aquaviario(self): {"document_id": self.id} ) - return self.modal_aquaviario_id.export_ds()[0] + return self.modal_aquaviario_id._build_binding("cte", "40") def _export_modal_rodoviario(self): if not self.modal_rodoviario_id: @@ -1147,7 +1141,7 @@ def _export_modal_rodoviario(self): {"document_id": self.id} ) - return self.modal_rodoviario_id.export_ds()[0] + return self.modal_rodoviario_id._build_binding("cte", "40") def _export_modal_dutoviario(self): if not self.modal_dutoviario_id: @@ -1155,7 +1149,7 @@ def _export_modal_dutoviario(self): {"document_id": self.id} ) - return self.modal_dutoviario_id.export_ds()[0] + return self.modal_dutoviario_id._build_binding("cte", "40") ################################ # Framework Spec model's methods @@ -1182,15 +1176,15 @@ def _serialize(self, edocs): for record in self.with_context(lang="pt_BR").filtered( filter_processador_edoc_cte ): - inf_cte = record.export_ds()[0] + inf_cte = record._build_binding("cte", "40") inf_cte_supl = None if record.cte40_infCTeSupl: - inf_cte_supl = record.cte40_infCTeSupl.export_ds()[0] + inf_cte_supl = record.cte40_infCTeSupl._build_binding("cte", "40") cte = Cte(infCte=inf_cte, infCTeSupl=inf_cte_supl, signature=None) edocs.append(cte) return edocs - def _processador(self): + def _edoc_processor(self): if not self.company_id.certificate_nfe_id: raise UserError(_("Certificado não encontrado")) @@ -1209,7 +1203,7 @@ def _document_export(self, pretty_print=True): result = super()._document_export() for record in self.filtered(filter_processador_edoc_cte): edoc = record.serialize()[0] - processador = record._processador() + processador = record._edoc_processor() xml_file = edoc.to_xml() event_id = self.event_ids.create_event_save_xml( company_id=self.company_id, @@ -1222,26 +1216,26 @@ def _document_export(self, pretty_print=True): ) record.authorization_event_id = event_id xml_assinado = processador.assina_raiz(edoc, edoc.infCte.Id) - self._valida_xml(xml_assinado) + self._validate_xml(xml_assinado) return result - def _valida_xml(self, xml_file): + def _validate_xml(self, xml_file): self.ensure_one() erros = Cte.schema_validation(xml_file) erros = "\n".join(erros) self.write({"xml_error_message": erros or False}) - def atualiza_status_cte(self, processo): + def update_status_cte(self, process): self.ensure_one() - if hasattr(processo, "protocolo"): - infProt = processo.protocolo.infProt + if hasattr(process, "protocolo"): + infProt = process.protocolo.infProt else: - infProt = processo.resposta.protCTe.infProt + infProt = process.resposta.protCTe.infProt if infProt.cStat in AUTORIZADO: state = SITUACAO_EDOC_AUTORIZADA - self._cte_response_add_proc(processo) + self._cte_response_add_proc(process) elif infProt.cStat in DENEGADO: state = SITUACAO_EDOC_DENEGADA else: @@ -1259,7 +1253,7 @@ def atualiza_status_cte(self, processo): response=infProt.xMotivo, protocol_date=protocol_date, protocol_number=infProt.nProt, - file_response_xml=processo.processo_xml.decode("utf-8"), + file_response_xml=process.processo_xml.decode("utf-8"), ) self.write( { @@ -1274,25 +1268,25 @@ def _eletronic_document_send(self): for record in self.filtered(filter_processador_edoc_cte): if record.xml_error_message: return - processador = record._processador() + processador = record._edoc_processor() for edoc in record.serialize(): - processo = None + process = None for p in processador.processar_documento(edoc): - processo = p - if processo.webservice == "cteRecepcaoLote": + process = p + if process.webservice == "cteRecepcaoLote": record.authorization_event_id._save_event_file( - processo.envio_xml, "xml" + process.envio_xml, "xml" ) - if processo.resposta.cStat in LOTE_PROCESSADO + ["100"]: - record.atualiza_status_cte(processo) + if process.resposta.cStat in LOTE_PROCESSADO + ["100"]: + record.update_status_cte(process) - elif processo.resposta.cStat in DENEGADO: + elif process.resposta.cStat in DENEGADO: record._change_state(SITUACAO_EDOC_DENEGADA) record.write( { - "status_code": processo.resposta.cStat, - "status_name": processo.resposta.xMotivo, + "status_code": process.resposta.cStat, + "status_name": process.resposta.xMotivo, } ) @@ -1300,8 +1294,8 @@ def _eletronic_document_send(self): record._change_state(SITUACAO_EDOC_REJEITADA) record.write( { - "status_code": processo.resposta.cStat, - "status_name": processo.resposta.xMotivo, + "status_code": process.resposta.cStat, + "status_name": process.resposta.xMotivo, } ) @@ -1314,7 +1308,7 @@ def _document_cancel(self, justificative): def _cte_cancel(self): self.ensure_one() - processador = self._processador() + processador = self._edoc_processor() if not self.authorization_protocol: raise UserError(_("Authorization Protocol Not Found!")) @@ -1324,17 +1318,17 @@ def _cte_cancel(self): protocolo_autorizacao=self.authorization_protocol, justificativa=self.cancel_reason.replace("\n", "\\n"), ) - processo = processador.enviar_lote_evento(lista_eventos=[evento]) + process = processador.enviar_lote_evento(lista_eventos=[evento]) self.cancel_event_id = self.event_ids.create_event_save_xml( company_id=self.company_id, environment=(EVENT_ENV_PROD if self.cte40_tpAmb == "1" else EVENT_ENV_HML), event_type="2", - xml_file=processo.envio_xml, + xml_file=process.envio_xml, document_id=self, ) - resposta = processo.resposta.infEvento + resposta = process.resposta.infEvento if resposta.cStat not in CANCELADO: mensagem = "Erro no cancelamento" @@ -1356,7 +1350,7 @@ def _cte_cancel(self): datetime.fromisoformat(resposta.dhRegEvento) ), protocol_number=resposta.nProt, - file_response_xml=processo.retorno.content.decode("utf-8"), + file_response_xml=process.retorno.content.decode("utf-8"), ) def _document_correction(self, justificative): @@ -1368,7 +1362,7 @@ def _document_correction(self, justificative): def _cte_correction(self, justificative): self.ensure_one() - processador = self._processador() + processador = self._edoc_processor() numeros = self.event_ids.filtered( lambda e: e.type == "14" and e.state == "done" @@ -1382,19 +1376,19 @@ def _cte_correction(self, justificative): justificativa=justificative.replace("\n", "\\n"), sequencia=sequence, ) - processo = processador.enviar_lote_evento(lista_eventos=[evento]) + process = processador.enviar_lote_evento(lista_eventos=[evento]) # Gravamos o arquivo no disco e no filestore ASAP. event_id = self.event_ids.create_event_save_xml( company_id=self.company_id, environment=(EVENT_ENV_PROD if self.cte40_tpAmb == "1" else EVENT_ENV_HML), event_type="14", - xml_file=processo.envio_xml, + xml_file=process.envio_xml, document_id=self, sequence=sequence, justification=justificative, ) - resposta = processo.resposta.infEvento + resposta = process.resposta.infEvento if resposta.cStat not in EVENTO_RECEBIDO and not ( resposta.chCTe == self.document_key @@ -1411,7 +1405,7 @@ def _cte_correction(self, justificative): datetime.fromisoformat(resposta.dhRegEvento) ), protocol_number=resposta.nProt, - file_response_xml=processo.retorno.content.decode("utf-8"), + file_response_xml=process.retorno.content.decode("utf-8"), ) def _document_qrcode(self): @@ -1430,7 +1424,7 @@ def get_cte_qrcode(self): # if self.document_type != MODELO_FISCAL_CTE: # return - processador = self._processador() + processador = self._edoc_processor() # if self.nfe_transmission == "1": # return processador.monta_qrcode(self.document_key) return processador.monta_qrcode(self.document_key) @@ -1544,7 +1538,7 @@ def _cte_create_proc(self, prot_element): ) return None - processor = self._processador() + processor = self._edoc_processor() # Extract the tag from the `enviCTe` message, which represents the CT-e xml_send = base64.b64decode(self.send_file_id.datas) diff --git a/l10n_br_cte/models/document_cargo_quantity_infos.py b/l10n_br_cte/models/document_cargo_quantity_infos.py index 44ea62fba70b..57b5e99397c2 100644 --- a/l10n_br_cte/models/document_cargo_quantity_infos.py +++ b/l10n_br_cte/models/document_cargo_quantity_infos.py @@ -9,7 +9,6 @@ class CTeCargoQuantityInfos(spec_models.SpecModel): _name = "l10n_br_cte.cargo.quantity.infos" _inherit = "cte.40.tcte_infq" - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" _description = "Informações de quantidades da Carga do CT-e" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/models/comment.py b/l10n_br_cte/models/document_comment.py similarity index 73% rename from l10n_br_cte/models/comment.py rename to l10n_br_cte/models/document_comment.py index dba4685a3278..215ed802dec5 100644 --- a/l10n_br_cte/models/comment.py +++ b/l10n_br_cte/models/document_comment.py @@ -9,16 +9,14 @@ class CTeComment(spec_models.StackedModel): _name = "l10n_br_fiscal.comment" _inherit = ["l10n_br_fiscal.comment", "cte.40.tcte_obscont", "cte.40.tcte_obsfisco"] - _stacked = "cte.40.tcte_obscont" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _spec_tab_name = "CTe" - _stacking_points = {} - _stack_skip = ("cte40_ObsCont_compl_id", "cte40_ObsFisco_compl_id") - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_obscont" + + # _stacking_points = {} + _cte40_stacking_skip_paths = ("cte40_ObsCont_compl_id", "cte40_ObsFisco_compl_id") cte40_xCampo = fields.Char() diff --git a/l10n_br_cte/models/document_line.py b/l10n_br_cte/models/document_line.py index df7ef721f09b..f1a2c2ed53ad 100644 --- a/l10n_br_cte/models/document_line.py +++ b/l10n_br_cte/models/document_line.py @@ -9,16 +9,14 @@ class CTeLine(spec_models.StackedModel): _name = "l10n_br_fiscal.document.line" _inherit = ["l10n_br_fiscal.document.line", "cte.40.tcte_vprest_comp"] - _stacked = "cte.40.tcte_vprest_comp" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _spec_tab_name = "CTe" - _stacking_points = {} - _stack_skip = ("cte40_Comp_vPrest_id",) - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_vprest_comp" + + # _stacking_points = {} + _cte40_stacking_skip_paths = ("cte40_Comp_vPrest_id",) ########################## # CT-e tag: comp diff --git a/l10n_br_cte/models/document_related.py b/l10n_br_cte/models/document_related.py index 5ab80e398d57..f68d6770d7d0 100644 --- a/l10n_br_cte/models/document_related.py +++ b/l10n_br_cte/models/document_related.py @@ -12,14 +12,11 @@ class CTeRelated(spec_models.StackedModel): "l10n_br_fiscal.document.related", "cte.40.tcte_infdoc", ] - _stacked = "cte.40.tcte_infdoc" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _spec_tab_name = "CTe" - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_infdoc" # InfNFe cte40_chave = fields.Char( diff --git a/l10n_br_cte/models/document_supplement.py b/l10n_br_cte/models/document_supplement.py index 10fd555c1514..9625c7441c19 100644 --- a/l10n_br_cte/models/document_supplement.py +++ b/l10n_br_cte/models/document_supplement.py @@ -9,13 +9,10 @@ class CTeSupplement(spec_models.StackedModel): _name = "l10n_br_fiscal.document.supplement" _inherit = ["l10n_br_fiscal.document.supplement", "cte.40.tcte_infctesupl"] - _stacked = "cte.40.tcte_infctesupl" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _spec_tab_name = "CTe" - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" + + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_mixin = "cte.40.tcte_infctesupl" cte40_qrCodCTe = fields.Char(related="qrcode") diff --git a/l10n_br_cte/models/document_transported_vehicles.py b/l10n_br_cte/models/document_transported_vehicles.py index bbb6af6bfd07..004ce2627172 100644 --- a/l10n_br_cte/models/document_transported_vehicles.py +++ b/l10n_br_cte/models/document_transported_vehicles.py @@ -9,7 +9,6 @@ class CTeCargoQuantityInfos(spec_models.SpecModel): _name = "l10n_br_cte.transported.vehicles" _inherit = "cte.40.veicnovos" - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" _description = "Informações dos veículos transportados" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/models/dutoviario.py b/l10n_br_cte/models/dutoviario.py index 8d980571c62c..51d479d08b61 100644 --- a/l10n_br_cte/models/dutoviario.py +++ b/l10n_br_cte/models/dutoviario.py @@ -6,12 +6,14 @@ from odoo.addons.spec_driven_model.models import spec_models -class Duto(spec_models.SpecModel): +class Duto(spec_models.StackedModel): _name = "l10n_br_cte.modal.duto" _inherit = "cte.40.duto" - _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_dutoviario_v4_00" _description = "Modal Dutoviario CTe" + _cte40_stacking_mixin = "cte.40.duto" + _cte40_odoo_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_duto_v4_00" + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") cte40_dIni = fields.Date(related="document_id.cte40_dIni") diff --git a/l10n_br_cte/models/ferroviario.py b/l10n_br_cte/models/ferroviario.py index d18932706e1f..d3048e812212 100644 --- a/l10n_br_cte/models/ferroviario.py +++ b/l10n_br_cte/models/ferroviario.py @@ -10,17 +10,12 @@ class Ferrov(spec_models.StackedModel): _name = "l10n_br_cte.modal.ferrov" _inherit = "cte.40.ferrov" - _stacked = "cte.40.ferrov" - _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_ferroviario_v4_00" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = ( + _description = "Modal Ferroviario CTe" + + _cte40_stacking_mixin = "cte.40.ferrov" + _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_ferroviario_v4_00" ) - _spec_tab_name = "CTe" - _description = "Modal Ferroviario CTe" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/models/normal_cte_infos.py b/l10n_br_cte/models/normal_cte_infos.py index 71165a69ad6b..9a893311c3b4 100644 --- a/l10n_br_cte/models/normal_cte_infos.py +++ b/l10n_br_cte/models/normal_cte_infos.py @@ -9,16 +9,13 @@ class CTeNormalInfos(spec_models.StackedModel): _name = "l10n_br_cte.normal.infos" _inherit = ["cte.40.tcte_infctenorm"] - _stacked = "cte.40.tcte_infctenorm" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" - _spec_tab_name = "CTe" _description = "Grupo de informações do CTe Normal e Substituto" - _force_stack_paths = "infctenorm.infdoc" + + _cte40_stacking_mixin = "cte.40.tcte_infctenorm" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_stacking_force_paths = "infctenorm.infdoc" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/models/res_partner.py b/l10n_br_cte/models/res_partner.py index a669c1e2b692..aab363047245 100644 --- a/l10n_br_cte/models/res_partner.py +++ b/l10n_br_cte/models/res_partner.py @@ -28,8 +28,6 @@ class ResPartner(spec_models.SpecModel): "cte.40.tenderfer", ] _cte_search_keys = ["cte40_CNPJ", "cte40_CPF", "cte40_xNome"] - _binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" - _field_prefix = "cte40_" cte40_choice_cnpj_cpf = fields.Selection( selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], diff --git a/l10n_br_cte/models/rodoviario.py b/l10n_br_cte/models/rodoviario.py index 7a52647dc089..11d63261a597 100644 --- a/l10n_br_cte/models/rodoviario.py +++ b/l10n_br_cte/models/rodoviario.py @@ -11,16 +11,13 @@ class Rodo(spec_models.StackedModel): _name = "l10n_br_cte.modal.rodo" _inherit = "cte.40.rodo" - _stacked = "cte.40.rodo" - _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_rodoviario_v4_00" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" - _spec_tab_name = "CTe" _description = "Modal Rodoviario CTe" + _cte40_stacking_mixin = "cte.40.rodo" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" + ) + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") cte40_RNTRC = fields.Char(related="document_id.cte40_RNTRC") @@ -31,16 +28,13 @@ class Rodo(spec_models.StackedModel): class Occ(spec_models.StackedModel): _name = "l10n_br_cte.modal.rodo.occ" _inherit = "cte.40.occ" - _stacked = "cte.40.occ" - _binding_module = "nfelib.cte.bindings.v4_0.cte_modal_rodoviario_v4_00" - _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" - _spec_tab_name = "CTe" _description = "Ordens de Coleta associados" + _cte40_stacking_mixin = "cte.40.occ" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" + ) + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") cte40_serie = fields.Char(string="Série da OCC") From 4f5c689b22d5f73519479130b2cd602619640c8f Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Sat, 26 Oct 2024 18:05:03 -0300 Subject: [PATCH 09/27] [IMP] l10n_br_cte: add tests --- l10n_br_cte/__manifest__.py | 2 +- l10n_br_cte/hooks.py | 6 +- l10n_br_cte/models/dutoviario.py | 4 +- l10n_br_cte/tests/__init__.py | 6 + ...8960000182570010000000041000000047-cte.xml | 162 ++++++++++++ ...6092000173570010000000031000000020-cte.XML | 130 ++++++++++ l10n_br_cte/tests/test_cte_document.py | 86 +++++++ l10n_br_cte/tests/test_cte_import.py | 73 ++++++ l10n_br_cte/tests/test_cte_res_partner.py | 66 +++++ l10n_br_cte/tests/test_cte_serialize.py | 234 ++++++++++++++++++ l10n_br_cte/tests/test_cte_serialize_lc.py | 28 +++ l10n_br_cte/tests/test_cte_serialize_sn.py | 29 +++ l10n_br_cte/tests/test_cte_structure.py | 160 ++++++++++++ 13 files changed, 981 insertions(+), 5 deletions(-) create mode 100644 l10n_br_cte/tests/__init__.py create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/43120178408960000182570010000000041000000047-cte.xml create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/51160624686092000173570010000000031000000020-cte.XML create mode 100644 l10n_br_cte/tests/test_cte_document.py create mode 100644 l10n_br_cte/tests/test_cte_import.py create mode 100644 l10n_br_cte/tests/test_cte_res_partner.py create mode 100644 l10n_br_cte/tests/test_cte_serialize.py create mode 100644 l10n_br_cte/tests/test_cte_serialize_lc.py create mode 100644 l10n_br_cte/tests/test_cte_serialize_sn.py create mode 100644 l10n_br_cte/tests/test_cte_structure.py diff --git a/l10n_br_cte/__manifest__.py b/l10n_br_cte/__manifest__.py index 3e7d3b73fc09..285b92121a39 100644 --- a/l10n_br_cte/__manifest__.py +++ b/l10n_br_cte/__manifest__.py @@ -29,7 +29,7 @@ "views/cte_document.xml", "wizards/document_correction_wizard.xml", ], - "post_init_hook": "post_init_hook", + # "post_init_hook": "post_init_hook", "installable": True, "auto_install": False, "external_dependencies": { diff --git a/l10n_br_cte/hooks.py b/l10n_br_cte/hooks.py index 6348b345b35b..5ea32d247dce 100644 --- a/l10n_br_cte/hooks.py +++ b/l10n_br_cte/hooks.py @@ -20,13 +20,13 @@ def post_init_hook(cr, registry): res_items = ( "cte", "samples", - "v3_0", + "v4_0", "43120178408960000182570010000000041000000047-cte.xml", ) resource_path = "/".join(res_items) doc_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) binding = Tcte.from_xml(doc_stream.read().decode()) - document_number = binding.infCte.ide.nCTE + document_number = binding.infCte.ide.nCT existing_docs = env["l10n_br_fiscal.document"].search( [("document_number", "=", document_number)] ) @@ -35,7 +35,7 @@ def post_init_hook(cr, registry): doc = ( env["cte.40.tcte_infcte"] .with_context(tracking_disable=True, edoc_type="in") - .build_from_binding("cte", "40", binding.infMDFe) + .build_from_binding("cte", "40", binding.infCte) ) _logger.info(doc.cte40_emit.cte40_CNPJ) except ValidationError: diff --git a/l10n_br_cte/models/dutoviario.py b/l10n_br_cte/models/dutoviario.py index 51d479d08b61..8262a25e99a8 100644 --- a/l10n_br_cte/models/dutoviario.py +++ b/l10n_br_cte/models/dutoviario.py @@ -12,7 +12,9 @@ class Duto(spec_models.StackedModel): _description = "Modal Dutoviario CTe" _cte40_stacking_mixin = "cte.40.duto" - _cte40_odoo_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_duto_v4_00" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_dutoviario_v4_00" + ) document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/tests/__init__.py b/l10n_br_cte/tests/__init__.py new file mode 100644 index 000000000000..8c797134a89c --- /dev/null +++ b/l10n_br_cte/tests/__init__.py @@ -0,0 +1,6 @@ +# from . import test_cte_serialize +# from . import test_cte_serialize_lc +# from . import test_cte_serialize_sn +from . import test_cte_import +# from . import test_cte_structure +# from . import test_cte_res_partner diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/43120178408960000182570010000000041000000047-cte.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/43120178408960000182570010000000041000000047-cte.xml new file mode 100644 index 000000000000..b42362d85ea6 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/43120178408960000182570010000000041000000047-cte.xml @@ -0,0 +1,162 @@ + + + + + 43 + 00000004 + 6353 + SERV. TRANSPORTE + 57 + 1 + 4 + 2012-01-06T17:25:56-02:00 + 1 + 1 + 7 + 2 + 0 + 0 + 104 + 4213609 + PORTO UNIAO + SC + 01 + + 4213609 + PORTO UNIAO + SC + 4128203 + UNIAO DA VITORIA + PR + 1 + 9 + + 0 + + + + MASTER + NOTA FISCAL DE PRODUTOR RURAL N. 253-254-255 + + + 78408960000182 + 251079554 + KERBER E CIA. LTDA. + PEDREIRA + + ESTRADA VELHA DE PALMAS + S/N + CAIXA POSTAL 268 + RIO DA AREIA + 4213609 + PORTO UNIAO + 89400000 + SC + 4235224933 + + + + 78408960000182 + 251079554 + KERBER E CIA. LTDA. + PEDREIRA + 4235224933 + + ESTRADA VELHA DE PALMAS + S/N + CAIXA POSTAL 268 + RIO DA AREIA + 4213609 + PORTO UNIAO + 89400000 + SC + 1058 + Brasil + + pedreira@kerberecia.com.br + + + 78408960000182 + 251079554 + KERBER E CIA. LTDA. + 4235224933 + + ESTRADA VELHA DE PALMAS + S/N + CAIXA POSTAL 268 + RIO DA AREIA + 4213609 + PORTO UNIAO + 89400000 + SC + 1058 + Brasil + + + + 81639791000104 + 3010264714 + HOBI E CIA LTDA. - MATRIZ + 4235211922 + + AUTO VIA JOAO REOLON + 02105 + CENTRO + 4128203 + UNIAO DA VITORIA + 84600000 + PR + 1058 + Brasil + + + + 81639791000104 + 3010264714 + HOBI E CIA LTDA. - MATRIZ + 4235211922 + + AUTO VIA JOAO REOLON + 02105 + CENTRO + 4128203 + UNIAO DA VITORIA + 84600000 + PR + 1058 + Brasil + + + + 2300.00 + 2300.00 + + + + + 00 + 2300.00 + 12.00 + 276.00 + + + + + + 174.38 + Pedra Brita + Pedra Brita a Granel + + 00 + TON + 9.3000 + + + + + 04150238 + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/51160624686092000173570010000000031000000020-cte.XML b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/51160624686092000173570010000000031000000020-cte.XML new file mode 100644 index 000000000000..24a88299e1a8 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/51160624686092000173570010000000031000000020-cte.XML @@ -0,0 +1,130 @@ + + + + + 51 + 00000002 + 5353 + PREST. DE SERV. TRANSPORTE A ESTAB. COMERCIAL.. + 57 + 1 + 3 + 2016-06-07T17:58:40-02:00 + 1 + 1 + 0 + 2 + 0 + 0 + 2.0.1 + 5108402 + VARZEA GRANDE + MT + 01 + 9 + 5101407 + ARIPUANA + MT + 5108402 + VARZEA GRANDE + MT + 1 + 9 + + 0 + + + + MASTER + + NOTA FISCAL DE PRODUTOR RURAL N. 253-254-255 + + + 24686092000173 + 136268870 + P J TOSTA TRANSPORTES ME + + RUA RENATO JOSE DOS SANTOS + 10 + COHAB PRIMAVERA + 5108402 + VARZEA GRANDE + 78132712 + MT + 6599893021 + + + + 72304553915 + ISENTO + ROGERIO MARCIO TOLARDO + 6635651335 + + GLEBA GUARIBA VI LOTE RURAL 38 + SN + ZONA RURAL + 5101407 + ARIPUANA + 78325000 + MT + 1058 + BRASIL + + + + 03851469000122 + 131952927 + FRICAL FRIGORIFICO LTDA EPP + 6536342236 + + ESTRADA SOUZA LIMA + SN + SOUZA LIMA + 5108402 + VARZEA GRANDE + 78110000 + MT + 1058 + BRASIL + + + + 4000.00 + 4000.00 + + + + + 90 + 1 + + + + + + 79400.00 + GADO + GADO + + 01 + KILO + 50.0000 + + + + + 99 + NOTA FISCAL MANUAL + 123456 + 2016-06-05 + 79400.00 + + + + + 05277204 + + + + + diff --git a/l10n_br_cte/tests/test_cte_document.py b/l10n_br_cte/tests/test_cte_document.py new file mode 100644 index 000000000000..7d23614fb464 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_document.py @@ -0,0 +1,86 @@ +# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from datetime import datetime + +from nfelib.nfe.ws.edoc_legacy import CTeAdapter + +from odoo.exceptions import UserError +from odoo.tests import SavepointCase + + +class CTeDocumentTest(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + FiscalDocument = cls.env["l10n_br_fiscal.document"] + + cls.acre_state = cls.env.ref("base.state_br_ac") + cls.cte_document_type_id = cls.env.ref("l10n_br_fiscal.document_57") + cls.sn_company_id = cls.env.ref("l10n_br_base.empresa_simples_nacional") + cls.sn_company_id.processador_edoc = "erpbrasil.edoc" + cls.cte_id = FiscalDocument.create( + { + "document_type_id": cls.cte_document_type_id.id, + "company_id": cls.sn_company_id.id, + "document_number": "70000", + "document_serie": "30", + "document_data": datetime.now(), + } + ) + + def test_cte_compute_fields(self): + self.cte_id.fiscal_additional_data = "TEST FISCAL ADDITIONAL DATA" + self.cte_id.customer_additional_data = "TEST CUSTOMER ADDITIONAL DATA" + + self.assertTrue(self.cte_id.cte40_infAdFisco) + self.assertTrue(self.cte_id.cte40_infCpl) + + def test_cte_inverse_fields(self): + self.cte_id.cte40_UFIni = self.acre_state.code + self.cte_id.cte40_UFFim = self.acre_state.code + self.assertEqual(self.cte_id.cte_initial_state_id, self.acre_state) + self.assertEqual(self.cte_id.cte_final_state_id, self.acre_state) + + self.cte_id.cte40_UF = self.acre_state.ibge_code + self.assertEqual(self.cte_id.company_id.partner_id.state_id, self.acre_state) + + self.cte_id.cte40_infMunCarrega = [ + ( + 0, + 0, + { + "cte40_cMunCarrega": "1200013", + "cte40_xMunCarrega": "Acrelândia", + }, + ) + ] + self.assertIn( + self.env.ref("l10n_br_base.city_1200013"), + self.cte_id.cte_loading_city_ids, + ) + + def test_cte_processor(self): + processor = self.cte_id._edoc_processor() + self.assertTrue(isinstance(processor, CTeAdapter)) + + self.cte_id.document_type_id = False + processor = self.cte_id._edoc_processor() + self.assertFalse(isinstance(processor, CTeAdapter)) + + self.cte_id.document_type_id = self.cte_document_type_id + + self.cte_id.company_id.certificate_nfe_id = False + processor = self.cte_id._edoc_processor() + self.assertTrue(isinstance(processor, CTeAdapter)) + + self.cte_id.company_id.certificate_ecnpj_id = False + with self.assertRaises(UserError): + processor = self.cte_id._edoc_processor() + + def test_generate_key(self): + self.cte_id._generate_key() + self.assertTrue(self.cte_id.document_key) + self.assertTrue(self.cte_id.key_random_code) + self.assertTrue(self.cte_id.key_check_digit) diff --git a/l10n_br_cte/tests/test_cte_import.py b/l10n_br_cte/tests/test_cte_import.py new file mode 100644 index 000000000000..92c3d53d7969 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_import.py @@ -0,0 +1,73 @@ +# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging + +import nfelib +import pkg_resources +from nfelib.cte.bindings.v4_0.cte_v4_00 import Tcte + +from odoo.models import NewId +from odoo.tests import SavepointCase + +_logger = logging.getLogger(__name__) + + +class CTeImportTest(SavepointCase): + def test_import_in_cte_dry_run(self): + res_items = ( + "cte", + "samples", + "v4_0", + "51160624686092000173570010000000031000000020-cte.XML", + ) + + resource_path = "/".join(res_items) + cte_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) + binding = Tcte.from_xml(cte_stream.read().decode()) + + cte = ( + self.env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type="in") + .build_from_binding("cte", "40", binding.infCte, dry_run=True) + ) + assert isinstance(cte.id, NewId) + self._check_cte(cte) + + def test_import_in_cte(self): + res_items = ( + "cte", + "samples", + "v4_0", + "51160624686092000173570010000000031000000020-cte.XML", + ) + resource_path = "/".join(res_items) + cte_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) + binding = Tcte.from_xml(cte_stream.read().decode()) + cte = ( + self.env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type="in") + .build_from_binding("cte", "40", binding.infCte, dry_run=False) + ) + + assert isinstance(cte.id, int) + self._check_cte(cte) + + def _check_cte(self, cte): + self.assertEqual(type(cte)._name, "l10n_br_fiscal.document") + + # ide + self.assertEqual(cte.cte40_nCT, "3") + # self.assertEqual(cte.cte40_infMunCarrega[0].cte40_xMunCarrega, "IVINHEMA") + self.assertEqual(cte.cte40_UFIni, "MT") + self.assertEqual(cte.cte40_UFFim, "MT") + + # # modal + # self.assertEqual(cte.cte40_placa, "XXX1228") + # self.assertEqual(cte.cte40_tara, "0") + # self.assertEqual(cte.cte40_condutor[0].cte40_xNome, "TESTE") + # self.assertEqual(len(cte.cte40_veicReboque), 0) + + self.assertEqual(cte.cte40_verProc, "104") + + def test_import_out_cte(self): + "(can be useful after an ERP migration)" diff --git a/l10n_br_cte/tests/test_cte_res_partner.py b/l10n_br_cte/tests/test_cte_res_partner.py new file mode 100644 index 000000000000..204e1c04775f --- /dev/null +++ b/l10n_br_cte/tests/test_cte_res_partner.py @@ -0,0 +1,66 @@ +# 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 + +from odoo.tests import TransactionCase + + +class TestCTeResPartner(TransactionCase): + def setUp(self): + super().setUp() + + self.partner_id = self.env.ref("l10n_br_base.res_partner_kmee") + + def test_compute_fields(self): + self.partner_id.country_id = self.env.ref("base.us") + + self.assertEqual( + self.partner_id.cte40_choice_tcontractor, "cte40_idEstrangeiro" + ) + self.assertEqual(self.partner_id.cte40_idEstrangeiro, self.partner_id.cnpj_cpf) + + def test_inverse_fields(self): + self.partner_id.cte40_idEstrangeiro = "999999999999" + self.assertEqual(self.partner_id.vat, self.partner_id.cte40_idEstrangeiro) + + self.partner_id.cte40_CNPJ = "97414612000162" + self.assertEqual( + self.partner_id.cnpj_cpf, cnpj_cpf.formata(self.partner_id.cte40_CNPJ) + ) + + self.partner_id.cte40_CPF = "48737433032" + self.assertEqual( + self.partner_id.cnpj_cpf, cnpj_cpf.formata(self.partner_id.cte40_CPF) + ) + + self.partner_id.cte40_IE = "630514648079" + self.assertEqual(self.partner_id.inscr_est, self.partner_id.cte40_IE) + + self.partner_id.cte40_CEP = "04324240" + self.assertEqual(self.partner_id.zip, format_zipcode(self.partner_id.cte40_CEP)) + + self.partner_id.cte40_fone = "(99) 9999-9999" + self.assertEqual(self.partner_id.phone, self.partner_id.cte40_fone) + + self.partner_id.cte40_cPais = "1058" + self.partner_id.cte40_UF = "SP" + self.partner_id.cte40_cMun = "3550308" + self.assertEqual( + self.partner_id.country_id.bc_code, self.partner_id.cte40_cPais + ) + self.assertEqual(self.partner_id.state_id.code, self.partner_id.cte40_UF) + self.assertEqual(self.partner_id.city_id.ibge_code, self.partner_id.cte40_cMun) + + self.partner_id.cte40_xLgr = "TESTE" + self.assertEqual(self.partner_id.street_name, self.partner_id.cte40_xLgr) + + self.partner_id.cte40_nro = "999" + self.assertEqual(self.partner_id.street_number, self.partner_id.cte40_nro) + + self.partner_id.cte40_xCpl = "TESTE" + self.assertEqual(self.partner_id.street2, self.partner_id.cte40_xCpl) + + self.partner_id.cte40_xBairro = "TESTE" + self.assertEqual(self.partner_id.district, self.partner_id.cte40_xBairro) diff --git a/l10n_br_cte/tests/test_cte_serialize.py b/l10n_br_cte/tests/test_cte_serialize.py new file mode 100644 index 000000000000..76ba19856958 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_serialize.py @@ -0,0 +1,234 @@ +# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# Gabriel Cardoso de Faria +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +import os +from datetime import datetime + +from xmldiff import main + +from odoo.tests.common import TransactionCase +from odoo.tools import config + +from odoo.addons import l10n_br_cte + +_logger = logging.getLogger(__name__) + + +class TestCTeSerialize(TransactionCase): + def setUp(self, cte_list): + super().setUp() + self.cte_list = cte_list + for cte_data in self.cte_list: + cte = self.env.ref(cte_data["record_ref"]) + cte_data["cte"] = cte + self.prepare_test_cte(cte) + + def prepare_test_cte(self, cte): + """ + Performs actions necessary to prepare an CTe of the demo data to + perform the tests + """ + if cte.state != "em_digitacao": # 2nd test run + cte.action_document_back2draft() + + cte._compute_amount() + cte.action_document_confirm() + cte.document_date = datetime.strptime( + "2020-01-01T11:00:00", "%Y-%m-%dT%H:%M:%S" + ) + cte.cte40_cMDF = "20801844" + + if cte.cte_modal == "1": + self.prepare_modal_rodoviario_data(cte) + elif cte.cte_modal == "2": + self.prepare_modal_aereo_data(cte) + elif cte.cte_modal == "3": + self.prepare_modal_aquaviario_data(cte) + elif cte.cte_modal == "4": + self.prepare_modal_ferroviario_data(cte) + + cte._document_export() + + def prepare_modal_rodoviario_data(self, cte): + cte.cte40_codAgPorto = "12345678" + + # infANTT + cte.cte40_RNTRC = "12345678" + cte.cte40_categCombVeic = "02" + cte.cte40_infCIOT = [ + ( + 0, + 0, + { + "is_company": False, + "cte40_CIOT": "123456789101", + "cte40_CPF": "99999999999", + }, + ), + ] + cte.cte40_disp = [ + ( + 0, + 0, + { + "cte40_CNPJForn": "99999999999999", + "cte40_CNPJPg": "99999999999999", + "cte40_nCompra": "1234", + "cte40_vValePed": 5, + "cte40_tpValePed": "01", + }, + ), + ] + cte.cte40_infPag = [ + ( + 0, + 0, + { + "partner_id": self.env.ref("l10n_br_base.res_partner_intel").id, + "cte40_vContrato": 5, + "cte40_indPag": "0", + "payment_type": "pix", + "cte40_PIX": "99999999999999", + "cte40_comp": [ + ( + 0, + 0, + { + "cte40_tpComp": "01", + "cte40_vComp": 5, + }, + ) + ], + }, + ), + ] + + # veicTracao + cte.cte40_cInt = "1" + cte.cte40_RENAVAM = "42423325472" + cte.cte40_placa = "AAA1233" + cte.cte40_tpTransp = False + cte.cte40_tara = 7500 + cte.cte40_capKG = 42500 + cte.cte40_capM3 = 300 + cte.cte40_tpRod = "03" + cte.cte40_tpCar = "00" + cte.rodo_vehicle_state_id = self.env.ref("base.state_br_ac").id + cte.cte40_condutor = [ + ( + 0, + 0, + { + "cte40_xNome": "Teste", + "cte40_CPF": "99999999999", + }, + ), + ( + 0, + 0, + { + "cte40_xNome": "Teste2", + "cte40_CPF": "99999999999", + }, + ), + ] + + # veicReboque + cte.cte40_veicReboque = [ + ( + 0, + 0, + { + "cte40_cInt": "2", + "cte40_placa": "AAA4321", + "cte40_RENAVAM": "11557770179", + "cte40_tara": 7200, + "cte40_capKG": 42500, + "cte40_capM3": 300, + "cte40_tpCar": "00", + "cte40_UF": "AC", + }, + ) + ] + + def prepare_modal_aereo_data(self, cte): + cte.cte40_nac = "TEST" + cte.cte40_matr = "TEST" + cte.cte40_nVoo = "123456789" + cte.cte40_cAerEmb = "OACI" + cte.cte40_cAerDes = "OACI" + cte.cte40_dVoo = datetime.strptime("2020-01-01", "%Y-%m-%d") + + def prepare_modal_aquaviario_data(self, cte): + cte.cte40_irin = "1234567899" + cte.cte40_tpEmb = "01" + cte.cte40_cEmbar = "123456" + cte.cte40_xEmbar = "teste" + cte.cte40_nViag = "123456" + cte.cte40_cPrtEmb = "BRADR" + cte.cte40_cPrtDest = "BRAFU" + cte.cte40_infTermCarreg = [ + (0, 0, {"loading_harbor": "BRADR"}), + (0, 0, {"loading_harbor": "BRANT"}), + ] + cte.cte40_infTermDescarreg = [ + (0, 0, {"unloading_harbor": "BRAFU"}), + (0, 0, {"unloading_harbor": "BRBZC"}), + ] + + def prepare_modal_ferroviario_data(self, cte): + cte.cte40_dhTrem = datetime.strptime("2020-01-01T11:00:00", "%Y-%m-%dT%H:%M:%S") + cte.cte40_xPref = "TES" + cte.cte40_xOri = "TES" + cte.cte40_xDest = "TES" + cte.cte40_qVag = 2 + cte.cte40_vag = [ + ( + 0, + 0, + { + "cte40_pesoBC": 500, + "cte40_pesoR": 1, + "cte40_tpVag": 123, + "cte40_serie": 123, + "cte40_nVag": 123, + "cte40_nSeq": 123, + "cte40_TU": 1, + }, + ), + ( + 0, + 0, + { + "cte40_pesoBC": 500, + "cte40_pesoR": 1, + "cte40_tpVag": 321, + "cte40_serie": 321, + "cte40_nVag": 321, + "cte40_nSeq": 321, + "cte40_TU": 1, + }, + ), + ] + + def serialize_xml(self, cte_data): + cte = cte_data["cte"] + xml_path = os.path.join( + l10n_br_cte.__path__[0], + "tests", + "cte", + "V4_00", + "leiauteCTe", + cte_data["xml_file"], + ) + output = os.path.join( + config["data_dir"], + "filestore", + self.cr.dbname, + cte.send_file_id.store_fname, + ) + _logger.info(f"XML file saved at {output}") + diff = main.diff_files(output, xml_path) + return diff diff --git a/l10n_br_cte/tests/test_cte_serialize_lc.py b/l10n_br_cte/tests/test_cte_serialize_lc.py new file mode 100644 index 000000000000..cead0bf51335 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_serialize_lc.py @@ -0,0 +1,28 @@ +# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging + +from .test_cte_serialize import TestCTeSerialize + +_logger = logging.getLogger(__name__) + + +class TestCTeExportLC(TestCTeSerialize): + def setUp(self): + cte_list = [ + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_ferroviario", + "xml_file": "CTe35230905472475000102580200000602011208018449.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_rodoviario", + "xml_file": "CTe35230905472475000102580200000602071611554500.xml", + }, + ] + super().setUp(cte_list) + + def test_serialize_xml(self): + for cte_data in self.cte_list: + diff = self.serialize_xml(cte_data) + _logger.info("Diff with expected XML (if any): %s" % (diff,)) + assert len(diff) == 0 diff --git a/l10n_br_cte/tests/test_cte_serialize_sn.py b/l10n_br_cte/tests/test_cte_serialize_sn.py new file mode 100644 index 000000000000..5ed1aae0da1b --- /dev/null +++ b/l10n_br_cte/tests/test_cte_serialize_sn.py @@ -0,0 +1,29 @@ +# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging + +from .test_cte_serialize import TestCTeSerialize + +_logger = logging.getLogger(__name__) + + +class TestCTeExportSN(TestCTeSerialize): + def setUp(self): + cte_list = [ + { + "record_ref": "l10n_br_cte.demo_cte_sn_modal_aereo", + "xml_file": "CTe35230905472475000102580200000602081550195716.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_sn_modal_aquaviario", + "xml_file": "CTe35231005472475000102580200000602161434590525.xml", + }, + ] + + super().setUp(cte_list) + + def test_serialize_xml(self): + for cte_data in self.cte_list: + diff = self.serialize_xml(cte_data) + _logger.info("Diff with expected XML (if any): %s" % (diff,)) + assert len(diff) == 0 diff --git a/l10n_br_cte/tests/test_cte_structure.py b/l10n_br_cte/tests/test_cte_structure.py new file mode 100644 index 000000000000..213702f076f3 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_structure.py @@ -0,0 +1,160 @@ +# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from io import StringIO + +from odoo.tests import SavepointCase + +from odoo.addons.spec_driven_model.models.spec_models import SpecModel + +from ..models.document import CTe + + +class CTeStructure(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + @classmethod + def get_stacked_tree(cls, klass): + """ + # > means the content of the m2o is stacked in the parent + # - means standard m2o. Eventually followd by the mapped Odoo model + # ≡ means o2m. Eventually followd by the mapped Odoo model + """ + spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + spec_prefix = "cte40" + stacking_settings = { + "odoo_module": getattr(klass, f"_{spec_prefix}_odoo_module"), + "stacking_mixin": getattr(klass, f"_{spec_prefix}_stacking_mixin"), + "stacking_points": getattr(klass, f"_{spec_prefix}_stacking_points"), + "stacking_skip_paths": getattr( + klass, f"_{spec_prefix}_stacking_skip_paths", [] + ), + "stacking_force_paths": getattr( + klass, f"_{spec_prefix}_stacking_force_paths", [] + ), + } + node = SpecModel._odoo_name_to_class( + stacking_settings["stacking_mixin"], spec_module + ) + tree = StringIO() + visited = set() + for kind, n, path, field_path, child_concrete in klass._visit_stack( + cls.env, node, stacking_settings + ): + visited.add(n) + path_items = path.split(".") + indent = " ".join(["" for i in range(0, len(path_items))]) + if kind == "stacked": + line = "\n%s> <%s>" % (indent, path.split(".")[-1]) + elif kind == "one2many": + line = "\n%s \u2261 <%s> %s" % ( + indent, + field_path, + child_concrete or "", + ) + elif kind == "many2one": + line = "\n%s - <%s> %s" % (indent, field_path, child_concrete or "") + tree.write(line.rstrip()) + tree_txt = tree.getvalue() + return tree_txt, visited + + def test_inherited_fields(self): + assert "cte40_CNPJ" in self.env["res.company"]._fields.keys() + + def test_concrete_spec(self): + # this ensure basic SQL is set up + self.assertEqual( + len( + self.env["cte.40.infmuncarrega"].search( + [("cte40_cMunCarrega", "=", "NO_RECORD")] + ) + ), + 0, + ) + + def test_m2o_concrete_to_concrete_spec(self): + self.assertEqual( + self.env["cte.40.infcte"] + ._fields["cte40_infCTe_infMunDescarga_id"] + .comodel_name, + "cte.40.infmundescarga", + ) + + def test_o2m_concrete_to_concrete_spec(self): + self.assertEqual( + self.env["cte.40.ide"]._fields["cte40_infMunCarrega"].comodel_name, + "cte.40.infmuncarrega", + ) + + def test_m2o_stacked_to_odoo(self): + self.assertEqual( + self.env["l10n_br_fiscal.document"]._fields["cte40_prodPred"].comodel_name, + "product.product", + ) + + def test_o2m_to_odoo(self): + self.assertEqual( + self.env["l10n_br_fiscal.document"] + ._fields["cte40_infEmbComb"] + .comodel_name, + "l10n_br_cte.modal.aquaviario.comboio", + ) + self.assertEqual( + len( + self.env["l10n_br_cte.modal.aquaviario.comboio"].search( + [("cte40_cEmbComb", "=", "NO_RECORD")] + ) + ), + 0, + ) + + def test_m2o_stacked_to_concrete(self): + # not stacked because optional + model = ( + self.env["l10n_br_fiscal.document"] + ._fields["cte40_infSolicNFF"] + .comodel_name + ) + self.assertEqual(model, "cte.40.infsolicnff") + + # def test_m2o_stacked(self): + # # not stacked because optional + # cte_model = self.env["l10n_br_fiscal.document"] + # # cte40_cana is optional so its fields shoudn't be stacked + # assert "cte40_XXX" not in cte_model._fields.keys() + + def test_doc_stacking_points(self): + doc_keys = [ + "cte40_ide", + "cte40_infModal", + "cte40_infDoc", + "cte40_tot", + "cte40_infAdic", + # "cte40_trem", + # "cte40_infANTT", + # "cte40_valePed", + # "cte40_veicTracao", + # "cte40_infBanc", + ] + keys = [ + k + for k in self.env["l10n_br_fiscal.document"] + .with_context(spec_schema="cte", spec_version="30") + ._get_stacking_points() + .keys() + ] + self.assertEqual(sorted(keys), sorted(doc_keys)) + + def test_doc_tree(self): + base_class = self.env["l10n_br_fiscal.document"] + tree, visited = self.get_stacked_tree(base_class) + self.assertEqual(tree, CTe.INFCTE_TREE) + self.assertEqual(len(visited), 6) # all stacked classes + + def test_m2o_force_stack(self): + pass + + def test_doc_visit_stack(self): + pass From b939222a4190c84867ee6522c1d7c0b88595f64d Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Tue, 29 Oct 2024 08:45:53 -0300 Subject: [PATCH 10/27] [IMP] l10n_br_cte: add res_country not create --- l10n_br_cte/models/__init__.py | 1 + l10n_br_cte/models/res_country.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 l10n_br_cte/models/res_country.py diff --git a/l10n_br_cte/models/__init__.py b/l10n_br_cte/models/__init__.py index 2082b680910c..165e0217d80d 100644 --- a/l10n_br_cte/models/__init__.py +++ b/l10n_br_cte/models/__init__.py @@ -14,6 +14,7 @@ from . import document_transported_vehicles from . import normal_cte_infos from . import document_comment +from . import res_country spec_schema = "cte" spec_version = "40" diff --git a/l10n_br_cte/models/res_country.py b/l10n_br_cte/models/res_country.py new file mode 100644 index 000000000000..e4d8595f2366 --- /dev/null +++ b/l10n_br_cte/models/res_country.py @@ -0,0 +1,20 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ResCountry(models.Model): + _inherit = "res.country" + _cte_search_keys = ["bc_code"] + + @api.model + def match_or_create_m2o(self, rec_dict, parent_dict, model=None): + """If country not found, break hard, don't create it""" + + if rec_dict.get("bc_code"): + domain = [("bc_code", "=", rec_dict.get("bc_code"))] + match = self.search(domain, limit=1) + if match: + return match.id + return False From 2c9f57e4bf31767c9a2d7551ef398dba4cf25bbe Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Tue, 29 Oct 2024 08:46:34 -0300 Subject: [PATCH 11/27] [FIX] l10n_br_cte: fix fone field --- l10n_br_cte/models/res_partner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_br_cte/models/res_partner.py b/l10n_br_cte/models/res_partner.py index aab363047245..bd7da37dd979 100644 --- a/l10n_br_cte/models/res_partner.py +++ b/l10n_br_cte/models/res_partner.py @@ -141,7 +141,7 @@ def _inverse_cte40_CEP(self): def _inverse_cte40_fone(self): for rec in self: if rec.cte40_fone: - rec.phone = rec.nfe40_fone + rec.phone = rec.cte40_fone def _compute_cep(self): for rec in self: From c76aa85243df304e8486fc65df83ad9b5d9734e0 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 14 Nov 2024 13:38:30 -0300 Subject: [PATCH 12/27] [IMP] l10n_br_fiscal: add cte demo data --- l10n_br_fiscal/demo/company_demo.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/l10n_br_fiscal/demo/company_demo.xml b/l10n_br_fiscal/demo/company_demo.xml index 224d742422c1..c8d18e20b549 100644 --- a/l10n_br_fiscal/demo/company_demo.xml +++ b/l10n_br_fiscal/demo/company_demo.xml @@ -162,6 +162,14 @@ True + + 1 + Série 1 + + + True + + 1 Série 1 @@ -202,6 +210,14 @@ True + + 1 + Série 1 + + + True + + 1 Série 1 From 251d30712bd2b427d2ee7c8723e8a9a63870187a Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 14 Nov 2024 13:46:30 -0300 Subject: [PATCH 13/27] [IMP] l10n_br_fiscal: add cte partners fields --- .../models/document_mixin_fields.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/l10n_br_fiscal/models/document_mixin_fields.py b/l10n_br_fiscal/models/document_mixin_fields.py index cdcb252e949f..d523e3496984 100644 --- a/l10n_br_fiscal/models/document_mixin_fields.py +++ b/l10n_br_fiscal/models/document_mixin_fields.py @@ -471,3 +471,41 @@ def _operation_domain(self): key_random_code = fields.Char(string="Document Key Random Code") key_check_digit = fields.Char(string="Document Key Check Digit") total_weight = fields.Float(string="Total Weight") + + ## CTE + + # commitment_date = fields.Datetime("Delivery Date") + # expected_date = fields.Datetime("Expected Date") + + # Remetente + partner_sendering_id = fields.Many2one( + "res.partner", + string="Sender Address", + help="Responsible for sending the goods, usually the issuer of the NFe.", + ) + + # Expedidor + partner_shippering_id = fields.Many2one( + "res.partner", + string="Shipper Address", + help="The one responsible for delivering the cargo to the carrier when \ + the shipment is not carried out by the sender.", + ) + + # # Destinatário + # partner_shipping_id = fields.Many2one( + # "res.partner", + # string="Recipient", + # help="The one who receives the goods at the end of the transport \ + # route, can be an individual or a company.", + # ) + + # Recebedor + partner_receivering_id = fields.Many2one( + "res.partner", + string="Receiver Address", + help="Actor who receives the goods. He is considered an intermediary \ + between the issuer and the final recipient.", + ) + + partner_insurance_id = fields.Many2one("res.partner", string="Insurance Partner") From 460d1e41020cc3832c92a6f52954f975b90209e8 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 14 Nov 2024 13:48:02 -0300 Subject: [PATCH 14/27] [RFC] l10n_br_cte: general refactor --- l10n_br_cte/README.rst | 66 +- l10n_br_cte/__manifest__.py | 25 +- l10n_br_cte/constants/cte.py | 105 ++ l10n_br_cte/constants/modal.py | 11 + l10n_br_cte/data/ir_config_parameter.xml | 9 + l10n_br_cte/demo/company_demo.xml | 16 + l10n_br_cte/demo/fiscal_document_demo.xml | 208 ++++ l10n_br_cte/hooks.py | 22 +- l10n_br_cte/models/__init__.py | 6 +- l10n_br_cte/models/aereo.py | 2 + l10n_br_cte/models/aquaviario.py | 7 + l10n_br_cte/models/document.py | 920 +++++++++++------- l10n_br_cte/models/document_line.py | 1 + l10n_br_cte/models/document_related.py | 4 + l10n_br_cte/models/document_type.py | 9 + l10n_br_cte/models/dutoviario.py | 1 + l10n_br_cte/models/ferroviario.py | 1 + l10n_br_cte/models/normal_cte_infos.py | 157 --- l10n_br_cte/models/res_city.py | 20 + l10n_br_cte/models/res_company.py | 120 ++- l10n_br_cte/models/res_config_settings.py | 10 + l10n_br_cte/models/res_country_state.py | 21 + l10n_br_cte/models/res_partner.py | 491 +++++++++- l10n_br_cte/models/rodoviario.py | 2 + l10n_br_cte/readme/CONFIGURE.rst | 11 +- l10n_br_cte/readme/CONTRIBUTORS.rst | 14 +- l10n_br_cte/readme/DESCRIPTION.rst | 8 +- l10n_br_cte/readme/ROADMAP.rst | 0 l10n_br_cte/readme/USAGE.rst | 23 +- l10n_br_cte/security/ir.model.access.csv | 1 - l10n_br_cte/static/description/index.html | 62 +- l10n_br_cte/tests/__init__.py | 12 +- ...08318053000167570010000000311040445899.xml | 196 ++++ ...08318053000167570010000000311040645898.xml | 198 ++++ ...8408960000182570010000000041000000047.xml} | 8 +- ...4686092000173570010000000031000000024.xml} | 14 +- ...24686092000173570010000000031000000024.xml | 130 +++ l10n_br_cte/tests/test_cte_document.py | 103 +- l10n_br_cte/tests/test_cte_import.py | 36 +- l10n_br_cte/tests/test_cte_res_partner.py | 12 +- l10n_br_cte/tests/test_cte_serialize.py | 224 ++--- l10n_br_cte/tests/test_cte_serialize_lc.py | 9 +- l10n_br_cte/tests/test_cte_serialize_sn.py | 10 +- l10n_br_cte/tests/test_cte_structure.py | 78 +- l10n_br_cte/tests/test_res_partner.py | 71 ++ l10n_br_cte/views/cte_action.xml | 47 + l10n_br_cte/views/cte_menu.xml | 13 + .../views/{cte_document.xml => document.xml} | 15 +- l10n_br_cte/views/document_line.xml | 13 - l10n_br_cte/views/document_related.xml | 17 - l10n_br_cte/{ => views}/modal/modal_aereo.xml | 0 .../{ => views}/modal/modal_aquaviario.xml | 0 .../{ => views}/modal/modal_ferroviario.xml | 0 .../{ => views}/modal/modal_rodoviario.xml | 0 l10n_br_cte/views/res_partner.xml | 17 - 55 files changed, 2559 insertions(+), 1017 deletions(-) create mode 100644 l10n_br_cte/constants/cte.py create mode 100644 l10n_br_cte/data/ir_config_parameter.xml create mode 100644 l10n_br_cte/demo/company_demo.xml create mode 100644 l10n_br_cte/demo/fiscal_document_demo.xml create mode 100644 l10n_br_cte/models/document_type.py delete mode 100644 l10n_br_cte/models/normal_cte_infos.py create mode 100644 l10n_br_cte/models/res_city.py create mode 100644 l10n_br_cte/models/res_country_state.py create mode 100644 l10n_br_cte/readme/ROADMAP.rst create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040445899.xml create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040645898.xml rename l10n_br_cte/tests/cte/v4_00/leiauteCTe/{43120178408960000182570010000000041000000047-cte.xml => CTe43120178408960000182570010000000041000000047.xml} (97%) rename l10n_br_cte/tests/cte/v4_00/leiauteCTe/{51160624686092000173570010000000031000000020-cte.XML => CTe51160724686092000173570010000000031000000024.xml} (92%) create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160824686092000173570010000000031000000024.xml create mode 100644 l10n_br_cte/tests/test_res_partner.py create mode 100644 l10n_br_cte/views/cte_action.xml create mode 100644 l10n_br_cte/views/cte_menu.xml rename l10n_br_cte/views/{cte_document.xml => document.xml} (93%) delete mode 100644 l10n_br_cte/views/document_line.xml delete mode 100644 l10n_br_cte/views/document_related.xml rename l10n_br_cte/{ => views}/modal/modal_aereo.xml (100%) rename l10n_br_cte/{ => views}/modal/modal_aquaviario.xml (100%) rename l10n_br_cte/{ => views}/modal/modal_ferroviario.xml (100%) rename l10n_br_cte/{ => views}/modal/modal_rodoviario.xml (100%) delete mode 100644 l10n_br_cte/views/res_partner.xml diff --git a/l10n_br_cte/README.rst b/l10n_br_cte/README.rst index caafff26c57b..1470f0b8b525 100644 --- a/l10n_br_cte/README.rst +++ b/l10n_br_cte/README.rst @@ -28,10 +28,12 @@ CT-e |badge1| |badge2| |badge3| |badge4| |badge5| -[ This file must be max 2-3 paragraphs, and is required. ] +Este módulo permite a emissão de CT-e (Conhecimento de Transporte). -This module extends the functionality of ... to support ... -and to allow you to ... +Mais especificamente ele: + * mapea os campos de CT-e do módulo ``l10n_br_cte_spec`` com os campos Odoo. + * usa a logica do módulo ``spec_driven_model`` para realizar esse mapeamento de forma dinâmica, em especial ele usa o sistema de modelos com várias camadas, ou ``StackedModel``, com os modelos ``l10n_br_fiscal.document`` e ``l10n_br_fiscal.document.related`` que tem varios niveis hierarquicos de elementos XML que estão sendo denormalizados dentro desses modelos Odoo  + * tem wizards para implementar a comunicação SOAP de CT-e com a SEFAZ (Autorização, Cancelamento, Encerramento...) .. IMPORTANT:: This is an alpha version, the data model and design can change at any time without warning. @@ -46,31 +48,27 @@ and to allow you to ... Configuration ============= -[ This file is optional, it should explain how to configure - the module before using it; it is aimed at advanced users. ] +Para configurar este módulo, você precisa definir um certificado digital na empresa e também definir o processador edoc da empresa. -To configure this module, you need to: +Usage +===== -#. Go to ... +**Passo a Passo:** -.. figure:: https://raw.githubusercontent.com/OCA/l10n-brazil/14.0/l10n_br_cte/static/description/image.png - :alt: alternative description - :width: 600 px +1. **Criar uma Fatura:** + - Defina o tipo de documento como **57 (CTe - Conhecimento de Transporte)**. -Usage -===== +2. **Configurar o Parceiro da Fatura:** + - Configure o parceiro responsável pelo pagamento do CTe e os parceiros como Rementente, Expedidor, Destinatário e Recebedor. -[ 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). ] +3. **Adicionar uma Linha na Aba Produtos:** + - Adicione uma linha de fatura e selecione o produto Frete ou outro que esteja previamente configurado. -To use this module, you need to: +4. **Acesse os detalhes fiscais da fatura e informe os demais dados necessário para emissão do CT-e:** + - Preencha os campos obrigatórios para emissão do CT-e. -#. Go to ... +5. **Valide o CT-e, verifique os dados do XML e envie para a SEFAZ:** + - Após preencher todos os dados necessários, valide o CT-e e envie para a SEFAZ. Bug Tracker =========== @@ -89,11 +87,24 @@ Authors ~~~~~~~ * KMEE +* Escodoo Contributors ~~~~~~~~~~~~ -* Ygor Carvalho + +* `KMEE `_: + + * Luis Felipe Mileo + * Ygor Carvalho + +* `ESCODOO `_: + + * Marcel Savegnago + +* `AKRETION `_: + + * Raphaël Valyi Maintainers ~~~~~~~~~~~ @@ -108,6 +119,17 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. +.. |maintainer-mileo| image:: https://github.com/mileo.png?size=40px + :target: https://github.com/mileo + :alt: mileo +.. |maintainer-marcelsavegnago| image:: https://github.com/marcelsavegnago.png?size=40px + :target: https://github.com/marcelsavegnago + :alt: marcelsavegnago + +Current `maintainers `__: + +|maintainer-mileo| |maintainer-marcelsavegnago| + This module is part of the `OCA/l10n-brazil `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_br_cte/__manifest__.py b/l10n_br_cte/__manifest__.py index 285b92121a39..c569068a0d9e 100644 --- a/l10n_br_cte/__manifest__.py +++ b/l10n_br_cte/__manifest__.py @@ -7,7 +7,8 @@ "version": "14.0.1.0.0", "category": "Localisation", "license": "AGPL-3", - "author": "KMEE, Odoo Community Association (OCA)", + "author": "KMEE,Escodoo,Odoo Community Association (OCA)", + "maintainers": ["mileo", "marcelsavegnago"], "website": "https://github.com/OCA/l10n-brazil", "development_status": "Alpha", "depends": [ @@ -18,18 +19,22 @@ ], "data": [ "security/ir.model.access.csv", - # "views/document_line.xml", - # 'views/document_related.xml', - # 'views/res_partner.xml', - "modal/modal_rodoviario.xml", - "modal/modal_aquaviario.xml", - "modal/modal_ferroviario.xml", - "modal/modal_aereo.xml", + "data/ir_config_parameter.xml", + "views/document.xml", + "views/cte_action.xml", + "views/cte_menu.xml", "views/res_company.xml", - "views/cte_document.xml", + "views/modal/modal_rodoviario.xml", + "views/modal/modal_aquaviario.xml", + "views/modal/modal_ferroviario.xml", + "views/modal/modal_aereo.xml", "wizards/document_correction_wizard.xml", ], - # "post_init_hook": "post_init_hook", + "demo": [ + "demo/company_demo.xml", + "demo/fiscal_document_demo.xml", + ], + "post_init_hook": "post_init_hook", "installable": True, "auto_install": False, "external_dependencies": { diff --git a/l10n_br_cte/constants/cte.py b/l10n_br_cte/constants/cte.py new file mode 100644 index 000000000000..bb8a4ed5cb87 --- /dev/null +++ b/l10n_br_cte/constants/cte.py @@ -0,0 +1,105 @@ +# Copyright (C) 2024 - TODAY, Marcel Savegnago +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +CTE_VERSIONS = [("4.00", "4.00")] + +CTE_VERSION_DEFAULT = "4.00" + +CTE_ENVIRONMENTS = [("1", "Produção"), ("2", "Homologação")] + +CTE_ENVIRONMENT_DEFAULT = "2" + +CTE_EMIT_TYPES = [ + ("1", "1 - Prestador de serviço de transporte"), + ("2", "2 - Transportador de Carga Própria"), + ("3", "3 - Prestador de serviço de transporte que emitirá CT-e Globalizado"), +] + +CTE_EMIT_TYPE_DEFAULT = "2" + +CTE_TRANSP_TYPE = [ + ("1", "Empresa de Transporte de Cargas – ETC"), + ("2", "Transportador Autônomo de Cargas – TAC"), + ("3", "Cooperativa de Transporte de Cargas – CTC"), +] + +CTE_TRANSP_TYPE_DEFAULT = "1" + +CTE_TRANSMISSIONS = [ + ("1", "Emissão Normal"), + ("2", "Contingência Off-Line"), + ("3", "Regime Especial NFF"), +] + +CTE_TRANSMISSION_DEFAULT = "1" + +CTE_EMISSION_PROCESSES = [("0", "Emissão de CTe com aplicativo do contribuinte")] + +CTE_EMISSION_PROCESS_DEFAULT = "0" + +CTE_TYPE = [ + ("0", "CT-e Normal"), + ("1", "CT-e de Complemento de Valores"), + ("3", "CT-e de Substituição"), +] + +CTE_TYPE_DEFAULT = "0" + +CTE_INDIETOMA = [ + ("1", "Contribuinte ICMS"), + ("2", "Contribuinte isento de inscrição"), + ("9", "Não Contribuinte"), +] + +CTE_INDIETOMA_DEFAULT = "1" + +CTE_TPSERV = [ + ("0", "Normal"), + ("1", "Subcontratação"), + ("2", "Redespacho"), + ("3", "Redespacho Intermediário"), + ("4", "Serviço Vinculado a Multimodal"), +] + +CTE_TPSERV_DEFAULT = "0" + +CTE_TPEMIS = [ + ("1", "Normal"), + ("3", "Regime Especial NFF"), + ("4", "EPEC pela SVC"), + ("5", "Contingência FSDA"), + ("7", "Autorização pela SVC-RS"), + ("8", "Autorização pela SVC-SP"), +] + +CTE_TPEMIS_DEFAULT = "1" + +CTE_TPIMP = [ + ("1", "Retrato"), + ("2", "Paisagem."), +] + +CTE_TPIMP_DEFAULT = "1" + + +CTE_ICMS_SUB_TAGS = [ + "ICMS00", + "ICMS20", + "ICMS45", + "ICMS60", + "ICMS90", + "ICMSOutraUF", + "ICMSSN", +] + +CTE_ICMS_SELECTION = list(map(lambda tag: (f"cte40_{tag}", tag), CTE_ICMS_SUB_TAGS)) + +CTE_CST = [ + ("00", "00 - Tributação normal ICMS"), + ("20", "20 - Tributação com BC reduzida do ICMS"), + ("45", "45 - ICMS Isento, não Tributado ou diferido"), + ("60", "60 - ICMS cobrado por substituição tributária"), + ("90", "90 - ICMS outros"), + ("90", "90 - ICMS Outra UF"), + ("01", "01 - Simples Nacional"), +] diff --git a/l10n_br_cte/constants/modal.py b/l10n_br_cte/constants/modal.py index 42dcd1d85a53..6b1c1625ac51 100644 --- a/l10n_br_cte/constants/modal.py +++ b/l10n_br_cte/constants/modal.py @@ -1,3 +1,14 @@ +CTE_MODALS = [ + ("01", "Rodoviário"), + ("02", "Aéreo"), + ("03", "Aquaviário"), + ("04", "Ferroviário"), + ("05", "Dutoviário"), + ("06", "Multimodal"), +] + +CTE_MODAL_DEFAULT = "01" + CTE_MODAL_VERSION_DEFAULT = "4.00" TUF = [ diff --git a/l10n_br_cte/data/ir_config_parameter.xml b/l10n_br_cte/data/ir_config_parameter.xml new file mode 100644 index 000000000000..a0a0745d0918 --- /dev/null +++ b/l10n_br_cte/data/ir_config_parameter.xml @@ -0,0 +1,9 @@ + + + + + l10n_br_cte.version.name + Odoo Brasil OCA v14 + + + diff --git a/l10n_br_cte/demo/company_demo.xml b/l10n_br_cte/demo/company_demo.xml new file mode 100644 index 000000000000..a3e331859b40 --- /dev/null +++ b/l10n_br_cte/demo/company_demo.xml @@ -0,0 +1,16 @@ + + + + + + oca + + + + + + oca + + + + diff --git a/l10n_br_cte/demo/fiscal_document_demo.xml b/l10n_br_cte/demo/fiscal_document_demo.xml new file mode 100644 index 000000000000..641667366c58 --- /dev/null +++ b/l10n_br_cte/demo/fiscal_document_demo.xml @@ -0,0 +1,208 @@ + + + + + + + + + + 573 + 1 + 35240708318053000167570010000000311040645898 + 2 + oca + + 3 + 01 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + + + + + + + + + + + + 574 + 1 + 35240708318053000167570010000000311040445899 + 2 + oca + + 3 + 01 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + + + + + + diff --git a/l10n_br_cte/hooks.py b/l10n_br_cte/hooks.py index 5ea32d247dce..972c6786362f 100644 --- a/l10n_br_cte/hooks.py +++ b/l10n_br_cte/hooks.py @@ -1,14 +1,18 @@ # Copyright (C) 2019-2020 - Raphael Valyi Akretion +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html import logging -import nfelib import pkg_resources -from nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00 import Tcte + +# from nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00 import Tcte +from nfelib.cte.bindings.v4_0.cte_v4_00 import Tcte from odoo import SUPERUSER_ID, api from odoo.exceptions import ValidationError +from odoo.addons import l10n_br_cte + _logger = logging.getLogger(__name__) @@ -18,13 +22,15 @@ def post_init_hook(cr, registry): is_demo = cr.fetchone()[0] if is_demo: res_items = ( + "tests", "cte", - "samples", - "v4_0", - "43120178408960000182570010000000041000000047-cte.xml", + "v4_00", + "leiauteCTe", + "CTe51160724686092000173570010000000031000000024.xml", ) + resource_path = "/".join(res_items) - doc_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) + doc_stream = pkg_resources.resource_stream(l10n_br_cte.__name__, resource_path) binding = Tcte.from_xml(doc_stream.read().decode()) document_number = binding.infCte.ide.nCT existing_docs = env["l10n_br_fiscal.document"].search( @@ -32,11 +38,11 @@ def post_init_hook(cr, registry): ) try: existing_docs.unlink() - doc = ( + cte = ( env["cte.40.tcte_infcte"] .with_context(tracking_disable=True, edoc_type="in") .build_from_binding("cte", "40", binding.infCte) ) - _logger.info(doc.cte40_emit.cte40_CNPJ) + _logger.info(cte.cte40_emit.cte40_CNPJ) except ValidationError: _logger.info(f"CTE-e already {document_number} imported by hooks") diff --git a/l10n_br_cte/models/__init__.py b/l10n_br_cte/models/__init__.py index 165e0217d80d..0d674aa9eed1 100644 --- a/l10n_br_cte/models/__init__.py +++ b/l10n_br_cte/models/__init__.py @@ -1,6 +1,6 @@ -from . import document from . import res_company from . import res_partner +from . import document from . import document_related from . import document_line from . import res_config_settings @@ -12,9 +12,11 @@ from . import document_cargo_quantity_infos from . import document_supplement from . import document_transported_vehicles -from . import normal_cte_infos from . import document_comment from . import res_country +from . import document_type +from . import res_country_state +from . import res_city spec_schema = "cte" spec_version = "40" diff --git a/l10n_br_cte/models/aereo.py b/l10n_br_cte/models/aereo.py index f5f69c888dba..e6c6e17cfd67 100644 --- a/l10n_br_cte/models/aereo.py +++ b/l10n_br_cte/models/aereo.py @@ -16,6 +16,7 @@ class Aereo(spec_models.StackedModel): _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aereo_v4_00" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") @@ -51,6 +52,7 @@ class Peri(spec_models.StackedModel): _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aereo_v4_00" ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aereo_v4_00" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/models/aquaviario.py b/l10n_br_cte/models/aquaviario.py index be9f52967d74..8b30ce02cd2e 100644 --- a/l10n_br_cte/models/aquaviario.py +++ b/l10n_br_cte/models/aquaviario.py @@ -15,6 +15,7 @@ class Aquav(spec_models.StackedModel): _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aquaviario_v4_00" ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aquaviario_v4_00" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") @@ -46,6 +47,12 @@ class Balsa(spec_models.SpecModel): _inherit = "cte.40.balsa" _description = "Grupo de informações das balsas" + _cte40_stacking_mixin = "cte.40.balsa" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_aquaviario_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_aquaviario_v4_00" + document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") cte40_xBalsa = fields.Char(string="Identificador da Balsa") diff --git a/l10n_br_cte/models/document.py b/l10n_br_cte/models/document.py index f2945c66e594..164c5e73a0e5 100644 --- a/l10n_br_cte/models/document.py +++ b/l10n_br_cte/models/document.py @@ -1,22 +1,30 @@ # Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import base64 import logging import re +import string import sys from datetime import datetime +from enum import Enum -from erpbrasil.edoc.cte import TransmissaoCTE +from erpbrasil.base.fiscal import cnpj_cpf + +# TODO: precisa tratar +# from erpbrasil.edoc.cte import TransmissaoCTE +from erpbrasil.base.fiscal.edoc import ChaveEdoc from lxml import etree from nfelib.cte.bindings.v4_0.cte_v4_00 import Cte from nfelib.cte.bindings.v4_0.proc_cte_v4_00 import CteProc -from nfelib.nfe.ws.edoc_legacy import CTeAdapter as edoc_cte -from requests import Session + +# TODO: precisa tratar nfelib +# from nfelib.nfe.ws.edoc_legacy import CTeAdapter as edoc_cte from xsdata.formats.dataclass.parsers import XmlParser from odoo import _, api, fields -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError from odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_ferroviario_v4_00 import ( FERROV_TPTRAF, @@ -51,7 +59,27 @@ from odoo.addons.l10n_br_fiscal.constants.icms import ICMS_CST, ICMS_SN_CST from odoo.addons.spec_driven_model.models import spec_models -from ..constants.modal import CTE_MODAL_VERSION_DEFAULT +from ..constants.cte import ( + CTE_CST, + CTE_ENVIRONMENTS, + CTE_ICMS_SELECTION, + CTE_INDIETOMA, + CTE_INDIETOMA_DEFAULT, + CTE_TPEMIS, + CTE_TPEMIS_DEFAULT, + CTE_TPIMP, + CTE_TPIMP_DEFAULT, + CTE_TPSERV, + CTE_TPSERV_DEFAULT, + CTE_TRANSMISSIONS, + CTE_TYPE, + CTE_TYPE_DEFAULT, +) +from ..constants.modal import ( + CTE_MODAL_DEFAULT, + CTE_MODAL_VERSION_DEFAULT, + CTE_MODALS, +) CTE_XML_NAMESPACE = {"cte": "http://www.portalfiscal.inf.br/cte"} @@ -60,10 +88,6 @@ _logger = logging.getLogger(__name__) -try: - pass -except ImportError: - _logger.error("Biblioteca erpbrasil.base não in stalada") def filter_processador_edoc_cte(record): @@ -80,25 +104,23 @@ class CTe(spec_models.StackedModel): _inherit = [ "l10n_br_fiscal.document", "cte.40.tcte_infcte", - "cte.40.tcte_imp", - "cte.40.tcte_fat", ] _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" ) _cte40_stacking_mixin = "cte.40.tcte_infcte" _cte40_stacking_skip_paths = ( - "cte40_fluxo", "cte40_semData", - "cte40_noInter", - "cte40_comHora", "cte40_noPeriodo", + "cte40_comHora", + "cte40_noInter", + # "cte40_NFref_ide_id", ) + # all m2o at this level will be stacked even if not required: _cte40_stacking_force_paths = ( "infcte.compl", - "infcte.compl.entrega" "infcte.vprest", - "infcte.imp", + # "infcte.compl.entrega", ) _cte_search_keys = ["cte40_Id"] @@ -133,6 +155,28 @@ class CTe(spec_models.StackedModel): > > """ + cte40_versao = fields.Char(related="document_version") + + cte_version = fields.Selection( + string="CT-e Version", + related="company_id.cte_version", + readonly=False, + ) + + cte_environment = fields.Selection( + selection=CTE_ENVIRONMENTS, + string="CTe Environment", + copy=False, + default=lambda self: self.env.company.cte_environment, + ) + + cte_transmission = fields.Selection( + selection=CTE_TRANSMISSIONS, + string="CTE Transmission", + copy=False, + default=lambda self: self.env.company.cte_transmission, + ) + ########################## # CT-e spec related fields ########################## @@ -141,21 +185,23 @@ class CTe(spec_models.StackedModel): # CT-e tag: infCte ########################## - cte40_versao = fields.Char(related="document_version") - cte40_Id = fields.Char( - compute="_compute_cte40_Id", - inverse="_inverse_cte40_Id", + compute="_compute_cte40_id", + inverse="_inverse_cte40_id", ) ########################## # CT-e tag: Id - # Methods + # Compute Methods ########################## @api.depends("document_type_id", "document_key") - def _compute_cte40_Id(self): + def _compute_cte40_id(self): + """Set schema data which are not just related fields""" + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_Id = False + if ( record.document_type_id and record.document_type_id.prefix @@ -164,10 +210,13 @@ def _compute_cte40_Id(self): record.cte40_Id = "{}{}".format( record.document_type_id.prefix, record.document_key ) - else: - record.cte40_Id = False - def _inverse_cte40_Id(self): + ########################## + # CT-e tag: id + # Inverse Methods + ########################## + + def _inverse_cte40_id(self): for record in self: if record.cte40_Id: record.document_key = re.findall(r"\d+", str(record.cte40_Id))[0] @@ -181,9 +230,9 @@ def _inverse_cte40_Id(self): string="cte40_cUF", ) - cte40_cCT = fields.Char(compute="_compute_cct") + cte40_cCT = fields.Char(compute="_compute_cte40_cct") - cte40_CFOP = fields.Char(compute="_compute_CFOP", store=True) + cte40_CFOP = fields.Char(compute="_compute_cte40_CFOP", store=True) cte40_natOp = fields.Char(related="operation_name") @@ -195,7 +244,9 @@ def _inverse_cte40_Id(self): cte40_dhEmi = fields.Datetime(related="document_date") - cte40_cDV = fields.Char(compute="_compute_cDV", store=True) + # TODO: Tratar/Avaliar + # cte40_cDV = fields.Char(compute="_compute_cte40_cDV", store=True) + # cte40_cDV = fields.Char(related="key_check_digit") cte40_procEmi = fields.Selection(default="0") @@ -206,146 +257,154 @@ def _inverse_cte40_Id(self): .get_param("l10n_br_cte.version.name", default="Odoo Brasil OCA v14"), ) - cte40_cMunEnv = fields.Char(compute="_compute_cte40_data", store=True) + cte40_cMunEnv = fields.Char( + compute="_compute_cte40_data", + store=True, + compute_sudo=True, + ) - cte40_xMunEnv = fields.Char(compute="_compute_cte40_data", store=True) + cte40_xMunEnv = fields.Char( + compute="_compute_cte40_data", + store=True, + compute_sudo=True, + ) - cte40_UFEnv = fields.Char(compute="_compute_cte40_data", store=True) + cte40_UFEnv = fields.Char( + compute="_compute_cte40_data", + store=True, + compute_sudo=True, + ) cte40_indIEToma = fields.Selection( - selection=[ - ("1", "Contribuinte ICMS"), - ("2", "Contribuinte isento de inscrição"), - ("9", "Não Contribuinte"), - ], - default="1", + selection=CTE_INDIETOMA, + default=CTE_INDIETOMA_DEFAULT, ) - cte40_cMunIni = fields.Char(compute="_compute_cte40_data") + cte40_cMunIni = fields.Char( + compute="_compute_cte40_data", + compute_sudo=True, + ) - cte40_xMunIni = fields.Char(compute="_compute_cte40_data") + cte40_xMunIni = fields.Char( + compute="_compute_cte40_data", + compute_sudo=True, + ) - cte40_UFIni = fields.Char(compute="_compute_cte40_data") + cte40_UFIni = fields.Char() - cte40_cMunFim = fields.Char(compute="_compute_cte40_data") + cte40_cMunFim = fields.Char( + compute="_compute_cte40_data", + compute_sudo=True, + ) - cte40_xMunFim = fields.Char(compute="_compute_cte40_data") + cte40_xMunFim = fields.Char( + compute="_compute_cte40_data", + compute_sudo=True, + ) - cte40_UFFim = fields.Char(compute="_compute_cte40_data") + cte40_UFFim = fields.Char() cte40_retira = fields.Selection(selection=[("0", "Sim"), ("1", "Não")], default="1") cte40_tpServ = fields.Selection( - selection=[ - ("0", "Normal"), - ("1", "Subcontratação"), - ("2", "Redespacho"), - ("3", "Redespacho Intermediário"), - ], - default="0", + selection=CTE_TPSERV, + default=CTE_TPSERV_DEFAULT, ) cte40_tpCTe = fields.Selection( - selection=[ - ("0", "CTe Normal"), - ("1", "CTe Complementar"), - ("3", "CTe Substituição"), - ], - default="0", + selection=CTE_TYPE, + default=CTE_TYPE_DEFAULT, ) - cte40_tpAmb = fields.Selection( - selection=[("1", "Produção"), ("2", "Homologação")], - string="CTe Environment", - copy=False, - default="2", - ) + cte40_tpAmb = fields.Selection(related="cte_environment") cte40_tpEmis = fields.Selection( - selection=[ - ("1", "Normal"), - ("3", "Regime Especial NFF"), - ("4", "EPEC pela SVC"), - ], - default="1", - ) - - cte40_tpImp = fields.Selection( - selection=[("1", "Retrato"), ("2", "Paisagem")], default="1" + selection=CTE_TPEMIS, + default=CTE_TPEMIS_DEFAULT, ) - def _export_fields_cte_40_toma3(self, xsd_fields, class_obj, export_dict): - if self.cte40_choice_toma == "cte40_toma4": - xsd_fields.remove("cte40_toma") - - def _export_fields_cte_40_tcte_toma4(self, xsd_fields, class_obj, export_dict): - if self.cte40_choice_toma == "cte40_toma3": - xsd_fields.remove("cte40_toma") - xsd_fields.remove("cte40_CNPJ") - xsd_fields.remove("cte40_CPF") - xsd_fields.remove("cte40_IE") - xsd_fields.remove("cte40_xNome") - xsd_fields.remove("cte40_xFant") - xsd_fields.remove("cte40_enderToma") + cte40_tpImp = fields.Selection(selection=CTE_TPIMP, default=CTE_TPIMP_DEFAULT) # toma + cte40_toma = fields.Selection(related="service_provider") + cte40_choice_toma = fields.Selection( selection=[ ("cte40_toma3", "toma3"), ("cte40_toma4", "toma4"), ], - compute="_compute_toma", - store=True, + compute="_compute_cte40_choice_toma", ) - cte40_toma = fields.Selection(related="service_provider") - cte40_enderToma = fields.Many2one(comodel_name="res.partner", related="partner_id") + cte40_enderReme = fields.Many2one(comodel_name="res.partner") + ########################## # CT-e tag: ide # Compute Methods ########################## - @api.depends("service_provider") - def _compute_toma(self): - for doc in self: - if doc.service_provider in ["0", "1", "2", "3"]: - doc.cte40_choice_toma = "cte40_toma3" + @api.depends("company_id", "partner_id", "service_provider") + def _compute_cte40_choice_toma(self): + for rec in self.filtered(filter_processador_edoc_cte): + if rec.service_provider in ["0", "1", "2", "3"]: + rec.cte40_choice_toma = "cte40_toma3" else: - doc.cte40_choice_toma = "cte40_toma4" + rec.cte40_choice_toma = "cte40_toma4" - @api.depends("fiscal_line_ids") - def _compute_CFOP(self): - for rec in self: + def _export_fields_cte_40_toma3(self, xsd_fields, class_obj, export_dict): + if self.cte40_choice_toma == "cte40_toma4": + xsd_fields.remove("cte40_toma") + + def _export_fields_cte_40_tcte_toma4(self, xsd_fields, class_obj, export_dict): + if self.cte40_choice_toma == "cte40_toma3": + xsd_fields.remove("cte40_toma") + xsd_fields.remove("cte40_CNPJ") + xsd_fields.remove("cte40_CPF") + xsd_fields.remove("cte40_IE") + xsd_fields.remove("cte40_xNome") + xsd_fields.remove("cte40_xFant") + xsd_fields.remove("cte40_enderToma") + + @api.depends("fiscal_line_ids", "fiscal_line_ids.cfop_id") + def _compute_cte40_CFOP(self): + for rec in self.filtered(filter_processador_edoc_cte): if rec.fiscal_line_ids: rec.cte40_CFOP = rec.fiscal_line_ids[0].cfop_id.code - @api.depends("document_key") - def _compute_cDV(self): - for rec in self: - if rec.document_key: - rec.cte40_cDV = rec.document_key[-1] + # TODO: Tratar + # @api.depends("document_key") + # def _compute_cte40_cDV(self): + # for rec in self.filtered(filter_processador_edoc_cte): + # if rec.document_key: + # rec.cte40_cDV = rec.document_key[-1] - def _compute_cct(self): - for rec in self: + def _compute_cte40_cct(self): + for rec in self.filtered(filter_processador_edoc_cte): if rec.document_key: rec.cte40_cCT = rec.document_key[35:43] @api.depends( "partner_id", "company_id", - "cte40_rem", - "cte40_dest", - "cte40_exped", - "cte40_receb", + "partner_sendering_id", + "partner_shippering_id", + "partner_shipping_id", + "partner_receivering_id", ) def _compute_cte40_data(self): - for doc in self: + for doc in self.filtered(filter_processador_edoc_cte): if doc.company_id.partner_id.country_id == doc.partner_id.country_id: - doc.cte40_xMunEnv = ( - doc.company_id.partner_id.city_id.name - ) # TODO: provavelmente vai depender de quem é o emissor + if doc.issuer == DOCUMENT_ISSUER_COMPANY: + doc.cte40_xMunEnv = ( + doc.company_id.partner_id.city_id.name + ) # TODO: provavelmente vai depender de quem é o emissor + else: + doc.cte40_xMunEnv = ( + doc.partner_id.city_id.name + ) # TODO: provavelmente vai depender de quem é o emissor + doc.cte40_cMunEnv = doc.company_id.partner_id.city_id.ibge_code doc.cte40_UFEnv = doc.company_id.partner_id.state_id.code doc.cte40_xMunIni = ( @@ -382,6 +441,25 @@ def _compute_cte40_data(self): doc.cte40_xMunFim = "EXTERIOR" doc.cte40_UFFim = "EX" + # TODO: nao esta rodando direto.. corrigir + def _compute_cte40_infQ(self): + for record in self.filtered(filter_processador_edoc_cte): + cargo_info_vals = [ + {"cte40_cUnid": "01", "cte40_tpMed": "Peso Bruto", "cte40_qCarga": 0}, + { + "cte40_cUnid": "01", + "cte40_tpMed": "Peso Base Calculado", + "cte40_qCarga": 0, + }, + {"cte40_cUnid": "01", "cte40_tpMed": "Peso Aferido", "cte40_qCarga": 0}, + {"cte40_cUnid": "00", "cte40_tpMed": "Cubagem", "cte40_qCarga": 0}, + {"cte40_cUnid": "03", "cte40_tpMed": "Unidade", "cte40_qCarga": 0}, + ] + + record.cte40_infQ = self.env["l10n_br_cte.cargo.quantity.infos"].create( + cargo_info_vals + ) + ########################## # CT-e tag: compl ########################## @@ -402,7 +480,7 @@ def _compute_cte40_data(self): @api.depends("comment_ids") def _compute_cte40_obsCont(self): - for doc in self: + for doc in self.filtered(filter_processador_edoc_cte): doc.cte40_obsCont = doc.comment_ids.filtered( lambda c: c.comment_type == "commercial" ) @@ -411,7 +489,7 @@ def _compute_cte40_obsCont(self): ) def _compute_cte40_compl(self): - for doc in self: + for doc in self.filtered(filter_processador_edoc_cte): fiscal_data = ( doc.fiscal_additional_data if doc.fiscal_additional_data else "" ) @@ -437,27 +515,15 @@ def _compute_cte40_compl(self): # CT-e tag: emit ########################## - cte40_CNPJ = fields.Char( - compute="_compute_emit_data", - ) - cte40_CPF = fields.Char( - compute="_compute_emit_data", - ) - cte40_IE = fields.Char( - compute="_compute_emit_data", - ) - cte40_xNome = fields.Char( - compute="_compute_emit_data", - ) - cte40_xFant = fields.Char( - compute="_compute_emit_data", - ) - cte40_enderEmit = fields.Many2one( - comodel_name="res.partner", compute="_compute_emit_data" + cte40_emit = fields.Many2one( + comodel_name="res.company", + compute="_compute_cte_emit_data", + string="Emit", ) cte40_CRT = fields.Selection( - compute="_compute_emit_data", + related="company_tax_framework", + string="Código de Regime Tributário (CTe)", ) ########################## @@ -465,25 +531,23 @@ def _compute_cte40_compl(self): # Compute Methods ########################## - @api.depends("company_id", "partner_id", "issuer") - def _compute_emit_data(self): - for doc in self: - if doc.issuer == DOCUMENT_ISSUER_COMPANY: - doc.cte40_CNPJ = doc.company_id.partner_id.cte40_CNPJ - doc.cte40_CPF = doc.company_id.partner_id.cte40_CPF - doc.cte40_IE = doc.company_id.partner_id.cte40_IE - doc.cte40_xNome = doc.company_id.partner_id.legal_name - doc.cte40_xFant = doc.company_id.partner_id.name - doc.cte40_enderEmit = doc.company_id.partner_id - doc.cte40_CRT = doc.company_tax_framework - else: - doc.cte40_CNPJ = doc.partner_id.cte40_CNPJ - doc.cte40_CPF = doc.partner_id.cte40_CPF - doc.cte40_IE = doc.partner_id.cte40_IE - doc.cte40_xNome = doc.partner_id.legal_name - doc.cte40_xFant = doc.partner_id.name - doc.cte40_enderEmit = doc.partner_id - doc.cte40_CRT = doc.partner_tax_framework + @api.depends("partner_id", "company_id") + def _compute_cte_emit_data(self): + for doc in self: # TODO if out + doc.cte40_emit = doc.company_id + + def _set_cte40_IEST(self): + self.ensure_one() + iest = "" + if self.partner_id: + dest_state_id = self.partner_id.state_id + if dest_state_id in self.company_id.state_tax_number_ids.mapped("state_id"): + stn_id = self.company_id.state_tax_number_ids.filtered( + lambda stn: stn.state_id == dest_state_id + ) + iest = stn_id.inscr_est + iest = re.sub("[^0-9]+", "", iest) + self.company_inscr_est_st = iest ########################## # CT-e tag: rem @@ -492,6 +556,7 @@ def _compute_emit_data(self): cte40_rem = fields.Many2one( comodel_name="res.partner", string="Remetente", + related="partner_sendering_id", ) ########################## @@ -501,6 +566,7 @@ def _compute_emit_data(self): cte40_exped = fields.Many2one( comodel_name="res.partner", string="Expedidor", + related="partner_shippering_id", ) ########################## @@ -508,7 +574,9 @@ def _compute_emit_data(self): ########################## cte40_dest = fields.Many2one( - comodel_name="res.partner", string="Destinatário", related="partner_shipping_id" + comodel_name="res.partner", + string="Destinatário", + related="partner_shipping_id", ) ########################## @@ -518,22 +586,16 @@ def _compute_emit_data(self): cte40_receb = fields.Many2one( comodel_name="res.partner", string="Recebedor", + related="partner_receivering_id", ) ########################## # CT-e tag: vPrest - # Methods ########################## - cte40_vTPrest = fields.Monetary( - compute="_compute_cte40_vPrest", - string="Valor da Total Prestação Base de Cálculo", - ) + cte40_vTPrest = fields.Monetary(related="amount_total") - cte40_vRec = fields.Monetary( - compute="_compute_cte40_vPrest", - string="Valor Recebido", - ) + cte40_vRec = fields.Monetary(related="amount_price_gross") cte40_comp = fields.One2many( comodel_name="l10n_br_fiscal.document.line", @@ -541,16 +603,6 @@ def _compute_emit_data(self): related="fiscal_line_ids", ) - def _compute_cte40_vPrest(self): - vTPrest = 0 - vRec = 0 - for doc in self: - for line in self.fiscal_line_ids: - vTPrest += line.amount_total - vRec += line.price_gross - doc.cte40_vTPrest = vTPrest - doc.cte40_vRec = vRec - ################################################## # CT-e tag: ICMS # Grupo N01. Grupo Tributação do ICMS= 00 @@ -564,6 +616,7 @@ def _compute_cte40_vPrest(self): cte40_vTotTrib = fields.Monetary(related="amount_estimate_tax") + # TODO: Tratar # cte40_infAdFisco = fields.Text(related="additional_data") ################################################## @@ -572,32 +625,16 @@ def _compute_cte40_vPrest(self): ################################################## cte40_choice_icms = fields.Selection( - selection=[ - ("cte40_ICMS00", "ICMS00"), - ("cte40_ICMS20", "ICMS20"), - ("cte40_ICMS45", "ICMS45"), - ("cte40_ICMS60", "ICMS60"), - ("cte40_ICMS90", "ICMS90"), - ("cte40_ICMSOutraUF", "ICMSOutraUF"), - ("cte40_ICMSSN", "ICMSSN"), - ], + selection=CTE_ICMS_SELECTION, string="Tipo de ICMS", - compute="_compute_choice_icms", + compute="_compute_cte40_choice_icms", store=True, ) cte40_CST = fields.Selection( - selection=[ - ("00", "00 - Tributação normal ICMS"), - ("20", "20 - Tributação com BC reduzida do ICMS"), - ("45", "45 - ICMS Isento, não Tributado ou diferido"), - ("60", "60 - ICMS cobrado por substituição tributária"), - ("90", "90 - ICMS outros"), - ("90", "90 - ICMS Outra UF"), - ("01", "01 - Simples Nacional"), - ], + selection=CTE_CST, string="Classificação Tributária do Serviço", - compute="_compute_choice_icms", + compute="_compute_cte40_choice_icms", store=True, ) @@ -612,9 +649,9 @@ def _compute_cte40_vPrest(self): # Compute Methods ########################## - @api.depends("fiscal_line_ids") - def _compute_choice_icms(self): - for record in self: + @api.depends("fiscal_line_ids", "fiscal_line_ids.icms_cst_id") + def _compute_cte40_choice_icms(self): + for record in self.filtered(filter_processador_edoc_cte): record.cte40_choice_icms = None record.cte40_CST = None if not record.fiscal_line_ids: @@ -637,7 +674,7 @@ def _compute_choice_icms(self): record.cte40_choice_icms = "cte40_ICMSSN" record.cte40_CST = "90" - def _export_fields_icms(self): + def _export_fields_cte40_icms(self): # Verifica se fiscal_line_ids está vazio para evitar erros if not self.fiscal_line_ids: return {} @@ -680,25 +717,26 @@ def _export_fields_icms(self): def _export_fields_cte_40_timp(self, xsd_fields, class_obj, export_dict): # TODO Not Implemented - if "cte40_ICMSOutraUF" in xsd_fields: - xsd_fields.remove("cte40_ICMSOutraUF") - - xsd_fields = [self.cte40_choice_icms] - icms_tag = ( - self.cte40_choice_icms.replace("cte40_", "") - .replace("ICMSSN", "Icmssn") - .replace("ICMS", "Icms") - ) - binding_module = sys.modules[self._binding_module] - icms = binding_module.Timp - icms_binding = getattr(icms, icms_tag) - icms_dict = self._export_fields_icms() - sliced_icms_dict = { - key: icms_dict.get(key) - for key in icms_binding.__dataclass_fields__.keys() - if icms_dict.get(key) - } - export_dict[icms_tag.upper()] = icms_binding(**sliced_icms_dict) + for record in self.filtered(filter_processador_edoc_cte): + if "cte40_ICMSOutraUF" in xsd_fields: + xsd_fields.remove("cte40_ICMSOutraUF") + + xsd_fields = [record.cte40_choice_icms] + icms_tag = ( + record.cte40_choice_icms.replace("cte40_", "") + .replace("ICMSSN", "Icmssn") + .replace("ICMS", "Icms") + ) + binding_module = sys.modules[record._get_spec_property("binding_module")] + icms = binding_module.Timp + icms_binding = getattr(icms, icms_tag) + icms_dict = record._export_fields_cte40_icms() + sliced_icms_dict = { + key: icms_dict.get(key) + for key in icms_binding.__dataclass_fields__.keys() + if icms_dict.get(key) + } + export_dict[icms_tag.upper()] = icms_binding(**sliced_icms_dict) # ########################## # # CT-e tag: ICMSUFFim @@ -739,16 +777,15 @@ def _export_fields_cte_40_timp(self, xsd_fields, class_obj, export_dict): default="cte40_infCTeNorm", ) - cte40_infCTeNorm = fields.One2many( - comodel_name="l10n_br_cte.normal.infos", + # def _compute_cte40_infDoc(self): + # for doc in self: + # doc.cte40_infDoc = doc + + cte40_infCTeComp = fields.One2many( + comodel_name="l10n_br_fiscal.document.related", inverse_name="document_id", ) - # cte40_infCTeComp = fields.One2many( - # comodel_name="l10n_br_fiscal.document.related", - # inverse_name="document_id", - # ) - ########################## # CT-e tag: infCarga ########################## @@ -788,17 +825,17 @@ def _export_fields_cte_40_timp(self, xsd_fields, class_obj, export_dict): ) def _compute_cte40_infDoc(self): - for doc in self: + for doc in self.filtered(filter_processador_edoc_cte): doc.cte40_infDoc = doc def _compute_cte40_infNFe(self): - for record in self: + for record in self.filtered(filter_processador_edoc_cte): record.cte40_infNFe = record.document_related_ids.filtered( lambda r: r.cte40_infDoc == "cte40_infNFe" ) def _compute_cte40_infOutros(self): - for record in self: + for record in self.filtered(filter_processador_edoc_cte): record.cte40_infOutros = record.document_related_ids.filtered( lambda r: r.cte40_infDoc == "cte40_infOutros" ) @@ -860,30 +897,23 @@ def _default_cte40_autxml(self): cte40_infRespTec = fields.Many2one( comodel_name="res.partner", - compute="_compute_infresptec", - string="Responsável Técnico CTe", + related="company_id.technical_support_id", ) - ########################## - # MDF-e tag: infRespTec - # Methods - ########################## - - @api.depends("company_id.technical_support_id") - def _compute_infresptec(self): - for record in self.filtered(filter_processador_edoc_cte): - record.cte40_infRespTec = record.company_id.technical_support_id - ########################## # CT-e tag: infmodal ########################## cte40_modal = fields.Selection(related="transport_modal") + cte_modal = fields.Selection( + selection=CTE_MODALS, string="Transport Modal", default=CTE_MODAL_DEFAULT + ) + cte40_versaoModal = fields.Char(default=CTE_MODAL_VERSION_DEFAULT) # Campos do Modal Aereo - modal_aereo_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aereo") + cte_modal_aereo_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aereo") cte40_nMinu = fields.Char( string="Número da Minuta", @@ -924,8 +954,8 @@ def _compute_infresptec(self): ), ) - # TODO: avaliar - # def _compute_dime(self): + # TODO: Tratar + # def _compute_cte40_dime(self): # for record in self: # for package in record.product_id.packaging_ids: # record.cte40_xDime = ( @@ -963,9 +993,9 @@ def _compute_infresptec(self): ) # Campos do Modal Aquaviario - modal_aquaviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aquav") + cte_modal_aquaviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.aquav") - # TODO: fix + # TODO: Tratar # cte40_vPrest = fields.Monetary( # compute="_compute_cte40_vPrest", # FIX # store=True, @@ -1017,7 +1047,7 @@ def _compute_infresptec(self): ) # Campos do Modal Dutoviario - modal_dutoviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.duto") + cte_modal_dutoviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.duto") cte40_dIni = fields.Date(string="Data de Início da prestação do serviço") @@ -1026,7 +1056,7 @@ def _compute_infresptec(self): cte40_vTar = fields.Float(string="Valor da tarifa") # Campos do Modal Ferroviario - modal_ferroviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.ferrov") + cte_modal_ferroviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.ferrov") cte40_tpTraf = fields.Selection( selection=FERROV_TPTRAF, @@ -1042,6 +1072,10 @@ def _compute_infresptec(self): ), ) + cte40_pass = fields.Char( + string="Fluxo Pass", + ) + cte40_vFrete = fields.Monetary( related="amount_freight_value", string="Valor do Frete do Tráfego Mútuo", @@ -1074,19 +1108,28 @@ def _compute_infresptec(self): ) # Campos do Modal rodoviario - modal_rodoviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.rodo") + cte_modal_rodoviario_id = fields.Many2one(comodel_name="l10n_br_cte.modal.rodo") cte40_RNTRC = fields.Char( string="RNTRC", help="Registro Nacional de Transportadores Rodoviários de Carga", compute="_compute_cte40_RNTRC", + store=True, ) + @api.depends( + "issuer", + "company_id", + "company_id.partner_id.rntrc_code", + "partner_id", + "partner_id.rntrc_code", + ) def _compute_cte40_RNTRC(self): - for record in self: - if record.issuer == DOCUMENT_ISSUER_COMPANY: + for record in self.filtered(filter_processador_edoc_cte): + record.cte40_RNTRC = None + if record.issuer == DOCUMENT_ISSUER_COMPANY and record.company_id: record.cte40_RNTRC = record.company_id.partner_id.rntrc_code - else: + elif record.partner_id: record.cte40_RNTRC = record.partner_id.rntrc_code cte40_occ = fields.One2many( @@ -1103,53 +1146,55 @@ def _compute_cte40_RNTRC(self): def _export_fields_cte_40_tcte_infmodal(self, xsd_fields, class_obj, export_dict): if self.cte40_modal == "01": - export_dict["any_element"] = self._export_modal_rodoviario() + export_dict["any_element"] = self._export_cte_modal_rodoviario() elif self.cte40_modal == "02": - export_dict["any_element"] = self._export_modal_aereo() + export_dict["any_element"] = self._export_cte_modal_aereo() elif self.cte40_modal == "03": - export_dict["any_element"] = self._export_modal_aquaviario() + export_dict["any_element"] = self._export_cte_modal_aquaviario() elif self.cte40_modal == "04": - export_dict["any_element"] = self._export_modal_ferroviario() + export_dict["any_element"] = self._export_cte_modal_ferroviario() elif self.cte40_modal == "05": - export_dict["any_element"] = self._export_modal_dutoviario() + export_dict["any_element"] = self._export_cte_modal_dutoviario() - def _export_modal_aereo(self): - if not self.modal_aereo_id: - self.modal_aereo_id = self.modal_aereo_id.create({"document_id": self.id}) + def _export_cte_modal_aereo(self): + if not self.cte_modal_aereo_id: + self.cte_modal_aereo_id = self.cte_modal_aereo_id.create( + {"document_id": self.id} + ) - return self.modal_aereo_id._build_binding("cte", "40") + return self.cte_modal_aereo_id._build_binding("cte", "40") - def _export_modal_ferroviario(self): - if not self.modal_ferroviario_id: - self.modal_ferroviario_id = self.modal_ferroviario_id.create( + def _export_cte_modal_ferroviario(self): + if not self.cte_modal_ferroviario_id: + self.cte_modal_ferroviario_id = self.cte_modal_ferroviario_id.create( {"document_id": self.id} ) - return self.modal_ferroviario_id._build_binding("cte", "40") + return self.cte_modal_ferroviario_id._build_binding("cte", "40") - def _export_modal_aquaviario(self): - if not self.modal_aquaviario_id: - self.modal_aquaviario_id = self.modal_aquaviario_id.create( + def _export_cte_modal_aquaviario(self): + if not self.cte_modal_aquaviario_id: + self.cte_modal_aquaviario_id = self.cte_modal_aquaviario_id.create( {"document_id": self.id} ) - return self.modal_aquaviario_id._build_binding("cte", "40") + return self.cte_modal_aquaviario_id._build_binding("cte", "40") - def _export_modal_rodoviario(self): - if not self.modal_rodoviario_id: - self.modal_rodoviario_id = self.modal_rodoviario_id.create( + def _export_cte_modal_rodoviario(self): + if not self.cte_modal_rodoviario_id: + self.cte_modal_rodoviario_id = self.cte_modal_rodoviario_id.create( {"document_id": self.id} ) - return self.modal_rodoviario_id._build_binding("cte", "40") + return self.cte_modal_rodoviario_id._build_binding("cte", "40") - def _export_modal_dutoviario(self): - if not self.modal_dutoviario_id: - self.modal_dutoviario_id = self.modal_dutoviario_id.create( + def _export_cte_modal_dutoviario(self): + if not self.cte_modal_dutoviario_id: + self.cte_modal_dutoviario_id = self.cte_modal_dutoviario_id.create( {"document_id": self.id} ) - return self.modal_dutoviario_id._build_binding("cte", "40") + return self.cte_modal_dutoviario_id._build_binding("cte", "40") ################################ # Framework Spec model's methods @@ -1165,8 +1210,168 @@ def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): # campo xObs está no l10n_br_fiscal.document if xsd_field == "cte40_infGlobalizado": return False + if xsd_field == "cte40_toma4" and self.cte40_choice_toma == "cte40_toma3": + return False + elif xsd_field == "cte40_toma3" and self.cte40_choice_toma == "cte40_toma4": + return False return super()._export_field(xsd_field, class_obj, member_spec, export_value) + def _export_many2one(self, field_name, xsd_required, class_obj=None): + """ + Overriden to avoid creating inner tag for m2o if none of the + denormalized inner attribute has been set. + """ + self.ensure_one() + if field_name in self._get_stacking_points().keys(): + if field_name == "cte40_ISSQNtot" and not any( + t == "issqn" + for t in self.cte40_det.mapped("product_id.tax_icms_or_issqn") + ): + return False + + elif (not xsd_required) and field_name not in ["cte40_enderDest"]: + comodel = self.env[ + self._get_stacking_points().get(field_name).comodel_name + ] + fields = [ + f + for f in comodel._fields + if f.startswith(self._spec_prefix()) + and f in self._fields.keys() + and f + # don't try to cte40_fat id when reading cte40_cobr for instance + not in self._get_stacking_points().keys() + ] + sub_tag_read = self.read(fields)[0] + if not any( + v + for k, v in sub_tag_read.items() + if k.startswith(self._spec_prefix()) + ): + return False + + if ( + field_name == "cte40_emit" + and self.fiscal_operation_type == "out" + and self.issuer == "company" + ): + self._set_cte40_IEST() + res = super()._export_many2one(field_name, xsd_required, class_obj) + if self.company_inscr_est_st: + res.IEST = self.company_inscr_est_st + return res + + return super()._export_many2one(field_name, xsd_required, class_obj) + + @api.model + def _prepare_import_dict( + self, values, model=None, parent_dict=None, defaults_model=None + ): + return { + **super()._prepare_import_dict(values, model, parent_dict, defaults_model), + "imported_document": True, + } + + def _build_attr(self, node, fields, vals, path, attr): + key = f"cte40_{attr[0]}" # TODO schema wise + value = getattr(node, attr[0]) + + # if attr[0] == "any_element": # build modal + # modal_id = self._get_modal_to_build(node.any_element.__module__) + # if modal_id is False: + # return + + # modal_attrs = modal_id.build_attrs(value, path=path) + # for chave, valor in modal_attrs.items(): + # vals[chave] = valor + # return + + if key == "cte40_mod": + if isinstance(value, Enum): + value = value.value + + vals["document_type_id"] = ( + self.env["l10n_br_fiscal.document.type"] + .search([("code", "=", value)], limit=1) + .id + ) + + return super()._build_attr(node, fields, vals, path, attr) + + def _build_many2one(self, comodel, vals, new_value, key, value, path): + if key == "cte40_emit" and self.env.context.get("edoc_type") == "in": + enderEmit_value = self.env["res.partner"].build_attrs( + value.enderEmit, path=path + ) + new_value.update(enderEmit_value) + company_cnpj = self.env.company.cnpj_cpf.translate( + str.maketrans("", "", string.punctuation) + ) + emit_cnpj = new_value.get("cte40_CNPJ").translate( + str.maketrans("", "", string.punctuation) + ) + if company_cnpj != emit_cnpj: + vals["issuer"] = "partner" + new_value["is_company"] = True + new_value["cnpj_cpf"] = emit_cnpj + super()._build_many2one( + self.env["res.partner"], vals, new_value, "partner_id", value, path + ) + elif key == "cte40_dest" and self.env.context.get("edoc_type") == "out": + enderDest_value = self.env["res.partner"].build_attrs( + value.enderDest, path=path + ) + new_value.update(enderDest_value) + company_cnpj = self.env.company.cnpj_cpf.translate( + str.maketrans("", "", string.punctuation) + ) + dest_cnpj = new_value.get("cte40_CNPJ").translate( + str.maketrans("", "", string.punctuation) + ) + if company_cnpj != dest_cnpj: + vals["issuer"] = "partner" + new_value["is_company"] = True + new_value["cnpj_cpf"] = dest_cnpj + super()._build_many2one( + self.env["res.partner"], vals, new_value, "partner_id", value, path + ) + elif ( + self.env.context.get("edoc_type") == "in" + and key + in [ + "cte40_dest", + "cte40_enderDest", + ] + ) or ( + self.env.context.get("edoc_type") == "out" + and key + in [ + "cte40_emit", + "cte40_enderEmit", + ] + ): + # this would be the emit/company data, but we won't update it on + # CTe import so just do nothing + return + elif ( + self._name == "account.invoice" + and comodel._name == "l10n_br_fiscal.document" + ): + # module l10n_br_account_nfe + # stacked m2o + vals.update(new_value) + else: + super()._build_many2one(comodel, vals, new_value, key, value, path) + + @api.model + def _get_concrete_model(self, model_name): + result = super()._get_concrete_model(model_name) + if self._module == "l10n_br_cte" and not result: + model_type = model_name.split(".")[-1] + model_name = model_name.rpartition(".")[0] + ".tcte_" + model_type + result = super()._get_concrete_model(model_name) + return result + ################################ # Business Model Methods ################################ @@ -1177,33 +1382,42 @@ def _serialize(self, edocs): filter_processador_edoc_cte ): inf_cte = record._build_binding("cte", "40") + inf_cte_supl = None if record.cte40_infCTeSupl: inf_cte_supl = record.cte40_infCTeSupl._build_binding("cte", "40") + cte = Cte(infCte=inf_cte, infCTeSupl=inf_cte_supl, signature=None) edocs.append(cte) return edocs + # TODO: precisa tratar a lib nfelib + # def _edoc_processor(self): + # if self.document_type != MODELO_FISCAL_CTE: + # return super()._edoc_processor() + + # if not self.company_id.certificate_nfe_id: + # raise UserError(_("Certificado não encontrado")) + + # certificado = self.env.company._get_br_ecertificate() + # session = Session() + # session.verify = False + # transmissao = TransmissaoCTE(certificado, session) + # return edoc_cte( + # transmissao, + # self.company_id.state_id.ibge_code, + # self.cte40_versao, + # self.cte40_tpAmb, + # ) + def _edoc_processor(self): - if not self.company_id.certificate_nfe_id: - raise UserError(_("Certificado não encontrado")) - - certificado = self.env.company._get_br_ecertificate() - session = Session() - session.verify = False - transmissao = TransmissaoCTE(certificado, session) - return edoc_cte( - transmissao, - self.company_id.state_id.ibge_code, - self.cte40_versao, - self.cte40_tpAmb, - ) + super()._edoc_processor() def _document_export(self, pretty_print=True): result = super()._document_export() for record in self.filtered(filter_processador_edoc_cte): edoc = record.serialize()[0] - processador = record._edoc_processor() + # processador = record._edoc_processor() xml_file = edoc.to_xml() event_id = self.event_ids.create_event_save_xml( company_id=self.company_id, @@ -1215,12 +1429,18 @@ def _document_export(self, pretty_print=True): document_id=self, ) record.authorization_event_id = event_id - xml_assinado = processador.assina_raiz(edoc, edoc.infCte.Id) - self._validate_xml(xml_assinado) + + # TODO: precisa tratar + # xml_assinado = processador.assina_raiz(edoc, edoc.infCte.Id) + # self._validate_xml(xml_assinado) return result def _validate_xml(self, xml_file): self.ensure_one() + + if not self.filtered(filter_processador_edoc_cte): + return super()._validate_xml(xml_file) + erros = Cte.schema_validation(xml_file) erros = "\n".join(erros) self.write({"xml_error_message": erros or False}) @@ -1411,7 +1631,7 @@ def _cte_correction(self, justificative): def _document_qrcode(self): super()._document_qrcode() - for record in self: + for record in self.filtered(filter_processador_edoc_cte): record.cte40_infCTeSupl = self.env[ "l10n_br_fiscal.document.supplement" ].create( @@ -1421,11 +1641,12 @@ def _document_qrcode(self): ) def get_cte_qrcode(self): + # TODO: Tratar # if self.document_type != MODELO_FISCAL_CTE: # return processador = self._edoc_processor() - # if self.nfe_transmission == "1": + # if self.cte_transmission == "1": # return processador.monta_qrcode(self.document_key) return processador.monta_qrcode(self.document_key) @@ -1433,25 +1654,6 @@ def get_cte_qrcode(self): # xml = processador.assina_raiz(serialized_doc, serialized_doc.infNFe.Id) # return processador._generate_qrcode_contingency(serialized_doc, xml) - # TODO: nao esta rodando direto.. corrigir - def _compute_cte40_infQ(self): - for record in self: - cargo_info_vals = [ - {"cte40_cUnid": "01", "cte40_tpMed": "Peso Bruto", "cte40_qCarga": 0}, - { - "cte40_cUnid": "01", - "cte40_tpMed": "Peso Base Calculado", - "cte40_qCarga": 0, - }, - {"cte40_cUnid": "01", "cte40_tpMed": "Peso Aferido", "cte40_qCarga": 0}, - {"cte40_cUnid": "00", "cte40_tpMed": "Cubagem", "cte40_qCarga": 0}, - {"cte40_cUnid": "03", "cte40_tpMed": "Unidade", "cte40_qCarga": 0}, - ] - - record.cte40_infQ = self.env["l10n_br_cte.cargo.quantity.infos"].create( - cargo_info_vals - ) - def _need_compute_cte_tags(self): if ( self.state_edoc in [SITUACAO_EDOC_EM_DIGITACAO, SITUACAO_EDOC_A_ENVIAR] @@ -1463,38 +1665,6 @@ def _need_compute_cte_tags(self): else: return False - # cte40_infAdFisco = fields.Text(related="additional_data") - - # def make_pdf(self): - # if not self.filtered(filter_processador_edoc_cte): - # return super().make_pdf() - - # file_pdf = self.file_report_id - # self.file_report_id = False - # file_pdf.unlink() - - # if self.authorization_file_id: - # arquivo = self.authorization_file_id - # xml_string = base64.b64decode(arquivo.datas).decode() - # else: - # arquivo = self.send_file_id - # xml_string = base64.b64decode(arquivo.datas).decode() - # # TODO: implementar temp_xml_autorizacao igual nfe ? - # # xml_string = self.temp_xml_autorizacao(xml_string) - - # pdf = Dacte(xml=xml_string).output() - - # self.file_report_id = self.env["ir.attachment"].create( - # { - # "name": self.document_key + ".pdf", - # "res_model": self._name, - # "res_id": self.id, - # "datas": base64.b64encode(pdf), - # "mimetype": "application/pdf", - # "type": "binary", - # } - # ) - def _cte_response_add_proc(self, ws_response_process): """ Inject the final NF-e, tag `cteProc`, into the response. @@ -1549,3 +1719,69 @@ def _cte_create_proc(self, prot_element): proc_xml = processor.monta_cte_proc(doc=doc_element, prot=prot_element) return proc_xml + + def import_binding_cte(self, binding, edoc_type="out"): + document = ( + self.env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type=edoc_type, dry_run=False) + .build_from_binding("cte", "40", binding.CTe.infCte) + ) + + if edoc_type == "in" and document.company_id.cnpj_cpf != cnpj_cpf.formata( + binding.CTe.infCte.emit.CNPJ + ): + document.fiscal_operation_type = "in" + document.issuer = "partner" + + return document + + def _document_number(self): + # TODO: Criar campos no fiscal para codigo aleatorio e digito verificador, + # pois outros modelos também precisam dessescampos: CT-e, MDF-e etc + result = super()._document_number() + for record in self.filtered(filter_processador_edoc_cte): + if record.document_key: + try: + chave = ChaveEdoc(record.document_key) + record.cte40_cCT = chave.codigo_aleatorio + record.cte40_cDV = chave.digito_verificador + except Exception as e: + raise ValidationError( + _( + "%(name)s:\n %(error)s", + name=record.document_type_id.name, + error=e, + ) + ) from e + return result + + # TODO: Tratar + # def make_pdf(self): + # if not self.filtered(filter_processador_edoc_cte): + # return super().make_pdf() + + # file_pdf = self.file_report_id + # self.file_report_id = False + # file_pdf.unlink() + + # if self.authorization_file_id: + # arquivo = self.authorization_file_id + # xml_string = base64.b64decode(arquivo.datas).decode() + # else: + # arquivo = self.send_file_id + # xml_string = base64.b64decode(arquivo.datas).decode() + # # TODO: implementar temp_xml_autorizacao igual nfe ? + # # xml_string = self.temp_xml_autorizacao(xml_string) + + # pdf = Dacte(xml=xml_string).output() + + # self.file_report_id = self.env["ir.attachment"].create( + # { + # "name": self.document_key + ".pdf", + # "res_model": self._name, + # "res_id": self.id, + # "datas": base64.b64encode(pdf), + # "mimetype": "application/pdf", + # "type": "binary", + # } + # ) diff --git a/l10n_br_cte/models/document_line.py b/l10n_br_cte/models/document_line.py index f1a2c2ed53ad..dd6aa339cde1 100644 --- a/l10n_br_cte/models/document_line.py +++ b/l10n_br_cte/models/document_line.py @@ -1,4 +1,5 @@ # Copyright 2023 KMEE +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import fields diff --git a/l10n_br_cte/models/document_related.py b/l10n_br_cte/models/document_related.py index f68d6770d7d0..dde298e111d1 100644 --- a/l10n_br_cte/models/document_related.py +++ b/l10n_br_cte/models/document_related.py @@ -1,4 +1,5 @@ # Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import api, fields @@ -11,6 +12,7 @@ class CTeRelated(spec_models.StackedModel): _inherit = [ "l10n_br_fiscal.document.related", "cte.40.tcte_infdoc", + "cte.40.tcte_infctecomp", ] _cte40_odoo_module = ( @@ -107,3 +109,5 @@ def _inverse_cte40_choice_infNF_infNFE_infOutros(self): for rec in self: if rec.cte40_choice_infNF_infNFE_infOutros == "cte40_infNFe": rec.document_type_id = self.env.ref("l10n_br_fiscal.document_55") + if rec.cte40_choice_infNF_infNFE_infOutros == "infOutros": + rec.document_type_id = self.env.ref("l10n_br_fiscal.document_01") diff --git a/l10n_br_cte/models/document_type.py b/l10n_br_cte/models/document_type.py new file mode 100644 index 000000000000..972f15bb43d1 --- /dev/null +++ b/l10n_br_cte/models/document_type.py @@ -0,0 +1,9 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class DocumentType(models.Model): + _inherit = "l10n_br_fiscal.document.type" + _cte_search_keys = ["code"] diff --git a/l10n_br_cte/models/dutoviario.py b/l10n_br_cte/models/dutoviario.py index 8262a25e99a8..1dad821045f9 100644 --- a/l10n_br_cte/models/dutoviario.py +++ b/l10n_br_cte/models/dutoviario.py @@ -15,6 +15,7 @@ class Duto(spec_models.StackedModel): _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_dutoviario_v4_00" ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_dutoviario_v4_00" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/models/ferroviario.py b/l10n_br_cte/models/ferroviario.py index d3048e812212..24211db2606c 100644 --- a/l10n_br_cte/models/ferroviario.py +++ b/l10n_br_cte/models/ferroviario.py @@ -16,6 +16,7 @@ class Ferrov(spec_models.StackedModel): _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_ferroviario_v4_00" ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_ferroviario_v4_00" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/models/normal_cte_infos.py b/l10n_br_cte/models/normal_cte_infos.py deleted file mode 100644 index 9a893311c3b4..000000000000 --- a/l10n_br_cte/models/normal_cte_infos.py +++ /dev/null @@ -1,157 +0,0 @@ -# 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 CTeNormalInfos(spec_models.StackedModel): - _name = "l10n_br_cte.normal.infos" - _inherit = ["cte.40.tcte_infctenorm"] - _description = "Grupo de informações do CTe Normal e Substituto" - - _cte40_stacking_mixin = "cte.40.tcte_infctenorm" - _cte40_odoo_module = ( - "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - ) - _cte40_stacking_force_paths = "infctenorm.infdoc" - - document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") - - currency_id = fields.Many2one( - comodel_name="res.currency", - related="document_id.company_id.currency_id", - ) - - cte40_vCarga = fields.Monetary( - related="document_id.cte40_vCarga", - currency_field="currency_id", - ) - - cte40_proPred = fields.Char( - related="document_id.cte40_proPred", - ) - - cte40_xOutCat = fields.Char( - related="document_id.cte40_xOutCat", - ) - - cte40_infQ = fields.One2many( - comodel_name="l10n_br_cte.cargo.quantity.infos", - related="document_id.cte40_infQ", - ) - - cte40_vCargaAverb = fields.Monetary( - related="document_id.cte40_vCargaAverb", - ) - - cte40_veicNovos = fields.One2many( - comodel_name="l10n_br_cte.transported.vehicles", - related="document_id.cte40_veicNovos", - ) - - cte40_infNFe = fields.One2many( - comodel_name="l10n_br_fiscal.document.related", - related="document_id.cte40_infNFe", - ) - - cte40_infOutros = fields.One2many( - comodel_name="l10n_br_fiscal.document.related", - related="document_id.cte40_infOutros", - ) - - cte40_versaoModal = fields.Char(related="document_id.cte40_versaoModal") - - # Campos do Modal Aereo - modal_aereo_id = fields.Many2one( - comodel_name="l10n_br_cte.modal.aereo", related="document_id.modal_aereo_id" - ) - - cte40_nMinu = fields.Char(related="document_id.cte40_nMinu") - - cte40_nOCA = fields.Char(related="document_id.cte40_nOCA") - - cte40_dPrevAereo = fields.Date(related="document_id.cte40_dPrevAereo") - - cte40_xDime = fields.Char(related="document_id.cte40_xDime") - - cte40_CL = fields.Char(related="document_id.cte40_CL") - - cte40_cTar = fields.Char(related="document_id.cte40_cTar") - - # Existem dois vTar no spec, um float e um monetary, por isso a mudança de nome - cte40_aereo_vTar = fields.Monetary(related="document_id.cte40_aereo_vTar") - - cte40_peri = fields.One2many( - comodel_name="l10n_br_cte.modal.aereo.peri", related="document_id.cte40_peri" - ) - - # Campos do Modal Aquaviario - modal_aquaviario_id = fields.Many2one( - comodel_name="l10n_br_cte.modal.aquav", - related="document_id.modal_aquaviario_id", - ) - - cte40_vPrest = fields.Monetary( - related="document_id.cte40_vTPrest" - ) # TODO: avaliar melhor - - cte40_vAFRMM = fields.Monetary(related="document_id.cte40_vAFRMM") - - cte40_xNavio = fields.Char(related="document_id.cte40_xNavio") - - cte40_nViag = fields.Char(related="document_id.cte40_nViag") - - cte40_direc = fields.Selection(related="document_id.cte40_direc") - - cte40_irin = fields.Char(related="document_id.cte40_irin") - - cte40_tpNav = fields.Selection(related="document_id.cte40_tpNav") - - cte40_balsa = fields.One2many( - comodel_name="l10n_br_cte.modal.aquav.balsa", - related="document_id.cte40_balsa", - ) - - # Campos do Modal Dutoviario - modal_dutoviario_id = fields.Many2one( - comodel_name="l10n_br_cte.modal.duto", - related="document_id.modal_dutoviario_id", - ) - - cte40_dIni = fields.Date(related="document_id.cte40_dIni") - - cte40_dFim = fields.Date(related="document_id.cte40_dFim") - - cte40_vTar = fields.Float(related="document_id.cte40_vTar") - - # Campos do Modal Ferroviario - modal_ferroviario_id = fields.Many2one( - comodel_name="l10n_br_cte.modal.ferrov", - related="document_id.modal_ferroviario_id", - ) - - cte40_tpTraf = fields.Selection(related="document_id.cte40_tpTraf") - - cte40_fluxo = fields.Char(related="document_id.cte40_fluxo") - - cte40_vFrete = fields.Monetary(related="document_id.cte40_vFrete") - - cte40_respFat = fields.Selection(related="document_id.cte40_respFat") - - cte40_ferrEmi = fields.Selection(related="document_id.cte40_ferrEmi") - - cte40_ferroEnv = fields.Many2many(related="document_id.cte40_ferroEnv") - - # Campos do Modal rodoviario - modal_rodoviario_id = fields.Many2one( - comodel_name="l10n_br_cte.modal.rodo", - related="document_id.modal_rodoviario_id", - ) - - cte40_RNTRC = fields.Char(related="document_id.cte40_RNTRC") - - cte40_occ = fields.One2many( - comodel_name="l10n_br_cte.modal.rodo.occ", related="document_id.cte40_occ" - ) diff --git a/l10n_br_cte/models/res_city.py b/l10n_br_cte/models/res_city.py new file mode 100644 index 000000000000..9543aa6f487b --- /dev/null +++ b/l10n_br_cte/models/res_city.py @@ -0,0 +1,20 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ResCity(models.Model): + _inherit = "res.city" + _cte_search_keys = ["ibge_code"] + + @api.model + def match_or_create_m2o(self, rec_dict, parent_dict, model=None): + """If city not found, break hard, don't create it""" + + if rec_dict.get("ibge_code"): + domain = [("ibge_code", "=", rec_dict.get("ibge_code"))] + match = self.search(domain, limit=1) + if match: + return match.id + return False diff --git a/l10n_br_cte/models/res_company.py b/l10n_br_cte/models/res_company.py index c07ec7c855f8..14ff286fa3f4 100644 --- a/l10n_br_cte/models/res_company.py +++ b/l10n_br_cte/models/res_company.py @@ -1,12 +1,30 @@ # Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import api, fields +from odoo.addons.spec_driven_model.models import spec_models -class ResCompany(models.Model): +from ..constants.cte import ( + CTE_ENVIRONMENT_DEFAULT, + CTE_ENVIRONMENTS, + CTE_TRANSMISSION_DEFAULT, + CTE_TRANSMISSIONS, + CTE_TYPE, + CTE_TYPE_DEFAULT, + CTE_VERSION_DEFAULT, + CTE_VERSIONS, +) + +PROCESSADOR_ERPBRASIL_EDOC = "oca" +PROCESSADOR = [(PROCESSADOR_ERPBRASIL_EDOC, "erpbrasil.edoc")] + + +class ResCompany(spec_models.SpecModel): _name = "res.company" - _inherit = ["res.company"] + _inherit = ["res.company", "cte.40.tcte_emit"] + _cte_search_keys = ["cte40_CNPJ", "cte40_xNome", "cte40_xFant"] ########################## # CT-e models fields @@ -24,42 +42,32 @@ class ResCompany(models.Model): ) cte_transmission = fields.Selection( - selection=[ - ("1", "Normal"), - ("2", "Regime Especial NFF"), - ("4", "EPEC pela SVC"), - ("5", "Contingência FSDA"), - ("7", "Contingência SVC-RS"), - ("8", "Contingência SVC-SP"), - ], - string="CT-e Transmission Type", - default="1", + selection=CTE_TRANSMISSIONS, + string="CTe Transmission", + copy=False, + default=CTE_TRANSMISSION_DEFAULT, ) cte_type = fields.Selection( - selection=[ - ("0", "CT-e Normal"), - ("1", "CT-e de Complemento de Valores"), - ("3", "CT-e de Substituição"), - ], - string="CT-e Type", - default="0", + selection=CTE_TYPE, + string="CTe Type", + default=CTE_TYPE_DEFAULT, ) cte_environment = fields.Selection( - selection=[("1", "Produção"), ("2", "Homologação")], - string="CT-e Environment", - default="2", + selection=CTE_ENVIRONMENTS, + string="CTe Environment", + default=CTE_ENVIRONMENT_DEFAULT, ) cte_version = fields.Selection( - selection=[("3.00", "3.00"), ("4.00", "4.00")], - string="CT-e Version", - default="4.00", + selection=CTE_VERSIONS, + string="CTe Version", + default=CTE_VERSION_DEFAULT, ) processador_edoc = fields.Selection( - selection_add=[("erpbrasil.edoc", "erpbrasil.edoc")], + selection_add=PROCESSADOR, ) cte_authorize_accountant_download_xml = fields.Boolean( @@ -67,3 +75,61 @@ class ResCompany(models.Model): "download CTe XML", default=False, ) + + cte40_enderEmit = fields.Many2one( + comodel_name="res.partner", + related="partner_id", + readonly=False, + ) + + cte40_choice_emit = fields.Selection( + [("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ ou CPF?", + compute="_compute_cte_data", + ) + + cte40_CNPJ = fields.Char(related="partner_id.cte40_CNPJ") + + cte40_CPF = fields.Char(related="partner_id.cte40_CPF") + + cte40_xNome = fields.Char(related="partner_id.legal_name") + + cte40_xFant = fields.Char(related="partner_id.name") + + cte40_IE = fields.Char(related="partner_id.cte40_IE") + + cte40_fone = fields.Char(related="partner_id.cte40_fone") + + cte40_CRT = fields.Selection(related="tax_framework") + + def _compute_cte_data(self): + # compute because a simple related field makes the match_record fail + for rec in self: + if rec.partner_id.is_company: + rec.cte40_choice_emit = "cte40_CNPJ" + else: + rec.cte40_choice_emit = "cte40_CPF" + + def _build_attr(self, node, fields, vals, path, attr): + if attr[0] == "enderEmit" and self.env.context.get("edoc_type") == "in": + # we don't want to try build a related partner_id for enderEmit + # when importing an CTe + # instead later the emit tag will be imported as the + # document partner_id (dest) and the enderEmit data will be + # injected in the same res.partner record. + return + return super()._build_attr(node, fields, vals, path, attr) + + @api.model + def _prepare_import_dict( + self, values, model=None, parent_dict=None, defaults_model=None + ): + # we disable enderEmit related creation with dry_run=True + context = self._context.copy() + context["dry_run"] = True + values = super(ResCompany, self.with_context(**context))._prepare_import_dict( + values, model, parent_dict, defaults_model + ) + if not values.get("name"): + values["name"] = values.get("cte40_xFant") or values.get("cte40_xNome") + return values diff --git a/l10n_br_cte/models/res_config_settings.py b/l10n_br_cte/models/res_config_settings.py index 278b43cb80ac..f9c49cfdced4 100644 --- a/l10n_br_cte/models/res_config_settings.py +++ b/l10n_br_cte/models/res_config_settings.py @@ -1,3 +1,7 @@ +# Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + from odoo import fields, models @@ -10,3 +14,9 @@ class ResConfigSettings(models.TransientModel): related="company_id.cte_authorize_accountant_download_xml", readonly=False, ) + + cte_transmission = fields.Selection( + string="NFe Transmission", + related="company_id.cte_transmission", + readonly=False, + ) diff --git a/l10n_br_cte/models/res_country_state.py b/l10n_br_cte/models/res_country_state.py new file mode 100644 index 000000000000..8b74126d5cbb --- /dev/null +++ b/l10n_br_cte/models/res_country_state.py @@ -0,0 +1,21 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ResCountryState(models.Model): + _inherit = "res.country.state" + _cte_search_keys = ["ibge_code", "code"] + _cte_extra_domain = [("ibge_code", "!=", False)] + + @api.model + def match_or_create_m2o(self, rec_dict, parent_dict, model=None): + """If state not found, break hard, don't create it""" + + if rec_dict.get("code"): + domain = [("code", "=", rec_dict.get("code")), ("ibge_code", "!=", False)] + match = self.search(domain, limit=1) + if match: + return match.id + return False diff --git a/l10n_br_cte/models/res_partner.py b/l10n_br_cte/models/res_partner.py index bd7da37dd979..047bfdd39f56 100644 --- a/l10n_br_cte/models/res_partner.py +++ b/l10n_br_cte/models/res_partner.py @@ -1,8 +1,11 @@ # Copyright 2023 KMEE INFORMATICA LTDA +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import logging +from erpbrasil.base.fiscal import cnpj_cpf + from odoo import api, fields from odoo.addons.spec_driven_model.models import spec_models @@ -23,115 +26,417 @@ class ResPartner(spec_models.SpecModel): "cte.40.tlocal", "cte.40.tendeemi", "cte.40.tcte_dest", + "cte.40.tcte_rem", + "cte.40.exped", + "cte.40.receb", "cte.40.tresptec", "cte.40.tcte_autxml", "cte.40.tenderfer", ] _cte_search_keys = ["cte40_CNPJ", "cte40_CPF", "cte40_xNome"] - - cte40_choice_cnpj_cpf = fields.Selection( - selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], - string="CNPJ/CPF do Parceiro", + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_dutoviario_v4_00" ) - cte40_CNPJ = fields.Char( - compute="_compute_cte_data", - store=True, - ) + @api.model + def _prepare_import_dict( + self, values, model=None, parent_dict=None, defaults_model=None + ): + values = super()._prepare_import_dict( + values, model, parent_dict, defaults_model + ) + if not values.get("name") and values.get("legal_name"): + values["name"] = values["legal_name"] + return values + + # cte.40.tlocal / cte.40.enderEmit / 'cte.40.enderDest + # TODO: may be not store=True -> then override match cte40_cInt = fields.Char( string="Código interno da Ferrovia envolvida", help="Código interno da Ferrovia envolvida\nUso da transportadora", ) + cte40_CNPJ = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_CNPJ", + store=True, + compute_sudo=True, + ) cte40_CPF = fields.Char( compute="_compute_cte_data", + inverse="_inverse_cte40_CPF", store=True, + compute_sudo=True, + ) + cte40_xLgr = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_nro = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_xCpl = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_xBairro = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_cMun = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_xMun = fields.Char( + readonly=True, + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + # Char overriding Selection: + cte40_UF = fields.Char( + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, ) - # Same problem with Tendereco that NFE has, it has to use m2o fields + # Same problem with Tendereco that cte has, it has to use m2o fields cte40_enderToma = fields.Many2one( - comodel_name="res.partner", compute="_compute_cte40_ender" + comodel_name="res.partner", + compute="_compute_cte40_enderToma", + compute_sudo=True, ) cte40_enderReme = fields.Many2one( - comodel_name="res.partner", compute="_compute_cte40_ender" + comodel_name="res.partner", + compute="_compute_cte40_enderReme", + compute_sudo=True, ) cte40_enderDest = fields.Many2one( - comodel_name="res.partner", compute="_compute_cte40_ender" + comodel_name="res.partner", + compute="_compute_cte40_enderDest", + compute_sudo=True, ) cte40_enderExped = fields.Many2one( - comodel_name="res.partner", compute="_compute_cte40_ender" + comodel_name="res.partner", + compute="_compute_cte40_enderExped", + compute_sudo=True, ) cte40_enderReceb = fields.Many2one( - comodel_name="res.partner", compute="_compute_cte40_ender" + comodel_name="res.partner", + compute="_compute_cte40_enderReceb", + compute_sudo=True, ) cte40_enderFerro = fields.Many2one( - comodel_name="res.partner", compute="_compute_cte40_ender" + comodel_name="res.partner", + compute="_compute_cte40_enderFerro", + compute_sudo=True, ) - # enderToma/enderEmit/enderReme - cte40_xLgr = fields.Char(related="street_name", readonly=True) - cte40_nro = fields.Char(related="street_number", readonly=True) - cte40_xCpl = fields.Char(related="street2", readonly=True) - cte40_xBairro = fields.Char(related="district", readonly=True) - cte40_cMun = fields.Char(related="city_id.ibge_code", readonly=True) - cte40_xMun = fields.Char(related="city_id.name", readonly=True) - cte40_UF = fields.Char(related="state_id.code") - cte40_CEP = fields.Char( - compute="_compute_cep", - inverse="_inverse_cte40_CEP", + # Emit + cte40_choice_emit = fields.Selection( + selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ/CPF do Emitente", + compute="_compute_cte_data", compute_sudo=True, ) + + # cte.40.tendereco + cte40_CEP = fields.Char( + compute="_compute_cte_data", inverse="_inverse_cte40_CEP", compute_sudo=True + ) cte40_cPais = fields.Char( - related="country_id.bc_code", + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, ) cte40_xPais = fields.Char( - related="country_id.name", + compute="_compute_cte40_ender", + inverse="_inverse_cte40_ender", + compute_sudo=True, + ) + cte40_fone = fields.Char( + compute="_compute_cte_data", inverse="_inverse_cte40_fone", compute_sudo=True ) - cte40_IE = fields.Char(compute="_compute_cte40_IE") - + # cte.40.dest cte40_xNome = fields.Char(related="legal_name") + cte40_xFant = fields.Char(related="name", string="Nome Fantasia") + cte40_IE = fields.Char( + compute="_compute_cte_data", + inverse="_inverse_cte40_IE", + compute_sudo=True, + ) + cte40_ISUF = fields.Char(related="suframa") + cte40_email = fields.Char(related="email") + cte40_xEnder = fields.Char( + compute="_compute_cte40_xEnder", + compute_sudo=True, + ) + # cte.40.infresptec cte40_xContato = fields.Char(related="legal_name") - cte40_email = fields.Char(related="email") + cte40_choice_tlocal = fields.Selection( + selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ/CPF do Parceiro", + compute="_compute_cte_data", + compute_sudo=True, + ) - cte40_fone = fields.Char( - compute="_compute_cte_data", inverse="_inverse_cte40_fone", compute_sudo=True + cte40_choice_toma = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", ) - def _compute_cte40_IE(self): - for rec in self: - rec.cte40_IE = str(rec.inscr_est).replace(".", "") + cte40_choice_dest = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) - def _compute_cte40_ender(self): + cte40_choice_rem = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + cte40_choice_dest = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + cte40_choice_receb = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + cte40_choice_exped = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ("cte40_idEstrangeiro", "idEstrangeiro"), + ], + compute="_compute_cte_data", + compute_sudo=True, + string="CNPJ/CPF/idEstrangeiro", + ) + + # cte.40.autXML + cte40_choice_autxml = fields.Selection( + selection=[("cte40_CNPJ", "CNPJ"), ("cte40_CPF", "CPF")], + string="CNPJ/CPF do Parceiro Autorizado", + compute="_compute_cte_data", + compute_sudo=True, + ) + + # cte.40.transporta + cte40_choice_transporta = fields.Selection( + selection=[ + ("cte40_CNPJ", "CNPJ"), + ("cte40_CPF", "CPF"), + ], + string="CNPJ or CPF", + compute="_compute_cte_data", + compute_sudo=True, + ) + + def _compute_cte40_xEnder(self): + for rec in self: + # Campos do endereço são separados no Emitente e Destinatario + # porém no caso da Transportadadora o campo do endereço é maior + # porém sem os detalhes como complemento e bairro, mas + # operacionalmente são importantes, por isso caso existam o + # Complemento e o Bairro é melhor agrega-los. + # campo street retorna "street_name, street_number" + endereco = rec.street + if rec.street2: + endereco += " - " + rec.street2 + if rec.district: + endereco += " - " + rec.district + + rec.cte40_xEnder = endereco + + def _compute_cte40_enderToma(self): for rec in self: rec.cte40_enderToma = rec.id - rec.cte40_enderReme = rec.id + + def _compute_cte40_enderDest(self): + for rec in self: rec.cte40_enderDest = rec.id - rec.cte40_enderExped = rec.id + + def _compute_cte40_enderReme(self): + for rec in self: + rec.cte40_enderReme = rec.id + + def _compute_cte40_enderReceb(self): + for rec in self: rec.cte40_enderReceb = rec.id + + def _compute_cte40_enderExped(self): + for rec in self: + rec.cte40_enderExped = rec.id + + def _compute_cte40_enderFerro(self): + for rec in self: rec.cte40_enderFerro = rec.id @api.depends("company_type", "inscr_est", "cnpj_cpf", "country_id") def _compute_cte_data(self): + """Set schema data which are not just related fields""" for rec in self: cnpj_cpf = punctuation_rm(rec.cnpj_cpf) if cnpj_cpf: - if rec.is_company: + if rec.country_id.code != "BR": + rec.cte40_choice_toma = "cte40_idEstrangeiro" + rec.cte40_choice_dest = "cte40_idEstrangeiro" + rec.cte40_choice_rem = "cte40_idEstrangeiro" + rec.cte40_choice_receb = "cte40_idEstrangeiro" + rec.cte40_choice_exped = "cte40_idEstrangeiro" + rec.cte40_choice_tlocal = False + elif rec.is_company: + rec.cte40_choice_tlocal = "cte40_CNPJ" + rec.cte40_choice_toma = "cte40_CNPJ" + rec.cte40_choice_emit = "cte40_CNPJ" + rec.cte40_choice_dest = "cte40_CNPJ" + rec.cte40_choice_rem = "cte40_CNPJ" + rec.cte40_choice_receb = "cte40_CNPJ" + rec.cte40_choice_exped = "cte40_CNPJ" + rec.cte40_choice_autxml = "cte40_CNPJ" + rec.cte40_choice_transporta = "cte40_CNPJ" rec.cte40_CNPJ = cnpj_cpf rec.cte40_CPF = None else: - rec.cte40_CNPJ = None + rec.cte40_choice_tlocal = "cte40_CPF" + rec.cte40_choice_toma = "cte40_CPF" + rec.cte40_choice_emit = "cte40_CPF" + rec.cte40_choice_dest = "cte40_CPF" + rec.cte40_choice_rem = "cte40_CPF" + rec.cte40_choice_receb = "cte40_CPF" + rec.cte40_choice_exped = "cte40_CPF" + rec.cte40_choice_autxml = "cte40_CPF" + rec.cte40_choice_transporta = "cte40_CPF" rec.cte40_CPF = cnpj_cpf + rec.cte40_CNPJ = None + else: + rec.cte40_choice_tlocal = False + rec.cte40_choice_toma = False + rec.cte40_choice_emit = False + rec.cte40_choice_dest = False + rec.cte40_choice_rem = False + rec.cte40_choice_receb = False + rec.cte40_choice_exped = False + rec.cte40_choice_autxml = False + rec.cte40_choice_transporta = False + rec.cte40_CNPJ = "" + rec.cte40_CPF = "" + + if rec.inscr_est: + rec.cte40_IE = punctuation_rm(rec.inscr_est) + else: + rec.cte40_IE = None + + rec.cte40_CEP = punctuation_rm(rec.zip) rec.cte40_fone = punctuation_rm(rec.phone or "").replace(" ", "") + def _inverse_cte40_CNPJ(self): + for rec in self: + if rec.cte40_CNPJ: + rec.is_company = True + rec.cte40_choice_tlocal = "cte40_CPF" + rec.cte40_choice_emit = "cte40_CPF" + if rec.country_id.code != "BR": + rec.cte40_choice_toma = "cte40_idEstrangeiro" + rec.cte40_choice_dest = "cte40_idEstrangeiro" + rec.cte40_choice_rem = "cte40_idEstrangeiro" + rec.cte40_choice_receb = "cte40_idEstrangeiro" + rec.cte40_choice_exped = "cte40_idEstrangeiro" + else: + rec.cte40_choice_toma = "cte40_CNPJ" + rec.cte40_choice_dest = "cte40_CNPJ" + rec.cte40_choice_rem = "cte40_CNPJ" + rec.cte40_choice_receb = "cte40_CNPJ" + rec.cte40_choice_exped = "cte40_CNPJ" + rec.cte40_choice_toma = "cte40_CPF" + rec.cte40_choice_dest = "cte40_CPF" + rec.cte40_choice_rem = "cte40_CPF" + rec.cte40_choice_receb = "cte40_CPF" + rec.cte40_choice_exped = "cte40_CPF" + rec.cte40_choice_autxml = "cte40_CPF" + rec.cte40_choice_transporta = "cte40_CPF" + rec.cnpj_cpf = cnpj_cpf.formata(str(rec.cte40_CNPJ)) + + def _inverse_cte40_CPF(self): + for rec in self: + if rec.cte40_CPF: + rec.is_company = False + rec.cte40_choice_tlocal = "cte40_CNPJ" + rec.cte40_choice_emit = "cte40_CNPJ" + if rec.country_id.code != "BR": + rec.cte40_choice_toma = "cte40_idEstrangeiro" + rec.cte40_choice_dest = "cte40_idEstrangeiro" + rec.cte40_choice_rem = "cte40_idEstrangeiro" + rec.cte40_choice_receb = "cte40_idEstrangeiro" + rec.cte40_choice_exped = "cte40_idEstrangeiro" + else: + rec.cte40_choice_toma = "cte40_CPF" + rec.cte40_choice_dest = "cte40_CPF" + rec.cte40_choice_rem = "cte40_CPF" + rec.cte40_choice_receb = "cte40_CPF" + rec.cte40_choice_exped = "cte40_CPF" + rec.cte40_choice_autxml = "cte40_CNPJ" + rec.cte40_choice_transporta = "cte40_CNPJ" + rec.cnpj_cpf = cnpj_cpf.formata(str(rec.cte40_CPF)) + + def _inverse_cte40_IE(self): + for rec in self: + if rec.cte40_IE: + rec.inscr_est = str(rec.cte40_IE) + def _inverse_cte40_CEP(self): for rec in self: if rec.cte40_CEP: @@ -143,11 +448,38 @@ def _inverse_cte40_fone(self): if rec.cte40_fone: rec.phone = rec.cte40_fone - def _compute_cep(self): - for rec in self: - rec.cte40_CEP = punctuation_rm(rec.zip) + @api.model + def match_or_create_m2o(self, rec_dict, parent_dict, model=None): + if model is not None and model != self: + return False + + if parent_dict.get("cte40_CNPJ", False): + rec_dict["cnpj_cpf"] = parent_dict["cte40_CNPJ"] + + if rec_dict.get("cte40_CNPJ", False): + rec_dict["cnpj_cpf"] = rec_dict["cte40_CNPJ"] + + if rec_dict.get("cnpj_cpf", False): + domain_cnpj = [ + "|", + ("cnpj_cpf", "=", rec_dict["cnpj_cpf"]), + ("cnpj_cpf", "=", cnpj_cpf.formata(rec_dict["cnpj_cpf"])), + ] + match = self.search(domain_cnpj, limit=1) + if match: + return match.id + + vals = self._prepare_import_dict( + rec_dict, model=model, parent_dict=parent_dict, defaults_model=model + ) + if self._context.get("dry_run", False): + rec_id = self.new(vals).id + else: + rec_id = self.with_context(parent_dict=parent_dict).create(vals).id + return rec_id def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): + # Se a NF-e é emitida em homologação altera o nome do destinatário if ( xsd_field == "cte40_xNome" and class_obj._name @@ -156,13 +488,17 @@ def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): ): return "CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL" - if not self.cnpj_cpf and self.parent_id: - cnpj_cpf = punctuation_rm(self.parent_id.cnpj_cpf) - else: - cnpj_cpf = punctuation_rm(self.cnpj_cpf) + if xsd_field in ("cte40_CNPJ", "cte40_CPF"): + # Caso o CNPJ/CPF esteja em branco e o parceiro tenha um parent_id + # É exportado o CNPJ/CPF do parent_id é importate para o endereço + # de entrega/retirada + if not self.cnpj_cpf and self.parent_id: + cnpj_cpf = punctuation_rm(self.parent_id.cnpj_cpf) + else: + cnpj_cpf = punctuation_rm(self.cnpj_cpf) - if xsd_field == self.cte40_choice_cnpj_cpf: - return cnpj_cpf + if xsd_field == self.cte40_choice_tlocal: + return cnpj_cpf if self.country_id.code != "BR": if xsd_field == "cte40_xBairro": @@ -176,4 +512,59 @@ def _export_field(self, xsd_field, class_obj, member_spec, export_value=None): if xsd_field == "cte40_UF": return "EX" + + if xsd_field == "cte40_idEstrangeiro": + return self.vat or self.cnpj_cpf or self.rg or "EXTERIOR" + return super()._export_field(xsd_field, class_obj, member_spec, export_value) + + ########################## + # NF-e tag: enderXXX + # Compute Methods + ########################## + + @api.depends( + "street_name", + "street_number", + "street2", + "district", + "city_id", + "state_id", + "country_id", + ) + def _compute_cte40_ender(self): + for rec in self: + rec.cte40_xLgr = rec.street_name + rec.cte40_nro = rec.street_number + rec.cte40_xCpl = rec.street2 + rec.cte40_xBairro = rec.district + rec.cte40_cMun = rec.city_id.ibge_code + rec.cte40_xMun = rec.city_id.name + rec.cte40_UF = rec.state_id.code + rec.cte40_cPais = rec.country_id.bc_code + rec.cte40_xPais = rec.country_id.name + + def _inverse_cte40_ender(self): + for rec in self: + if rec.cte40_cMun and rec.cte40_UF: + city_id = self.env["res.city"].search( + [("ibge_code", "=", rec.cte40_cMun)] + ) + if rec.cte40_cPais: + country_id = self.env["res.country"].search( + [("bc_code", "=", rec.cte40_cPais)] + ) + else: + country_id = self.env["res.country"].search([("code", "=", "BR")]) + + state_id = self.env["res.country.state"].search( + [("code", "=", rec.cte40_UF), ("country_id", "=", country_id.id)] + ) + + rec.street_name = rec.cte40_xLgr + rec.street_number = rec.cte40_nro + rec.street2 = rec.cte40_xCpl + rec.district = rec.cte40_xBairro + rec.city_id = city_id + rec.country_id = country_id + rec.state_id = state_id diff --git a/l10n_br_cte/models/rodoviario.py b/l10n_br_cte/models/rodoviario.py index 11d63261a597..dc1b84886958 100644 --- a/l10n_br_cte/models/rodoviario.py +++ b/l10n_br_cte/models/rodoviario.py @@ -17,6 +17,7 @@ class Rodo(spec_models.StackedModel): _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_rodoviario_v4_00" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") @@ -34,6 +35,7 @@ class Occ(spec_models.StackedModel): _cte40_odoo_module = ( "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_modal_rodoviario_v4_00" ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_modal_rodoviario_v4_00" document_id = fields.Many2one(comodel_name="l10n_br_fiscal.document") diff --git a/l10n_br_cte/readme/CONFIGURE.rst b/l10n_br_cte/readme/CONFIGURE.rst index 754e51aeff53..63de5b32e262 100644 --- a/l10n_br_cte/readme/CONFIGURE.rst +++ b/l10n_br_cte/readme/CONFIGURE.rst @@ -1,10 +1 @@ -[ 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 +Para configurar este módulo, você precisa definir um certificado digital na empresa e também definir o processador edoc da empresa. diff --git a/l10n_br_cte/readme/CONTRIBUTORS.rst b/l10n_br_cte/readme/CONTRIBUTORS.rst index 957041454b69..451e440cc7a7 100644 --- a/l10n_br_cte/readme/CONTRIBUTORS.rst +++ b/l10n_br_cte/readme/CONTRIBUTORS.rst @@ -1 +1,13 @@ -* Ygor Carvalho + +* `KMEE `_: + + * Luis Felipe Mileo + * Ygor Carvalho + +* `ESCODOO `_: + + * Marcel Savegnago + +* `AKRETION `_: + + * Raphaël Valyi diff --git a/l10n_br_cte/readme/DESCRIPTION.rst b/l10n_br_cte/readme/DESCRIPTION.rst index 43cf54b776e0..f76ae240183e 100644 --- a/l10n_br_cte/readme/DESCRIPTION.rst +++ b/l10n_br_cte/readme/DESCRIPTION.rst @@ -1,4 +1,6 @@ -[ This file must be max 2-3 paragraphs, and is required. ] +Este módulo permite a emissão de CT-e (Conhecimento de Transporte). -This module extends the functionality of ... to support ... -and to allow you to ... +Mais especificamente ele: + * mapea os campos de CT-e do módulo ``l10n_br_cte_spec`` com os campos Odoo. + * usa a logica do módulo ``spec_driven_model`` para realizar esse mapeamento de forma dinâmica, em especial ele usa o sistema de modelos com várias camadas, ou ``StackedModel``, com os modelos ``l10n_br_fiscal.document`` e ``l10n_br_fiscal.document.related`` que tem varios niveis hierarquicos de elementos XML que estão sendo denormalizados dentro desses modelos Odoo  + * tem wizards para implementar a comunicação SOAP de CT-e com a SEFAZ (Autorização, Cancelamento, Encerramento...) diff --git a/l10n_br_cte/readme/ROADMAP.rst b/l10n_br_cte/readme/ROADMAP.rst new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_cte/readme/USAGE.rst b/l10n_br_cte/readme/USAGE.rst index f4629c3d548a..2700e93d02b0 100644 --- a/l10n_br_cte/readme/USAGE.rst +++ b/l10n_br_cte/readme/USAGE.rst @@ -1,11 +1,16 @@ -[ 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). ] +**Passo a Passo:** -To use this module, you need to: +1. **Criar uma Fatura:** + - Defina o tipo de documento como **57 (CTe - Conhecimento de Transporte)**. -#. Go to ... +2. **Configurar o Parceiro da Fatura:** + - Configure o parceiro responsável pelo pagamento do CTe e os parceiros como Rementente, Expedidor, Destinatário e Recebedor. + +3. **Adicionar uma Linha na Aba Produtos:** + - Adicione uma linha de fatura e selecione o produto Frete ou outro que esteja previamente configurado. + +4. **Acesse os detalhes fiscais da fatura e informe os demais dados necessário para emissão do CT-e:** + - Preencha os campos obrigatórios para emissão do CT-e. + +5. **Valide o CT-e, verifique os dados do XML e envie para a SEFAZ:** + - Após preencher todos os dados necessários, valide o CT-e e envie para a SEFAZ. diff --git a/l10n_br_cte/security/ir.model.access.csv b/l10n_br_cte/security/ir.model.access.csv index a7ed316bbe7a..1ee6536b59b4 100644 --- a/l10n_br_cte/security/ir.model.access.csv +++ b/l10n_br_cte/security/ir.model.access.csv @@ -12,6 +12,5 @@ l10n_br_cte_modal_aquaviario_balsa_user,l10n_br_cte_modal_aquav_balsa_user,model l10n_br_cte_modal_duto_user,l10n_br_cte_modal_duto_user,model_l10n_br_cte_modal_duto,base.group_user,1,1,1,1 -l10n_br_cte_normal_infos_user,l10n_br_cte_normal_infos_user,model_l10n_br_cte_normal_infos,base.group_user,1,1,1,1 l10n_br_cte_cargo_quantity_infos_user,l10n_br_cte_cargo_quantity_infos_user,model_l10n_br_cte_cargo_quantity_infos,base.group_user,1,1,1,1 l10n_br_cte_transported_vehicles_user,l10n_br_cte_transported_vehicles_user,model_l10n_br_cte_transported_vehicles,base.group_user,1,1,1,1 diff --git a/l10n_br_cte/static/description/index.html b/l10n_br_cte/static/description/index.html index e1d718ef2fd5..fbdac83c2104 100644 --- a/l10n_br_cte/static/description/index.html +++ b/l10n_br_cte/static/description/index.html @@ -370,9 +370,16 @@

CT-e

!! source digest: sha256:c91616235e33e68d0115aa3807f25142a45f5013a23492f240cf507a41d41340 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

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

-

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

-

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

+

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

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

Important

This is an alpha version, the data model and design can change at any time without warning. @@ -395,32 +402,22 @@

CT-e

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 -
+

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

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:

+

Passo a Passo:

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

Credits

Authors

  • KMEE
  • +
  • Escodoo

Contributors

Maintainers

@@ -454,6 +464,8 @@

Maintainers

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

+

Current maintainers:

+

mileo marcelsavegnago

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

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

diff --git a/l10n_br_cte/tests/__init__.py b/l10n_br_cte/tests/__init__.py index 8c797134a89c..70c0e034c995 100644 --- a/l10n_br_cte/tests/__init__.py +++ b/l10n_br_cte/tests/__init__.py @@ -1,6 +1,8 @@ -# from . import test_cte_serialize -# from . import test_cte_serialize_lc -# from . import test_cte_serialize_sn +from . import test_cte_serialize +from . import test_cte_serialize_lc +from . import test_cte_serialize_sn from . import test_cte_import -# from . import test_cte_structure -# from . import test_cte_res_partner +from . import test_cte_structure +from . import test_cte_res_partner +from . import test_cte_document +from . import test_res_partner diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040445899.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040445899.xml new file mode 100644 index 000000000000..826c0bba9cd5 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040445899.xml @@ -0,0 +1,196 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 574 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 9 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3501152 + Alumínio + SP + 01 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 59594315000157 + 755338250133 + TESTE - Simples Nacional + TESTE - Simples Nacional + + Rua Paulo Dias + 586 + Vila Santa Luzia + 3501152 + Alumínio + 18125000 + SP + 2130109965 + + 1 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 90 + 1 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + + 12345678 + + 01 + 01 + + XYZ + + + + 02 + 02 + + ABC + + + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040645898.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040645898.xml new file mode 100644 index 000000000000..f3655a190d86 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040645898.xml @@ -0,0 +1,198 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 573 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 8 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 01 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + + 12345678 + + 01 + 01 + + XYZ + + + + 02 + 02 + + ABC + + + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/43120178408960000182570010000000041000000047-cte.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe43120178408960000182570010000000041000000047.xml similarity index 97% rename from l10n_br_cte/tests/cte/v4_00/leiauteCTe/43120178408960000182570010000000041000000047-cte.xml rename to l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe43120178408960000182570010000000041000000047.xml index b42362d85ea6..4e9f11593b81 100644 --- a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/43120178408960000182570010000000041000000047-cte.xml +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe43120178408960000182570010000000041000000047.xml @@ -2,13 +2,13 @@ - 43 - 00000004 + 3 + 00000572 6353 SERV. TRANSPORTE 57 1 - 4 + 572 2012-01-06T17:25:56-02:00 1 1 @@ -21,7 +21,7 @@ PORTO UNIAO SC 01 - + 0 4213609 PORTO UNIAO SC diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/51160624686092000173570010000000031000000020-cte.XML b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160724686092000173570010000000031000000024.xml similarity index 92% rename from l10n_br_cte/tests/cte/v4_00/leiauteCTe/51160624686092000173570010000000031000000020-cte.XML rename to l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160724686092000173570010000000031000000024.xml index 24a88299e1a8..efaf7c991dba 100644 --- a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/51160624686092000173570010000000031000000020-cte.XML +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160724686092000173570010000000031000000024.xml @@ -1,15 +1,15 @@ - + - + 51 - 00000002 + 570005757 5353 PREST. DE SERV. TRANSPORTE A ESTAB. COMERCIAL.. 57 1 - 3 - 2016-06-07T17:58:40-02:00 + 571 + 2016-07-07T17:58:40-02:00 1 1 0 @@ -21,7 +21,7 @@ VARZEA GRANDE MT 01 - 9 + 0 5101407 ARIPUANA MT @@ -116,7 +116,7 @@ 99 NOTA FISCAL MANUAL 123456 - 2016-06-05 + 2016-07-05 79400.00 diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160824686092000173570010000000031000000024.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160824686092000173570010000000031000000024.xml new file mode 100644 index 000000000000..b7af3a4f2055 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe51160824686092000173570010000000031000000024.xml @@ -0,0 +1,130 @@ + + + + + 51 + 570005757 + 5353 + PREST. DE SERV. TRANSPORTE A ESTAB. COMERCIAL.. + 57 + 1 + 575 + 2016-07-07T17:58:40-02:00 + 1 + 1 + 0 + 2 + 0 + 0 + 2.0.1 + 5108402 + VARZEA GRANDE + MT + 01 + 0 + 5101407 + ARIPUANA + MT + 5108402 + VARZEA GRANDE + MT + 1 + 9 + + 0 + + + + MASTER + + NOTA FISCAL DE PRODUTOR RURAL N. 253-254-255 + + + 24686092000173 + 136268870 + P J TOSTA TRANSPORTES ME + + RUA RENATO JOSE DOS SANTOS + 10 + COHAB PRIMAVERA + 5108402 + VARZEA GRANDE + 78132712 + MT + 6599893021 + + + + 72304553915 + ISENTO + ROGERIO MARCIO TOLARDO + 6635651335 + + GLEBA GUARIBA VI LOTE RURAL 38 + SN + ZONA RURAL + 5101407 + ARIPUANA + 78325000 + MT + 1058 + BRASIL + + + + 03851469000122 + 131952927 + FRICAL FRIGORIFICO LTDA EPP + 6536342236 + + ESTRADA SOUZA LIMA + SN + SOUZA LIMA + 5108402 + VARZEA GRANDE + 78110000 + MT + 1058 + BRASIL + + + + 4000.00 + 4000.00 + + + + + 90 + 1 + + + + + + 79400.00 + GADO + GADO + + 01 + KILO + 50.0000 + + + + + 99 + NOTA FISCAL MANUAL + 123456 + 2016-07-05 + 79400.00 + + + + + 05277204 + + + + + diff --git a/l10n_br_cte/tests/test_cte_document.py b/l10n_br_cte/tests/test_cte_document.py index 7d23614fb464..5af40c9a37b7 100644 --- a/l10n_br_cte/tests/test_cte_document.py +++ b/l10n_br_cte/tests/test_cte_document.py @@ -1,11 +1,8 @@ -# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from datetime import datetime -from nfelib.nfe.ws.edoc_legacy import CTeAdapter - -from odoo.exceptions import UserError from odoo.tests import SavepointCase @@ -19,65 +16,63 @@ def setUpClass(cls): cls.acre_state = cls.env.ref("base.state_br_ac") cls.cte_document_type_id = cls.env.ref("l10n_br_fiscal.document_57") cls.sn_company_id = cls.env.ref("l10n_br_base.empresa_simples_nacional") - cls.sn_company_id.processador_edoc = "erpbrasil.edoc" + cls.sn_company_id.processador_edoc = "oca" cls.cte_id = FiscalDocument.create( { "document_type_id": cls.cte_document_type_id.id, "company_id": cls.sn_company_id.id, "document_number": "70000", "document_serie": "30", - "document_data": datetime.now(), + "document_date": datetime.now(), } ) - def test_cte_compute_fields(self): - self.cte_id.fiscal_additional_data = "TEST FISCAL ADDITIONAL DATA" - self.cte_id.customer_additional_data = "TEST CUSTOMER ADDITIONAL DATA" - - self.assertTrue(self.cte_id.cte40_infAdFisco) - self.assertTrue(self.cte_id.cte40_infCpl) - - def test_cte_inverse_fields(self): - self.cte_id.cte40_UFIni = self.acre_state.code - self.cte_id.cte40_UFFim = self.acre_state.code - self.assertEqual(self.cte_id.cte_initial_state_id, self.acre_state) - self.assertEqual(self.cte_id.cte_final_state_id, self.acre_state) - - self.cte_id.cte40_UF = self.acre_state.ibge_code - self.assertEqual(self.cte_id.company_id.partner_id.state_id, self.acre_state) - - self.cte_id.cte40_infMunCarrega = [ - ( - 0, - 0, - { - "cte40_cMunCarrega": "1200013", - "cte40_xMunCarrega": "Acrelândia", - }, - ) - ] - self.assertIn( - self.env.ref("l10n_br_base.city_1200013"), - self.cte_id.cte_loading_city_ids, - ) - - def test_cte_processor(self): - processor = self.cte_id._edoc_processor() - self.assertTrue(isinstance(processor, CTeAdapter)) - - self.cte_id.document_type_id = False - processor = self.cte_id._edoc_processor() - self.assertFalse(isinstance(processor, CTeAdapter)) - - self.cte_id.document_type_id = self.cte_document_type_id - - self.cte_id.company_id.certificate_nfe_id = False - processor = self.cte_id._edoc_processor() - self.assertTrue(isinstance(processor, CTeAdapter)) - - self.cte_id.company_id.certificate_ecnpj_id = False - with self.assertRaises(UserError): - processor = self.cte_id._edoc_processor() + # TODO: Tratar + # def test_cte_compute_fields(self): + # self.cte_id.fiscal_additional_data = "TEST FISCAL ADDITIONAL DATA" + # self.cte_id.customer_additional_data = "TEST CUSTOMER ADDITIONAL DATA" + + # self.assertTrue(self.cte_id.cte40_infAdFisco) + # self.assertTrue(self.cte_id.cte40_infCpl) + + # TODO: Tratar + # def test_cte_inverse_fields(self): + # self.cte_id.cte40_UFIni = self.acre_state.code + # self.cte_id.cte40_UFFim = self.acre_state.code + # self.assertEqual(self.cte_id.cte_initial_state_id, self.acre_state) + # self.assertEqual(self.cte_id.cte_final_state_id, self.acre_state) + + # self.cte_id.cte40_UF = self.acre_state.ibge_code + # self.assertEqual(self.cte_id.company_id.partner_id.state_id, self.acre_state) + + # self.cte_id.cte40_infMunCarrega = [ + # ( + # 0, + # 0, + # { + # "cte40_cMunCarrega": "1200013", + # "cte40_xMunCarrega": "Acrelândia", + # }, + # ) + # ] + # self.assertIn( + # self.env.ref("l10n_br_base.city_1200013"), + # self.cte_id.cte_loading_city_ids, + # ) + + # def test_cte_processor(self): + # processor = self.cte_id._edoc_processor() + # self.assertTrue(isinstance(processor, CTeAdapter)) + + # self.cte_id.document_type_id = False + # processor = self.cte_id._edoc_processor() + # self.assertFalse(isinstance(processor, CTeAdapter)) + + # self.cte_id.document_type_id = self.cte_document_type_id + + # self.cte_id.company_id.certificate_nfe_id = False + # with self.assertRaises(UserError): + # processor = self.cte_id._edoc_processor() def test_generate_key(self): self.cte_id._generate_key() diff --git a/l10n_br_cte/tests/test_cte_import.py b/l10n_br_cte/tests/test_cte_import.py index 92c3d53d7969..9d2613ac5aea 100644 --- a/l10n_br_cte/tests/test_cte_import.py +++ b/l10n_br_cte/tests/test_cte_import.py @@ -1,30 +1,31 @@ -# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging -import nfelib import pkg_resources from nfelib.cte.bindings.v4_0.cte_v4_00 import Tcte from odoo.models import NewId from odoo.tests import SavepointCase +from odoo.addons import l10n_br_cte + _logger = logging.getLogger(__name__) class CTeImportTest(SavepointCase): def test_import_in_cte_dry_run(self): res_items = ( + "tests", "cte", - "samples", - "v4_0", - "51160624686092000173570010000000031000000020-cte.XML", + "v4_00", + "leiauteCTe", + "CTe51160824686092000173570010000000031000000024.xml", ) resource_path = "/".join(res_items) - cte_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) + cte_stream = pkg_resources.resource_stream(l10n_br_cte.__name__, resource_path) binding = Tcte.from_xml(cte_stream.read().decode()) - cte = ( self.env["cte.40.tcte_infcte"] .with_context(tracking_disable=True, edoc_type="in") @@ -35,13 +36,15 @@ def test_import_in_cte_dry_run(self): def test_import_in_cte(self): res_items = ( + "tests", "cte", - "samples", - "v4_0", - "51160624686092000173570010000000031000000020-cte.XML", + "v4_00", + "leiauteCTe", + "CTe51160724686092000173570010000000031000000024.xml", ) + resource_path = "/".join(res_items) - cte_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) + cte_stream = pkg_resources.resource_stream(l10n_br_cte.__name__, resource_path) binding = Tcte.from_xml(cte_stream.read().decode()) cte = ( self.env["cte.40.tcte_infcte"] @@ -55,19 +58,10 @@ def test_import_in_cte(self): def _check_cte(self, cte): self.assertEqual(type(cte)._name, "l10n_br_fiscal.document") - # ide - self.assertEqual(cte.cte40_nCT, "3") - # self.assertEqual(cte.cte40_infMunCarrega[0].cte40_xMunCarrega, "IVINHEMA") self.assertEqual(cte.cte40_UFIni, "MT") self.assertEqual(cte.cte40_UFFim, "MT") - # # modal - # self.assertEqual(cte.cte40_placa, "XXX1228") - # self.assertEqual(cte.cte40_tara, "0") - # self.assertEqual(cte.cte40_condutor[0].cte40_xNome, "TESTE") - # self.assertEqual(len(cte.cte40_veicReboque), 0) - - self.assertEqual(cte.cte40_verProc, "104") + self.assertEqual(cte.cte40_verProc, "2.0.1") def test_import_out_cte(self): "(can be useful after an ERP migration)" diff --git a/l10n_br_cte/tests/test_cte_res_partner.py b/l10n_br_cte/tests/test_cte_res_partner.py index 204e1c04775f..cda6d4cf43b0 100644 --- a/l10n_br_cte/tests/test_cte_res_partner.py +++ b/l10n_br_cte/tests/test_cte_res_partner.py @@ -1,4 +1,4 @@ -# Copyright 2023 KMEE +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from erpbrasil.base.fiscal import cnpj_cpf @@ -16,14 +16,12 @@ def setUp(self): def test_compute_fields(self): self.partner_id.country_id = self.env.ref("base.us") - self.assertEqual( - self.partner_id.cte40_choice_tcontractor, "cte40_idEstrangeiro" - ) - self.assertEqual(self.partner_id.cte40_idEstrangeiro, self.partner_id.cnpj_cpf) + self.assertEqual(self.partner_id.cte40_choice_toma, "cte40_idEstrangeiro") + self.assertEqual(self.partner_id.cte40_cPais, self.env.ref("base.us").bc_code) def test_inverse_fields(self): - self.partner_id.cte40_idEstrangeiro = "999999999999" - self.assertEqual(self.partner_id.vat, self.partner_id.cte40_idEstrangeiro) + # self.partner_id.cte40_idEstrangeiro = "999999999999" + # self.assertEqual(self.partner_id.vat, self.partner_id.cte40_idEstrangeiro) self.partner_id.cte40_CNPJ = "97414612000162" self.assertEqual( diff --git a/l10n_br_cte/tests/test_cte_serialize.py b/l10n_br_cte/tests/test_cte_serialize.py index 76ba19856958..cd9bf0a36c72 100644 --- a/l10n_br_cte/tests/test_cte_serialize.py +++ b/l10n_br_cte/tests/test_cte_serialize.py @@ -1,5 +1,4 @@ -# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - -# Gabriel Cardoso de Faria +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging @@ -33,185 +32,114 @@ def prepare_test_cte(self, cte): if cte.state != "em_digitacao": # 2nd test run cte.action_document_back2draft() + cte.fiscal_line_ids.name = "Frete" + cte.fiscal_line_ids._onchange_fiscal_operation_line_id() + cte.fiscal_line_ids.cfop_id = cte.env.ref("l10n_br_fiscal.cfop_5352") cte._compute_amount() + cte.action_document_confirm() cte.document_date = datetime.strptime( "2020-01-01T11:00:00", "%Y-%m-%dT%H:%M:%S" ) - cte.cte40_cMDF = "20801844" + cte.cte40_cCT = "57000111" - if cte.cte_modal == "1": + if cte.cte_modal == "01": self.prepare_modal_rodoviario_data(cte) - elif cte.cte_modal == "2": + elif cte.cte_modal == "02": self.prepare_modal_aereo_data(cte) - elif cte.cte_modal == "3": + elif cte.cte_modal == "03": self.prepare_modal_aquaviario_data(cte) - elif cte.cte_modal == "4": + elif cte.cte_modal == "04": self.prepare_modal_ferroviario_data(cte) cte._document_export() def prepare_modal_rodoviario_data(self, cte): - cte.cte40_codAgPorto = "12345678" - - # infANTT cte.cte40_RNTRC = "12345678" - cte.cte40_categCombVeic = "02" - cte.cte40_infCIOT = [ - ( - 0, - 0, - { - "is_company": False, - "cte40_CIOT": "123456789101", - "cte40_CPF": "99999999999", - }, - ), - ] - cte.cte40_disp = [ - ( - 0, - 0, - { - "cte40_CNPJForn": "99999999999999", - "cte40_CNPJPg": "99999999999999", - "cte40_nCompra": "1234", - "cte40_vValePed": 5, - "cte40_tpValePed": "01", - }, - ), - ] - cte.cte40_infPag = [ - ( - 0, - 0, - { - "partner_id": self.env.ref("l10n_br_base.res_partner_intel").id, - "cte40_vContrato": 5, - "cte40_indPag": "0", - "payment_type": "pix", - "cte40_PIX": "99999999999999", - "cte40_comp": [ - ( - 0, - 0, - { - "cte40_tpComp": "01", - "cte40_vComp": 5, - }, - ) - ], - }, - ), - ] - - # veicTracao - cte.cte40_cInt = "1" - cte.cte40_RENAVAM = "42423325472" - cte.cte40_placa = "AAA1233" - cte.cte40_tpTransp = False - cte.cte40_tara = 7500 - cte.cte40_capKG = 42500 - cte.cte40_capM3 = 300 - cte.cte40_tpRod = "03" - cte.cte40_tpCar = "00" - cte.rodo_vehicle_state_id = self.env.ref("base.state_br_ac").id - cte.cte40_condutor = [ + cte.cte40_occ = [ ( 0, 0, { - "cte40_xNome": "Teste", - "cte40_CPF": "99999999999", + "cte40_serie": "01", + "cte40_nOcc": "01", + "cte40_cInt": "XYZ", }, ), ( 0, 0, { - "cte40_xNome": "Teste2", - "cte40_CPF": "99999999999", + "cte40_serie": "02", + "cte40_nOcc": "02", + "cte40_cInt": "ABC", }, ), ] - # veicReboque - cte.cte40_veicReboque = [ - ( - 0, - 0, - { - "cte40_cInt": "2", - "cte40_placa": "AAA4321", - "cte40_RENAVAM": "11557770179", - "cte40_tara": 7200, - "cte40_capKG": 42500, - "cte40_capM3": 300, - "cte40_tpCar": "00", - "cte40_UF": "AC", - }, - ) - ] - def prepare_modal_aereo_data(self, cte): - cte.cte40_nac = "TEST" - cte.cte40_matr = "TEST" - cte.cte40_nVoo = "123456789" - cte.cte40_cAerEmb = "OACI" - cte.cte40_cAerDes = "OACI" - cte.cte40_dVoo = datetime.strptime("2020-01-01", "%Y-%m-%d") + pass + # cte.cte40_nac = "TEST" + # cte.cte40_matr = "TEST" + # cte.cte40_nVoo = "123456789" + # cte.cte40_cAerEmb = "OACI" + # cte.cte40_cAerDes = "OACI" + # cte.cte40_dVoo = datetime.strptime("2020-01-01", "%Y-%m-%d") def prepare_modal_aquaviario_data(self, cte): - cte.cte40_irin = "1234567899" - cte.cte40_tpEmb = "01" - cte.cte40_cEmbar = "123456" - cte.cte40_xEmbar = "teste" - cte.cte40_nViag = "123456" - cte.cte40_cPrtEmb = "BRADR" - cte.cte40_cPrtDest = "BRAFU" - cte.cte40_infTermCarreg = [ - (0, 0, {"loading_harbor": "BRADR"}), - (0, 0, {"loading_harbor": "BRANT"}), - ] - cte.cte40_infTermDescarreg = [ - (0, 0, {"unloading_harbor": "BRAFU"}), - (0, 0, {"unloading_harbor": "BRBZC"}), - ] + pass + # cte.cte40_irin = "1234567899" + # cte.cte40_tpEmb = "01" + # cte.cte40_cEmbar = "123456" + # cte.cte40_xEmbar = "teste" + # cte.cte40_nViag = "123456" + # cte.cte40_cPrtEmb = "BRADR" + # cte.cte40_cPrtDest = "BRAFU" + # cte.cte40_infTermCarreg = [ + # (0, 0, {"loading_harbor": "BRADR"}), + # (0, 0, {"loading_harbor": "BRANT"}), + # ] + # cte.cte40_infTermDescarreg = [ + # (0, 0, {"unloading_harbor": "BRAFU"}), + # (0, 0, {"unloading_harbor": "BRBZC"}), + # ] def prepare_modal_ferroviario_data(self, cte): - cte.cte40_dhTrem = datetime.strptime("2020-01-01T11:00:00", "%Y-%m-%dT%H:%M:%S") - cte.cte40_xPref = "TES" - cte.cte40_xOri = "TES" - cte.cte40_xDest = "TES" - cte.cte40_qVag = 2 - cte.cte40_vag = [ - ( - 0, - 0, - { - "cte40_pesoBC": 500, - "cte40_pesoR": 1, - "cte40_tpVag": 123, - "cte40_serie": 123, - "cte40_nVag": 123, - "cte40_nSeq": 123, - "cte40_TU": 1, - }, - ), - ( - 0, - 0, - { - "cte40_pesoBC": 500, - "cte40_pesoR": 1, - "cte40_tpVag": 321, - "cte40_serie": 321, - "cte40_nVag": 321, - "cte40_nSeq": 321, - "cte40_TU": 1, - }, - ), - ] + pass + # cte.cte40_dhTrem = datetime.strptime( + # "2020-01-01T11:00:00", "%Y-%m-%dT%H:%M:%S") + # cte.cte40_xPref = "TES" + # cte.cte40_xOri = "TES" + # cte.cte40_xDest = "TES" + # cte.cte40_qVag = 2 + # cte.cte40_vag = [ + # ( + # 0, + # 0, + # { + # "cte40_pesoBC": 500, + # "cte40_pesoR": 1, + # "cte40_tpVag": 123, + # "cte40_serie": 123, + # "cte40_nVag": 123, + # "cte40_nSeq": 123, + # "cte40_TU": 1, + # }, + # ), + # ( + # 0, + # 0, + # { + # "cte40_pesoBC": 500, + # "cte40_pesoR": 1, + # "cte40_tpVag": 321, + # "cte40_serie": 321, + # "cte40_nVag": 321, + # "cte40_nSeq": 321, + # "cte40_TU": 1, + # }, + # ), + # ] def serialize_xml(self, cte_data): cte = cte_data["cte"] @@ -219,7 +147,7 @@ def serialize_xml(self, cte_data): l10n_br_cte.__path__[0], "tests", "cte", - "V4_00", + "v4_00", "leiauteCTe", cte_data["xml_file"], ) diff --git a/l10n_br_cte/tests/test_cte_serialize_lc.py b/l10n_br_cte/tests/test_cte_serialize_lc.py index cead0bf51335..1f083e25b910 100644 --- a/l10n_br_cte/tests/test_cte_serialize_lc.py +++ b/l10n_br_cte/tests/test_cte_serialize_lc.py @@ -1,4 +1,4 @@ -# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging @@ -10,15 +10,12 @@ class TestCTeExportLC(TestCTeSerialize): def setUp(self): cte_list = [ - { - "record_ref": "l10n_br_cte.demo_cte_lc_modal_ferroviario", - "xml_file": "CTe35230905472475000102580200000602011208018449.xml", - }, { "record_ref": "l10n_br_cte.demo_cte_lc_modal_rodoviario", - "xml_file": "CTe35230905472475000102580200000602071611554500.xml", + "xml_file": "CTe35240708318053000167570010000000311040645898.xml", }, ] + super().setUp(cte_list) def test_serialize_xml(self): diff --git a/l10n_br_cte/tests/test_cte_serialize_sn.py b/l10n_br_cte/tests/test_cte_serialize_sn.py index 5ed1aae0da1b..f067b8f800b1 100644 --- a/l10n_br_cte/tests/test_cte_serialize_sn.py +++ b/l10n_br_cte/tests/test_cte_serialize_sn.py @@ -1,4 +1,4 @@ -# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging @@ -11,12 +11,8 @@ class TestCTeExportSN(TestCTeSerialize): def setUp(self): cte_list = [ { - "record_ref": "l10n_br_cte.demo_cte_sn_modal_aereo", - "xml_file": "CTe35230905472475000102580200000602081550195716.xml", - }, - { - "record_ref": "l10n_br_cte.demo_cte_sn_modal_aquaviario", - "xml_file": "CTe35231005472475000102580200000602161434590525.xml", + "record_ref": "l10n_br_cte.demo_cte_sn_modal_rodoviario", + "xml_file": "CTe35240708318053000167570010000000311040445899.xml", }, ] diff --git a/l10n_br_cte/tests/test_cte_structure.py b/l10n_br_cte/tests/test_cte_structure.py index 213702f076f3..c08007807676 100644 --- a/l10n_br_cte/tests/test_cte_structure.py +++ b/l10n_br_cte/tests/test_cte_structure.py @@ -1,4 +1,4 @@ -# @ 2020 KMEE INFORMATICA LTDA - www.kmee.com.br - +# Copyright 2024 - TODAY, Marcel Savegnago # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from io import StringIO @@ -7,8 +7,6 @@ from odoo.addons.spec_driven_model.models.spec_models import SpecModel -from ..models.document import CTe - class CTeStructure(SavepointCase): @classmethod @@ -67,44 +65,43 @@ def test_concrete_spec(self): # this ensure basic SQL is set up self.assertEqual( len( - self.env["cte.40.infmuncarrega"].search( - [("cte40_cMunCarrega", "=", "NO_RECORD")] + self.env["cte.40.tcte_infnfe"].search( + [("cte40_chave", "=", "NO_RECORD")] ) ), 0, ) - def test_m2o_concrete_to_concrete_spec(self): - self.assertEqual( - self.env["cte.40.infcte"] - ._fields["cte40_infCTe_infMunDescarga_id"] - .comodel_name, - "cte.40.infmundescarga", - ) + # TODO: Nao achei um exemplo de m2o concreto para concreto + # def test_m2o_concrete_to_concrete_spec(self): + # self.assertEqual( + # self.env["cte.40.infnfe"] + # ._fields["cte40_infUnidCarga_infNFe_id"] + # .comodel_name, + # "cte.40.tunidcarga", + # ) def test_o2m_concrete_to_concrete_spec(self): self.assertEqual( - self.env["cte.40.ide"]._fields["cte40_infMunCarrega"].comodel_name, - "cte.40.infmuncarrega", + self.env["cte.40.tcte_infdoc"]._fields["cte40_infOutros"].comodel_name, + "cte.40.infoutros", ) def test_m2o_stacked_to_odoo(self): self.assertEqual( - self.env["l10n_br_fiscal.document"]._fields["cte40_prodPred"].comodel_name, - "product.product", + self.env["l10n_br_fiscal.document"]._fields["cte40_enderReme"].comodel_name, + "res.partner", ) def test_o2m_to_odoo(self): self.assertEqual( - self.env["l10n_br_fiscal.document"] - ._fields["cte40_infEmbComb"] - .comodel_name, - "l10n_br_cte.modal.aquaviario.comboio", + self.env["l10n_br_fiscal.document"]._fields["cte40_occ"].comodel_name, + "l10n_br_cte.modal.rodo.occ", ) self.assertEqual( len( - self.env["l10n_br_cte.modal.aquaviario.comboio"].search( - [("cte40_cEmbComb", "=", "NO_RECORD")] + self.env["l10n_br_cte.modal.rodo.occ"].search( + [("cte40_nOcc", "=", "NO_RECORD")] ) ), 0, @@ -117,8 +114,9 @@ def test_m2o_stacked_to_concrete(self): ._fields["cte40_infSolicNFF"] .comodel_name ) - self.assertEqual(model, "cte.40.infsolicnff") + self.assertEqual(model, "cte.40.tcte_infsolicnff") + # TODO: Tratar # def test_m2o_stacked(self): # # not stacked because optional # cte_model = self.env["l10n_br_fiscal.document"] @@ -128,30 +126,36 @@ def test_m2o_stacked_to_concrete(self): def test_doc_stacking_points(self): doc_keys = [ "cte40_ide", + "cte40_toma3", + "cte40_toma4", + # "cte40_enderToma", + "cte40_compl", + "cte40_fluxo", + "cte40_entrega", + "cte40_comData", + "cte40_semHora", + "cte40_vPrest", + "cte40_imp", + "cte40_ICMS", + "cte40_infCTeNorm", + "cte40_infCarga", "cte40_infModal", - "cte40_infDoc", - "cte40_tot", - "cte40_infAdic", - # "cte40_trem", - # "cte40_infANTT", - # "cte40_valePed", - # "cte40_veicTracao", - # "cte40_infBanc", ] keys = [ k for k in self.env["l10n_br_fiscal.document"] - .with_context(spec_schema="cte", spec_version="30") + .with_context(spec_schema="cte", spec_version="40") ._get_stacking_points() .keys() ] self.assertEqual(sorted(keys), sorted(doc_keys)) - def test_doc_tree(self): - base_class = self.env["l10n_br_fiscal.document"] - tree, visited = self.get_stacked_tree(base_class) - self.assertEqual(tree, CTe.INFCTE_TREE) - self.assertEqual(len(visited), 6) # all stacked classes + # TODO: Tratar + # def test_doc_tree(self): + # base_class = self.env["l10n_br_fiscal.document"] + # tree, visited = self.get_stacked_tree(base_class) + # self.assertEqual(tree, CTe.INFCTE_TREE) + # self.assertEqual(len(visited), 15) # all stacked classes def test_m2o_force_stack(self): pass diff --git a/l10n_br_cte/tests/test_res_partner.py b/l10n_br_cte/tests/test_res_partner.py new file mode 100644 index 000000000000..3566ec06f679 --- /dev/null +++ b/l10n_br_cte/tests/test_res_partner.py @@ -0,0 +1,71 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestResPartner(TransactionCase): + def setUp(self): + super(TestResPartner, self).setUp() + # Basic setup for the test + self.partner_model = self.env["res.partner"] + self.partner = self.partner_model.create( + { + "name": "Test Partner", + "cnpj_cpf": "87697453000105", + "zip": "12345000", + "phone": "(11) 91234-5678", + "email": "test@company.com", + "country_id": self.env.ref("base.br").id, + "is_company": True, + } + ) + + def test_compute_cte40_xEnder(self): + """Test the computation of the field cte40_xEnder""" + self.partner.write( + { + "street": "Test Street", + "street2": "Apt 101", + "district": "Downtown", + } + ) + self.partner._compute_cte40_xEnder() + self.assertEqual( + self.partner.cte40_xEnder, + "Test Street - Apt 101 - Downtown", + "The cte40_xEnder field was not computed correctly", + ) + + def test_compute_cte_data(self): + """Test the computation of fields related to CNPJ/CPF""" + self.partner._compute_cte_data() + self.assertEqual( + self.partner.cte40_CNPJ, "87697453000105", "CNPJ was not computed correctly" + ) + self.assertFalse(self.partner.cte40_CPF, "CPF should not be set for companies") + + def test_inverse_cte40_CNPJ(self): + """Test the inverse method for the CNPJ field""" + self.partner.cte40_CNPJ = "21524956000162" + self.partner._inverse_cte40_CNPJ() + self.assertEqual( + self.partner.cnpj_cpf, + "21.524.956/0001-62", + "CNPJ was not formatted correctly", + ) + + def test_inverse_cte40_CEP(self): + """Test the inverse method for the ZIP code""" + self.partner.cte40_CEP = "12345999" + self.partner._inverse_cte40_CEP() + self.assertEqual( + self.partner.zip, "12345-999", "ZIP code was not formatted correctly" + ) + + def test_match_or_create_m2o(self): + """Test the match_or_create_m2o method""" + parent_dict = {"cte40_CNPJ": "87697453000105"} + rec_dict = {} + result_id = self.partner_model.match_or_create_m2o(rec_dict, parent_dict) + self.assertTrue(result_id, "Could not create or find a Many2One record") diff --git a/l10n_br_cte/views/cte_action.xml b/l10n_br_cte/views/cte_action.xml new file mode 100644 index 000000000000..221453ddb923 --- /dev/null +++ b/l10n_br_cte/views/cte_action.xml @@ -0,0 +1,47 @@ + + + + + + CT-e + ir.actions.act_window + l10n_br_fiscal.document + tree,form + + + + +

+ Add a new CT-e +

+
+
+ +
diff --git a/l10n_br_cte/views/cte_menu.xml b/l10n_br_cte/views/cte_menu.xml new file mode 100644 index 000000000000..c52f8ff8920d --- /dev/null +++ b/l10n_br_cte/views/cte_menu.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/l10n_br_cte/views/cte_document.xml b/l10n_br_cte/views/document.xml similarity index 93% rename from l10n_br_cte/views/cte_document.xml rename to l10n_br_cte/views/document.xml index 1f092ef14c5e..930523a8b352 100644 --- a/l10n_br_cte/views/cte_document.xml +++ b/l10n_br_cte/views/document.xml @@ -1,13 +1,14 @@ - - l10n_br_cte.document.form.inherit + + >cte.document.form.view (in l10n_br_cte) l10n_br_fiscal.document - 10 + 5 - - - + + + - + diff --git a/l10n_br_cte/views/document_line.xml b/l10n_br_cte/views/document_line.xml deleted file mode 100644 index e2da706e8b22..000000000000 --- a/l10n_br_cte/views/document_line.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - document.line - - - - - - diff --git a/l10n_br_cte/views/document_related.xml b/l10n_br_cte/views/document_related.xml deleted file mode 100644 index 1a66fbe13e15..000000000000 --- a/l10n_br_cte/views/document_related.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - document.related.form (in l10n_br_cte) - document.related - - - - - - - - - diff --git a/l10n_br_cte/modal/modal_aereo.xml b/l10n_br_cte/views/modal/modal_aereo.xml similarity index 100% rename from l10n_br_cte/modal/modal_aereo.xml rename to l10n_br_cte/views/modal/modal_aereo.xml diff --git a/l10n_br_cte/modal/modal_aquaviario.xml b/l10n_br_cte/views/modal/modal_aquaviario.xml similarity index 100% rename from l10n_br_cte/modal/modal_aquaviario.xml rename to l10n_br_cte/views/modal/modal_aquaviario.xml diff --git a/l10n_br_cte/modal/modal_ferroviario.xml b/l10n_br_cte/views/modal/modal_ferroviario.xml similarity index 100% rename from l10n_br_cte/modal/modal_ferroviario.xml rename to l10n_br_cte/views/modal/modal_ferroviario.xml diff --git a/l10n_br_cte/modal/modal_rodoviario.xml b/l10n_br_cte/views/modal/modal_rodoviario.xml similarity index 100% rename from l10n_br_cte/modal/modal_rodoviario.xml rename to l10n_br_cte/views/modal/modal_rodoviario.xml diff --git a/l10n_br_cte/views/res_partner.xml b/l10n_br_cte/views/res_partner.xml deleted file mode 100644 index 9d738eaa174c..000000000000 --- a/l10n_br_cte/views/res_partner.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - res.partner.form (in l10n_br_cte) - res.partner - - - - - - - - - From ba7a927a1101e02fd390dcb4a916ee8cce0e3659 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Tue, 19 Nov 2024 17:41:03 -0300 Subject: [PATCH 15/27] [IMP] l10n_br_cte: add rntrc_code on company partner --- l10n_br_cte/demo/company_demo.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/l10n_br_cte/demo/company_demo.xml b/l10n_br_cte/demo/company_demo.xml index a3e331859b40..ea8dc04edd59 100644 --- a/l10n_br_cte/demo/company_demo.xml +++ b/l10n_br_cte/demo/company_demo.xml @@ -6,11 +6,19 @@ oca + + 07946021 + + oca + + 07946021 + + From 96f9917a6ffbc30887e768e3f0176ec3bb1fab1a Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Tue, 19 Nov 2024 17:42:10 -0300 Subject: [PATCH 16/27] [FIX] l10n_br_cte: remove _stacking_points from document_comment and document_line --- l10n_br_cte/models/document_comment.py | 2 -- l10n_br_cte/models/document_line.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/l10n_br_cte/models/document_comment.py b/l10n_br_cte/models/document_comment.py index 215ed802dec5..e02083676884 100644 --- a/l10n_br_cte/models/document_comment.py +++ b/l10n_br_cte/models/document_comment.py @@ -14,8 +14,6 @@ class CTeComment(spec_models.StackedModel): "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" ) _cte40_stacking_mixin = "cte.40.tcte_obscont" - - # _stacking_points = {} _cte40_stacking_skip_paths = ("cte40_ObsCont_compl_id", "cte40_ObsFisco_compl_id") cte40_xCampo = fields.Char() diff --git a/l10n_br_cte/models/document_line.py b/l10n_br_cte/models/document_line.py index dd6aa339cde1..babbba3c8811 100644 --- a/l10n_br_cte/models/document_line.py +++ b/l10n_br_cte/models/document_line.py @@ -15,8 +15,6 @@ class CTeLine(spec_models.StackedModel): "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" ) _cte40_stacking_mixin = "cte.40.tcte_vprest_comp" - - # _stacking_points = {} _cte40_stacking_skip_paths = ("cte40_Comp_vPrest_id",) ########################## From 68b8bd74ade8ced78ee52079be44e29331bbc43e Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Tue, 19 Nov 2024 17:57:42 -0300 Subject: [PATCH 17/27] [FIX] l10n_br_cte: fix demo data record name --- l10n_br_cte/demo/fiscal_document_demo.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/l10n_br_cte/demo/fiscal_document_demo.xml b/l10n_br_cte/demo/fiscal_document_demo.xml index 641667366c58..68cc152c9f0d 100644 --- a/l10n_br_cte/demo/fiscal_document_demo.xml +++ b/l10n_br_cte/demo/fiscal_document_demo.xml @@ -49,7 +49,7 @@ @@ -59,7 +59,7 @@ @@ -69,7 +69,7 @@ @@ -151,7 +151,7 @@ @@ -161,7 +161,7 @@ @@ -171,7 +171,7 @@ From 8cffed1f2a8bf9713df4a427bd6d8a8ff4d47ed1 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Wed, 20 Nov 2024 17:06:30 -0300 Subject: [PATCH 18/27] [FIX] l10n_br_cte: fix fiscal document form view --- l10n_br_cte/views/document.xml | 53 +++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/l10n_br_cte/views/document.xml b/l10n_br_cte/views/document.xml index 930523a8b352..cb0a578b2fdf 100644 --- a/l10n_br_cte/views/document.xml +++ b/l10n_br_cte/views/document.xml @@ -21,11 +21,26 @@ >[('document_type_id.code', 'in', ['57', '08', '09', '10', '11', '26', '67', '8B'])] - - - - - + + + + + @@ -35,8 +50,15 @@ - - + + @@ -59,9 +81,16 @@ - - + + @@ -84,7 +113,11 @@ - + From eea4592362859304aa268280b5bfa1d89a2401a7 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 21 Nov 2024 16:33:36 -0300 Subject: [PATCH 19/27] [TEST] l10n_br_cte: dacte print --- l10n_br_cte/models/document.py | 63 ++++++++++++++++------------------ 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/l10n_br_cte/models/document.py b/l10n_br_cte/models/document.py index 164c5e73a0e5..534fdb6067e9 100644 --- a/l10n_br_cte/models/document.py +++ b/l10n_br_cte/models/document.py @@ -10,6 +10,7 @@ from datetime import datetime from enum import Enum +from brazilfiscalreport.dacte import Dacte from erpbrasil.base.fiscal import cnpj_cpf # TODO: precisa tratar @@ -83,9 +84,6 @@ CTE_XML_NAMESPACE = {"cte": "http://www.portalfiscal.inf.br/cte"} -# TODO: https://github.com/Engenere/BrazilFiscalReport/pull/23 -# from brazilfiscalreport.dacte import Dacte - _logger = logging.getLogger(__name__) @@ -1755,33 +1753,32 @@ def _document_number(self): ) from e return result - # TODO: Tratar - # def make_pdf(self): - # if not self.filtered(filter_processador_edoc_cte): - # return super().make_pdf() - - # file_pdf = self.file_report_id - # self.file_report_id = False - # file_pdf.unlink() - - # if self.authorization_file_id: - # arquivo = self.authorization_file_id - # xml_string = base64.b64decode(arquivo.datas).decode() - # else: - # arquivo = self.send_file_id - # xml_string = base64.b64decode(arquivo.datas).decode() - # # TODO: implementar temp_xml_autorizacao igual nfe ? - # # xml_string = self.temp_xml_autorizacao(xml_string) - - # pdf = Dacte(xml=xml_string).output() - - # self.file_report_id = self.env["ir.attachment"].create( - # { - # "name": self.document_key + ".pdf", - # "res_model": self._name, - # "res_id": self.id, - # "datas": base64.b64encode(pdf), - # "mimetype": "application/pdf", - # "type": "binary", - # } - # ) + def make_pdf(self): + if not self.filtered(filter_processador_edoc_cte): + return super().make_pdf() + + file_pdf = self.file_report_id + self.file_report_id = False + file_pdf.unlink() + + if self.authorization_file_id: + arquivo = self.authorization_file_id + xml_string = base64.b64decode(arquivo.datas).decode() + else: + arquivo = self.send_file_id + xml_string = base64.b64decode(arquivo.datas).decode() + # TODO: implementar temp_xml_autorizacao igual nfe ? + # xml_string = self.temp_xml_autorizacao(xml_string) + + pdf = Dacte(xml=xml_string).output() + + self.file_report_id = self.env["ir.attachment"].create( + { + "name": self.document_key + ".pdf", + "res_model": self._name, + "res_id": self.id, + "datas": base64.b64encode(pdf), + "mimetype": "application/pdf", + "type": "binary", + } + ) From 207786b3f4ee74379f3db84acc0d685654a95925 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Fri, 22 Nov 2024 13:35:16 -0300 Subject: [PATCH 20/27] [RFC] l10n_bt_cte: tests refactor --- l10n_br_cte/demo/fiscal_document_demo.xml | 499 +++++++++++++++++- l10n_br_cte/models/document.py | 6 +- ...9594315000157570010000057311040445890.xml} | 12 +- ...1583054000129570010000057311040645894.xml} | 10 +- ...81583054000129570010000057411040645890.xml | 214 ++++++++ ...81583054000129570010000057511040645897.xml | 200 +++++++ ...81583054000129570010000057611040645893.xml | 189 +++++++ ...81583054000129570010000057711040645890.xml | 190 +++++++ l10n_br_cte/tests/test_cte_serialize.py | 153 ++++-- l10n_br_cte/tests/test_cte_serialize_lc.py | 18 +- l10n_br_cte/tests/test_cte_serialize_sn.py | 2 +- 11 files changed, 1421 insertions(+), 72 deletions(-) rename l10n_br_cte/tests/cte/v4_00/leiauteCTe/{CTe35240708318053000167570010000000311040445899.xml => CTe35240759594315000157570010000057311040445890.xml} (95%) rename l10n_br_cte/tests/cte/v4_00/leiauteCTe/{CTe35240708318053000167570010000000311040645898.xml => CTe35240781583054000129570010000057311040645894.xml} (95%) create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057411040645890.xml create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057511040645897.xml create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057611040645893.xml create mode 100644 l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057711040645890.xml diff --git a/l10n_br_cte/demo/fiscal_document_demo.xml b/l10n_br_cte/demo/fiscal_document_demo.xml index 68cc152c9f0d..6cc9849c0050 100644 --- a/l10n_br_cte/demo/fiscal_document_demo.xml +++ b/l10n_br_cte/demo/fiscal_document_demo.xml @@ -1,6 +1,8 @@ + + @@ -12,7 +14,7 @@ 573 1 - 35240708318053000167570010000000311040645898 + 35240781583054000129570010000057311040645894 2 oca @@ -47,7 +49,7 @@ - + 2 - + Frete @@ -91,6 +93,18 @@ + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + @@ -103,6 +117,464 @@ + + + + + + + 574 + 1 + 35240781583054000129570010000057411040645890 + 2 + oca + + 3 + 02 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + + + + + + 575 + 1 + 35240781583054000129570010000057511040645897 + 2 + oca + + 3 + 03 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + + + + + + 576 + 1 + 35240781583054000129570010000057611040645893 + 2 + oca + + 3 + 04 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + + + + + + 577 + 1 + 35240781583054000129570010000057711040645890 + 2 + oca + + 3 + 05 + + + + + + + + + + + + + + + out + + XYZ Product + Other Product Data + 1000 + 1000 + + + + + + + 00 + Volume + 1000.0 + + + + + 01 + Peso Bruto + 500.0 + + + + + 03 + Unidade + 2 + + + + + + Frete + + + 100 + 1 + out + + + + + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + + + + + + + + + + + + @@ -112,9 +584,9 @@ ref="l10n_br_fiscal.empresa_sn_document_57_serie_1" /> - 574 + 573 1 - 35240708318053000167570010000000311040445899 + 35240759594315000157570010000057311040445890 2 oca @@ -149,7 +621,7 @@ - + 2 - + Frete @@ -193,6 +665,18 @@ + + + + + 41190806117473000150550010000586251016759484 + 10 + 33.19 + + @@ -205,4 +689,5 @@ + diff --git a/l10n_br_cte/models/document.py b/l10n_br_cte/models/document.py index 534fdb6067e9..b8dad3525015 100644 --- a/l10n_br_cte/models/document.py +++ b/l10n_br_cte/models/document.py @@ -77,9 +77,7 @@ CTE_TYPE_DEFAULT, ) from ..constants.modal import ( - CTE_MODAL_DEFAULT, CTE_MODAL_VERSION_DEFAULT, - CTE_MODALS, ) CTE_XML_NAMESPACE = {"cte": "http://www.portalfiscal.inf.br/cte"} @@ -904,9 +902,7 @@ def _default_cte40_autxml(self): cte40_modal = fields.Selection(related="transport_modal") - cte_modal = fields.Selection( - selection=CTE_MODALS, string="Transport Modal", default=CTE_MODAL_DEFAULT - ) + cte_modal = fields.Selection(related="transport_modal") cte40_versaoModal = fields.Char(default=CTE_MODAL_VERSION_DEFAULT) diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040445899.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240759594315000157570010000057311040445890.xml similarity index 95% rename from l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040445899.xml rename to l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240759594315000157570010000057311040445890.xml index 826c0bba9cd5..cc05dae58e7b 100644 --- a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040445899.xml +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240759594315000157570010000057311040445890.xml @@ -1,6 +1,6 @@ - + 35 57000111 @@ -8,11 +8,11 @@ Venda 57 1 - 574 + 573 2020-01-01T12:00:00+01:00 1 1 - 9 + 0 2 0 0 @@ -171,7 +171,11 @@ 1000.00
- + + + 41190806117473000150550010000586251016759484 + + 12345678 diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040645898.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057311040645894.xml similarity index 95% rename from l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040645898.xml rename to l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057311040645894.xml index f3655a190d86..2c05057add52 100644 --- a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240708318053000167570010000000311040645898.xml +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057311040645894.xml @@ -1,6 +1,6 @@ - + 35 57000111 @@ -12,7 +12,7 @@ 2020-01-01T12:00:00+01:00 1 1 - 8 + 4 2 0 0 @@ -173,7 +173,11 @@ 1000.00 - + + + 41190806117473000150550010000586251016759484 + + 12345678 diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057411040645890.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057411040645890.xml new file mode 100644 index 000000000000..be8747c9cfb0 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057411040645890.xml @@ -0,0 +1,214 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 574 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 0 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 02 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + TEST123 + OCA56789 + 2024-11-22 + + Dimensão Padrão + + + TEST_CL + TAR123 + 0.00 + + + 1234 + 15 + + 300.0000 + 1 + + + + 5678 + 20 + + 500.0000 + 3 + + + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057511040645897.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057511040645897.xml new file mode 100644 index 000000000000..c392518fec8e --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057511040645897.xml @@ -0,0 +1,200 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 575 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 7 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 03 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + 47.00 + 1200.00 + Navio Mercante 123 + + Balsa A + + + Balsa B + + Viagem001 + S + IRIN12345 + 0 + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057611040645893.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057611040645893.xml new file mode 100644 index 000000000000..21c97f44ed96 --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057611040645893.xml @@ -0,0 +1,189 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 576 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 3 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 04 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + 1 + Fluxo Norte-Sul + + + + + diff --git a/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057711040645890.xml b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057711040645890.xml new file mode 100644 index 000000000000..dd1f1f13c7cf --- /dev/null +++ b/l10n_br_cte/tests/cte/v4_00/leiauteCTe/CTe35240781583054000129570010000057711040645890.xml @@ -0,0 +1,190 @@ + + + + + 35 + 57000111 + 5352 + Venda + 57 + 1 + 577 + 2020-01-01T12:00:00+01:00 + 1 + 1 + 0 + 2 + 0 + 0 + Odoo Brasil OCA v14 + 3550308 + São Paulo + SP + 05 + 0 + 3550308 + São Paulo + SP + 1302603 + Manaus + AM + 1 + 1 + + 3 + + + + Documento emitido por: Marc Demo + + Documento emitido por: Marc Demo + + + + 81583054000129 + 078016350838 + Empresa Lucro Presumido Ltda + Empresa Lucro Presumido + + Avenida Paulista + 1 + Bela Vista + 3550308 + São Paulo + 01311000 + SP + 551199999999 + + 3 + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + Cliente 2 -SP - Simples Nacional + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 12046835000161 + 887273429152 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 1177777777 + + Avenida Doutor Chucri Zaidan + 950 + Vila Cordeiro + 3550308 + São Paulo + 04583110 + SP + 1058 + Brasil + + cliente2@cliente2.com.br + + + 84148732000113 + 095693211 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221458888 + + Avenida Javari + s/n + Lote 9.45/15E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente4@cliente4.com.br + + + 46081676000158 + 782175040 + CTE EMITIDO EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL + 9221459999 + 101362102 + + Avenida Javari + s/n + Lote 8.45/30E + Distrito Industrial + 1302603 + Manaus + 69075110 + AM + 1058 + Brasil + + cliente3@cliente3.com.br + + + 47.00 + 47.00 + + Frete + 47.00 + + + + + + 00 + 47.00 + 18.00 + 8.46 + + + + + + 1000.00 + XYZ Product + Other Product Data + + 00 + Volume + 1000.0000 + + + 01 + Peso Bruto + 500.0000 + + + 03 + Unidade + 2.0000 + + 1000.00 + + + + 41190806117473000150550010000586251016759484 + + + + + 1500.000000 + 2024-01-01 + 2024-12-31 + + + + + diff --git a/l10n_br_cte/tests/test_cte_serialize.py b/l10n_br_cte/tests/test_cte_serialize.py index cd9bf0a36c72..fb424f849c65 100644 --- a/l10n_br_cte/tests/test_cte_serialize.py +++ b/l10n_br_cte/tests/test_cte_serialize.py @@ -51,6 +51,8 @@ def prepare_test_cte(self, cte): self.prepare_modal_aquaviario_data(cte) elif cte.cte_modal == "04": self.prepare_modal_ferroviario_data(cte) + elif cte.cte_modal == "05": + self.prepare_modal_dutoviario_data(cte) cte._document_export() @@ -78,65 +80,114 @@ def prepare_modal_rodoviario_data(self, cte): ] def prepare_modal_aereo_data(self, cte): - pass - # cte.cte40_nac = "TEST" - # cte.cte40_matr = "TEST" - # cte.cte40_nVoo = "123456789" - # cte.cte40_cAerEmb = "OACI" - # cte.cte40_cAerDes = "OACI" - # cte.cte40_dVoo = datetime.strptime("2020-01-01", "%Y-%m-%d") + # Dados gerais do modal aéreo + cte.cte40_nMinu = "TEST123" # Número do Minuta + cte.cte40_nOCA = "OCA56789" # Número do OCA + cte.cte40_dPrevAereo = datetime.strptime( + "2024-11-22", "%Y-%m-%d" + ).date() # Data prevista de entrega + cte.cte40_CL = "TEST_CL" # Código de Localidade + cte.cte40_cTar = "TAR123" # Código da Tarifa + cte.cte40_vTar = 2500.00 # Valor da Tarifa + cte.cte40_xDime = "Dimensão Padrão" # Dimensões do volume + + # Lista de produtos perigosos + cte.cte40_peri = [ + ( + 0, + 0, + { + "cte40_nONU": "1234", # Número ONU do produto perigoso + "cte40_qTotEmb": "15", # Quantidade total de volumes embarcados + "cte40_qTotProd": 300.0, # Quantidade total do produto perigoso + "cte40_uniAP": "1", # Unidade de Medida do Produto + }, + ), + ( + 0, + 0, + { + "cte40_nONU": "5678", + "cte40_qTotEmb": "20", + "cte40_qTotProd": 500.0, + "cte40_uniAP": "3", + }, + ), + ] def prepare_modal_aquaviario_data(self, cte): - pass - # cte.cte40_irin = "1234567899" - # cte.cte40_tpEmb = "01" - # cte.cte40_cEmbar = "123456" - # cte.cte40_xEmbar = "teste" - # cte.cte40_nViag = "123456" - # cte.cte40_cPrtEmb = "BRADR" - # cte.cte40_cPrtDest = "BRAFU" - # cte.cte40_infTermCarreg = [ - # (0, 0, {"loading_harbor": "BRADR"}), - # (0, 0, {"loading_harbor": "BRANT"}), - # ] - # cte.cte40_infTermDescarreg = [ - # (0, 0, {"unloading_harbor": "BRAFU"}), - # (0, 0, {"unloading_harbor": "BRBZC"}), - # ] + # Dados gerais do modal aquaviário + cte.cte40_vAFRMM = ( + 1200.00 + ) # Valor do Adicional de Frete para Renovação da Marinha Mercante + # cte.cte40_vPrest = 3500.00 # Valor total do frete aquaviário + cte.cte40_xNavio = "Navio Mercante 123" # Nome do navio + cte.cte40_nViag = "Viagem001" # Número da viagem + cte.cte40_direc = "S" # Direção de navegação: 1 = Cabotagem, 2 = Longo curso + cte.cte40_irin = "IRIN12345" # Inscrição do Registro Internacional de Navios + cte.cte40_tpNav = "0" # Tipo de navegação: 01 = Interior, 02 = Cabotagem, etc. + + # Informações das balsas transportadas + cte.cte40_balsa = [ + ( + 0, + 0, + { + "cte40_xBalsa": "Balsa A", # Identificador da primeira balsa + }, + ), + ( + 0, + 0, + { + "cte40_xBalsa": "Balsa B", # Identificador da segunda balsa + }, + ), + ] + + def prepare_modal_dutoviario_data(self, cte): + # Dados gerais do modal dutoviário + cte.cte40_dIni = "2024-01-01" # Data de início da operação dutoviária + cte.cte40_dFim = "2024-12-31" # Data de término da operação dutoviária + cte.cte40_vTar = 1500.00 # Valor da tarifa aplicada no transporte def prepare_modal_ferroviario_data(self, cte): - pass - # cte.cte40_dhTrem = datetime.strptime( - # "2020-01-01T11:00:00", "%Y-%m-%dT%H:%M:%S") - # cte.cte40_xPref = "TES" - # cte.cte40_xOri = "TES" - # cte.cte40_xDest = "TES" - # cte.cte40_qVag = 2 - # cte.cte40_vag = [ - # ( - # 0, - # 0, - # { - # "cte40_pesoBC": 500, - # "cte40_pesoR": 1, - # "cte40_tpVag": 123, - # "cte40_serie": 123, - # "cte40_nVag": 123, - # "cte40_nSeq": 123, - # "cte40_TU": 1, - # }, - # ), + # Dados gerais do modal ferroviário + cte.cte40_tpTraf = "1" # Tipo de Tráfego: 1 = Nacional, 2 = Internacional + cte.cte40_fluxo = "Fluxo Norte-Sul" # Fluxo de transporte + cte.cte40_vFrete = 5000.00 # Valor do frete ferroviário + cte.cte40_chCTeFerroOrigem = ( + "CTE123456789" + ) # Chave do CTe Ferroviário de origem + cte.cte40_respFat = ( + "1" + ) # Responsável pelo Faturamento: 1 = Emitente, 2 = Receptor + cte.cte40_ferrEmi = ( + "1" + ) # Emissor do documento: 1 = Ferrovia Emitente, 2 = Outro + # cte.cte40_ferroEnv = [ # ( # 0, # 0, # { - # "cte40_pesoBC": 500, - # "cte40_pesoR": 1, - # "cte40_tpVag": 321, - # "cte40_serie": 321, - # "cte40_nVag": 321, - # "cte40_nSeq": 321, - # "cte40_TU": 1, + # "cte40_CNPJ": "12345678000199", # CNPJ da ferrovia envolvida + # "cte40_cInt": "FERRO001", # Código interno da ferrovia + # "cte40_IE": "ISENTO", # Inscrição Estadual + # "cte40_xNome": "Ferrovia Teste LTDA", # Nome ou razão social + # "cte40_enderFerro": ( + # 0, + # 0, + # { + # "cte40_xLgr": "Rua da Ferrovia", + # "cte40_nro": "123", + # "cte40_xCpl": "Prédio 2", + # "cte40_xBairro": "Centro", + # "cte40_cMun": "1234567", # Código do município IBGE + # "cte40_xMun": "Cidade Ferrovia", + # "cte40_CEP": "12345000", + # "cte40_UF": "SP", # Unidade Federativa + # }, + # ), # }, # ), # ] diff --git a/l10n_br_cte/tests/test_cte_serialize_lc.py b/l10n_br_cte/tests/test_cte_serialize_lc.py index 1f083e25b910..7c43edc2ecd9 100644 --- a/l10n_br_cte/tests/test_cte_serialize_lc.py +++ b/l10n_br_cte/tests/test_cte_serialize_lc.py @@ -12,7 +12,23 @@ def setUp(self): cte_list = [ { "record_ref": "l10n_br_cte.demo_cte_lc_modal_rodoviario", - "xml_file": "CTe35240708318053000167570010000000311040645898.xml", + "xml_file": "CTe35240781583054000129570010000057311040645894.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_aereo", + "xml_file": "CTe35240781583054000129570010000057411040645890.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_aquaviario", + "xml_file": "CTe35240781583054000129570010000057511040645897.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_ferroviario", + "xml_file": "CTe35240781583054000129570010000057611040645893.xml", + }, + { + "record_ref": "l10n_br_cte.demo_cte_lc_modal_dutoviario", + "xml_file": "CTe35240781583054000129570010000057711040645890.xml", }, ] diff --git a/l10n_br_cte/tests/test_cte_serialize_sn.py b/l10n_br_cte/tests/test_cte_serialize_sn.py index f067b8f800b1..a65ff2d3db34 100644 --- a/l10n_br_cte/tests/test_cte_serialize_sn.py +++ b/l10n_br_cte/tests/test_cte_serialize_sn.py @@ -12,7 +12,7 @@ def setUp(self): cte_list = [ { "record_ref": "l10n_br_cte.demo_cte_sn_modal_rodoviario", - "xml_file": "CTe35240708318053000167570010000000311040445899.xml", + "xml_file": "CTe35240759594315000157570010000057311040445890.xml", }, ] From 134194728dbf930a0fa5758f6ebecc51f569ab06 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Mon, 28 Oct 2024 08:16:37 -0300 Subject: [PATCH 21/27] [RFC] l10n_br_cte_spec: rename model spec_models to spec_mixin --- l10n_br_cte_spec/models/__init__.py | 2 +- .../models/{spec_models.py => spec_mixin.py} | 0 l10n_br_cte_spec/tests/test_cte_import.py | 10 +++++----- 3 files changed, 6 insertions(+), 6 deletions(-) rename l10n_br_cte_spec/models/{spec_models.py => spec_mixin.py} (100%) diff --git a/l10n_br_cte_spec/models/__init__.py b/l10n_br_cte_spec/models/__init__.py index 1d382931ae2d..3140ceedcffa 100644 --- a/l10n_br_cte_spec/models/__init__.py +++ b/l10n_br_cte_spec/models/__init__.py @@ -1,2 +1,2 @@ -from . import spec_models +from . import spec_mixin from . import v4_0 diff --git a/l10n_br_cte_spec/models/spec_models.py b/l10n_br_cte_spec/models/spec_mixin.py similarity index 100% rename from l10n_br_cte_spec/models/spec_models.py rename to l10n_br_cte_spec/models/spec_mixin.py diff --git a/l10n_br_cte_spec/tests/test_cte_import.py b/l10n_br_cte_spec/tests/test_cte_import.py index f6ec5c7acdf3..fc39651fc2a8 100644 --- a/l10n_br_cte_spec/tests/test_cte_import.py +++ b/l10n_br_cte_spec/tests/test_cte_import.py @@ -12,7 +12,7 @@ from odoo import api from odoo.tests import TransactionCase -from ..models import spec_models +from ..models import spec_mixin tz_datetime = re.compile(r".*[-+]0[0-9]:00$") @@ -116,10 +116,10 @@ def match_or_create_m2o_fake(self, comodel, new_value, create_m2o=False): return comodel.new(new_value).id -# spec_models.CteSpecMixin._update_cache = _update_cache -spec_models.CteSpecMixin.build_fake = build_fake -spec_models.CteSpecMixin.build_attrs_fake = build_attrs_fake -spec_models.CteSpecMixin.match_or_create_m2o_fake = match_or_create_m2o_fake +# spec_mixin.CteSpecMixin._update_cache = _update_cache +spec_mixin.CteSpecMixin.build_fake = build_fake +spec_mixin.CteSpecMixin.build_attrs_fake = build_attrs_fake +spec_mixin.CteSpecMixin.match_or_create_m2o_fake = match_or_create_m2o_fake class CTeImportTest(TransactionCase): From ea2c34e36fe09b37b2559d9e9b923b5b567b5bc4 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 21 Nov 2024 16:58:26 -0300 Subject: [PATCH 22/27] [RFC] l10n_br_cte_spec: multi schema refactor --- l10n_br_cte_spec/README.rst | 8 ++--- l10n_br_cte_spec/models/spec_mixin.py | 10 +++--- .../static/description/index.html | 20 +++++------ l10n_br_cte_spec/tests/test_cte_import.py | 34 +++++++++++++++---- 4 files changed, 42 insertions(+), 30 deletions(-) diff --git a/l10n_br_cte_spec/README.rst b/l10n_br_cte_spec/README.rst index 6fbd8be6c3fd..ff00c3cd862c 100644 --- a/l10n_br_cte_spec/README.rst +++ b/l10n_br_cte_spec/README.rst @@ -32,11 +32,7 @@ Este módulo contem a estrutura de dados do Conhecimento de Transporte Eletrôni Este módulo não faz nada sozinho, ele precisaria de um modulo `l10n_br_cte` que mapearia esses mixins nos documentos fiscais Odoo de forma semelhante a forma como o módulo `l10n_br_nfe` faz como o módulo `l10n_br_nfe_spec`. -Este módulo inclue os principais leiautes persistantes de CT-e: - -* CT-e (Conhecimento de Transporte Eletrônico) -* CT-e OS (Conhecimento de transporte eletrônico para outros serviço - +Este módulo inclui os principais layouts persistentes do CT-e (Conhecimento de Transporte Eletrônico). Geração ~~~~~~~ @@ -48,7 +44,7 @@ https://github.com/akretion/xsdata-odoo O comando usado foi:: - export XSDATA_SCHEMA=cte; export XSDATA_VERSION=30; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese" + export XSDATA_SCHEMA=cte; export XSDATA_VERSION=40; export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim"; export XSDATA_LANG="portuguese" xsdata generate nfelib/cte/schemas/v4_0 --package nfelib.cte.odoo.v4_0 --output=odoo .. IMPORTANT:: diff --git a/l10n_br_cte_spec/models/spec_mixin.py b/l10n_br_cte_spec/models/spec_mixin.py index 52d5afaccfb3..799fffbfe806 100644 --- a/l10n_br_cte_spec/models/spec_mixin.py +++ b/l10n_br_cte_spec/models/spec_mixin.py @@ -8,12 +8,10 @@ class CteSpecMixin(models.AbstractModel): _description = "Abstract Model" _name = "spec.mixin.cte" _field_prefix = "cte40_" - _schema_name = "cte" - _schema_version = "4.0.0" - _odoo_module = "l10n_br_cte" - _spec_module = "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" - _binding_module = "nfelib.cte.bindings.v4_0.cte_v4_00" - _spec_tab_name = "cte" + _cte40_odoo_module = ( + "odoo.addons.l10n_br_cte_spec.models.v4_0.cte_tipos_basico_v4_00" + ) + _cte40_binding_module = "nfelib.cte.bindings.v4_0.cte_tipos_basico_v4_00" brl_currency_id = fields.Many2one( comodel_name="res.currency", diff --git a/l10n_br_cte_spec/static/description/index.html b/l10n_br_cte_spec/static/description/index.html index b3c0f5544677..5ad76673f993 100644 --- a/l10n_br_cte_spec/static/description/index.html +++ b/l10n_br_cte_spec/static/description/index.html @@ -1,4 +1,3 @@ - @@ -9,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -275,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -301,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -373,18 +373,14 @@

cte spec

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

-

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

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

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

Geração

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

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

O comando usado foi:

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

Contributors

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

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

diff --git a/l10n_br_cte_spec/tests/test_cte_import.py b/l10n_br_cte_spec/tests/test_cte_import.py index fc39651fc2a8..660a51f30e59 100644 --- a/l10n_br_cte_spec/tests/test_cte_import.py +++ b/l10n_br_cte_spec/tests/test_cte_import.py @@ -6,8 +6,7 @@ import nfelib import pkg_resources -from nfelib.cte.bindings.v4_0.cte_v4_00 import Cte -from xsdata.formats.dataclass.parsers import XmlParser +from nfelib.cte.bindings.v4_0.cte_v4_00 import Tcte from odoo import api from odoo.tests import TransactionCase @@ -67,6 +66,9 @@ def build_attrs_fake(self, node, create_m2o=False): key = fields[key]["related"][0] comodel_name = fields[key]["relation"] comodel = self.env.get(comodel_name) + elif fields.get(key) and fields[key].get("relation"): + comodel_name = fields[key]["relation"] + comodel = self.env.get(comodel_name) else: comodel = None for name in self.env.keys(): @@ -131,8 +133,26 @@ def test_import_cte(self): "43120178408960000182570010000000041000000047-cte.xml", ) resource_path = "/".join(res_items) - nfe_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) - parser = XmlParser() - binding = parser.from_string(nfe_stream.read().decode(), Cte) - cte = self.env["cte.40.tcte"].build_fake(binding, create=False) - self.assertEqual(cte.cte40_infCte.cte40_emit.cte40_CNPJ, "78408960000182") + cte_stream = pkg_resources.resource_stream(nfelib.__name__, resource_path) + binding = Tcte.from_xml(cte_stream.read().decode()) + cte = ( + self.env["cte.40.tcte_infcte"] + .with_context(tracking_disable=True, edoc_type="in", lang="pt_BR") + .build_fake(binding.infCte, create=False) + ) + self.assertEqual(cte.cte40_emit.cte40_xNome, "KERBER E CIA. LTDA.") + + self.assertEqual(cte.cte40_ide.cte40_cCT, "00000004") + self.assertEqual( + cte.cte40_Id, "CTe43120178408960000182570010000000041000000047" + ) + + self.assertEqual(cte.cte40_emit.cte40_CNPJ, "78408960000182") + self.assertEqual(cte.cte40_receb.cte40_CNPJ, "81639791000104") + + self.assertEqual(cte.cte40_exped.cte40_CNPJ, "78408960000182") + self.assertEqual(cte.cte40_dest.cte40_CNPJ, "81639791000104") + + self.assertEqual( + cte.cte40_infCTeNorm.cte40_infCarga.cte40_proPred, "Pedra Brita" + ) From 825b18ae9d82dfbd0dfada6970695a7ed0cd5405 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Fri, 22 Nov 2024 11:41:19 -0300 Subject: [PATCH 23/27] [FIX] spec_driven_model: define default value if rec_name not found --- spec_driven_model/models/spec_mixin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec_driven_model/models/spec_mixin.py b/spec_driven_model/models/spec_mixin.py index ee246ece556a..fd51d9f6146b 100644 --- a/spec_driven_model/models/spec_mixin.py +++ b/spec_driven_model/models/spec_mixin.py @@ -124,7 +124,8 @@ def _register_hook(self): filter( lambda x: (x.startswith(field_prefix) and "_choice" not in x), fields, - ) + ), + None, ) model_type = type( name, From 8147d0cc74b3589aef85602ac319b273e4995a24 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Mon, 25 Nov 2024 13:04:53 -0300 Subject: [PATCH 24/27] [RFC] l10n_br_cte: refactor dacte report --- l10n_br_cte/__init__.py | 3 +- l10n_br_cte/__manifest__.py | 14 ++++- l10n_br_cte/models/document.py | 33 ++++------ l10n_br_cte/models/res_company.py | 16 +++++ l10n_br_cte/report/__init__.py | 1 + l10n_br_cte/report/dacte_report.xml | 12 ++++ l10n_br_cte/report/ir_actions_report.py | 81 +++++++++++++++++++++++++ 7 files changed, 134 insertions(+), 26 deletions(-) create mode 100644 l10n_br_cte/report/__init__.py create mode 100644 l10n_br_cte/report/dacte_report.xml create mode 100644 l10n_br_cte/report/ir_actions_report.py diff --git a/l10n_br_cte/__init__.py b/l10n_br_cte/__init__.py index cc6b6354ad8f..ee3b8ee6dfd6 100644 --- a/l10n_br_cte/__init__.py +++ b/l10n_br_cte/__init__.py @@ -1,2 +1,3 @@ -from . import models from .hooks import post_init_hook +from . import models +from . import report diff --git a/l10n_br_cte/__manifest__.py b/l10n_br_cte/__manifest__.py index c569068a0d9e..6372e3e8dd6a 100644 --- a/l10n_br_cte/__manifest__.py +++ b/l10n_br_cte/__manifest__.py @@ -18,17 +18,25 @@ "spec_driven_model", ], "data": [ - "security/ir.model.access.csv", + # Data "data/ir_config_parameter.xml", + # Security + "security/ir.model.access.csv", + # Views "views/document.xml", - "views/cte_action.xml", - "views/cte_menu.xml", "views/res_company.xml", "views/modal/modal_rodoviario.xml", "views/modal/modal_aquaviario.xml", "views/modal/modal_ferroviario.xml", "views/modal/modal_aereo.xml", + # Report + "report/dacte_report.xml", + # Wizards "wizards/document_correction_wizard.xml", + # Actions + "views/cte_action.xml", + # Menus + "views/cte_menu.xml", ], "demo": [ "demo/company_demo.xml", diff --git a/l10n_br_cte/models/document.py b/l10n_br_cte/models/document.py index b8dad3525015..7e42b291cb1f 100644 --- a/l10n_br_cte/models/document.py +++ b/l10n_br_cte/models/document.py @@ -10,7 +10,6 @@ from datetime import datetime from enum import Enum -from brazilfiscalreport.dacte import Dacte from erpbrasil.base.fiscal import cnpj_cpf # TODO: precisa tratar @@ -1753,28 +1752,18 @@ def make_pdf(self): if not self.filtered(filter_processador_edoc_cte): return super().make_pdf() + attachment_data = { + "name": self.document_key + ".pdf", + "res_model": self._name, + "res_id": self.id, + "mimetype": "application/pdf", + "type": "binary", + } + report = self.env.ref("l10n_br_cte.report_dacte") + pdf_data = report._render_qweb_pdf(self.fiscal_line_ids.document_id.ids) + attachment_data["datas"] = base64.b64encode(pdf_data[0]) file_pdf = self.file_report_id self.file_report_id = False file_pdf.unlink() - if self.authorization_file_id: - arquivo = self.authorization_file_id - xml_string = base64.b64decode(arquivo.datas).decode() - else: - arquivo = self.send_file_id - xml_string = base64.b64decode(arquivo.datas).decode() - # TODO: implementar temp_xml_autorizacao igual nfe ? - # xml_string = self.temp_xml_autorizacao(xml_string) - - pdf = Dacte(xml=xml_string).output() - - self.file_report_id = self.env["ir.attachment"].create( - { - "name": self.document_key + ".pdf", - "res_model": self._name, - "res_id": self.id, - "datas": base64.b64encode(pdf), - "mimetype": "application/pdf", - "type": "binary", - } - ) + self.file_report_id = self.env["ir.attachment"].create(attachment_data) diff --git a/l10n_br_cte/models/res_company.py b/l10n_br_cte/models/res_company.py index 14ff286fa3f4..63146d1a04b4 100644 --- a/l10n_br_cte/models/res_company.py +++ b/l10n_br_cte/models/res_company.py @@ -102,6 +102,22 @@ class ResCompany(spec_models.SpecModel): cte40_CRT = fields.Selection(related="tax_framework") + dacte_margin_top = fields.Integer( + default=5, help="Top margin in mm for the DACTE layout." + ) + + dacte_margin_right = fields.Integer( + default=5, help="Right margin in mm for the DACTE layout." + ) + + dacte_margin_bottom = fields.Integer( + default=5, help="Bottom margin in mm for the DACTE layout." + ) + + dacte_margin_left = fields.Integer( + default=5, help="Left margin in mm for the DACTE layout." + ) + def _compute_cte_data(self): # compute because a simple related field makes the match_record fail for rec in self: diff --git a/l10n_br_cte/report/__init__.py b/l10n_br_cte/report/__init__.py new file mode 100644 index 000000000000..a248cf2162f5 --- /dev/null +++ b/l10n_br_cte/report/__init__.py @@ -0,0 +1 @@ +from . import ir_actions_report diff --git a/l10n_br_cte/report/dacte_report.xml b/l10n_br_cte/report/dacte_report.xml new file mode 100644 index 000000000000..2247f26abca9 --- /dev/null +++ b/l10n_br_cte/report/dacte_report.xml @@ -0,0 +1,12 @@ + + + DACTE + l10n_br_fiscal.document + qweb-pdf + main_template_dacte + main_template_dacte + '%s' % (object.document_key) + + report + + diff --git a/l10n_br_cte/report/ir_actions_report.py b/l10n_br_cte/report/ir_actions_report.py new file mode 100644 index 000000000000..255f45ada93c --- /dev/null +++ b/l10n_br_cte/report/ir_actions_report.py @@ -0,0 +1,81 @@ +# Copyright 2024 Engenere.one +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import base64 +from io import BytesIO + +from brazilfiscalreport.dacte import Dacte, DacteConfig, Margins + +from odoo import _, api, models +from odoo.exceptions import UserError + + +class IrActionsReport(models.Model): + _inherit = "ir.actions.report" + + def _render_qweb_html(self, res_ids, data=None): + if self.report_name == "main_template_dacte": + return + + return super()._render_qweb_html(res_ids, data=data) + + def _render_qweb_pdf(self, res_ids, data=None): + if self.report_name not in ["main_template_dacte"]: + return super()._render_qweb_pdf(res_ids, data=data) + + cte = self.env["l10n_br_fiscal.document"].search([("id", "in", res_ids)]) + + return self._render_dacte(cte) + + def _render_dacte(self, cte): + if cte.document_type != "57": + raise UserError(_("You can only print a DACTE of a CTe(57).")) + + cte_xml = False + if cte.authorization_file_id: + cte_xml = base64.b64decode(cte.authorization_file_id.datas) + elif cte.send_file_id: + cte_xml = base64.b64decode(cte.send_file_id.datas) + + if not cte_xml: + raise UserError(_("No xml file was found.")) + + return self.render_dacte_brazilfiscalreport(cte, cte_xml) + + def render_dacte_brazilfiscalreport(self, cte, cte_xml): + logo = False + if cte.issuer == "company" and cte.company_id.logo: + logo = base64.b64decode(cte.company_id.logo) + elif cte.issuer != "company" and cte.company_id.logo_web: + logo = base64.b64decode(cte.company_id.logo_web) + + if logo: + tmpLogo = BytesIO() + tmpLogo.write(logo) + tmpLogo.seek(0) + else: + tmpLogo = False + config = self._get_dacte_config(tmpLogo, cte.company_id) + + dacte = Dacte(xml=cte_xml, config=config) + + tmpDacte = BytesIO() + dacte.output(tmpDacte) + dacte_file = tmpDacte.getvalue() + tmpDacte.close() + + return dacte_file, "pdf" + + @api.model + def _get_dacte_config(self, tmpLogo, company): + margins = Margins( + top=company.dacte_margin_top, + right=company.dacte_margin_right, + bottom=company.dacte_margin_bottom, + left=company.dacte_margin_left, + ) + dacte_config = { + "logo": tmpLogo, + "margins": margins, + } + return DacteConfig(**dacte_config) From 84e68469ea4c894b59591d56fb25b9ba9ebb63e8 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Mon, 25 Nov 2024 18:29:06 -0300 Subject: [PATCH 25/27] [IMP] l10n_br_cte: add make pdf tests --- l10n_br_cte/tests/__init__.py | 1 + l10n_br_cte/tests/test_cte_dacte.py | 39 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 l10n_br_cte/tests/test_cte_dacte.py diff --git a/l10n_br_cte/tests/__init__.py b/l10n_br_cte/tests/__init__.py index 70c0e034c995..a9d1058fa8bb 100644 --- a/l10n_br_cte/tests/__init__.py +++ b/l10n_br_cte/tests/__init__.py @@ -6,3 +6,4 @@ from . import test_cte_res_partner from . import test_cte_document from . import test_res_partner +from . import test_cte_dacte diff --git a/l10n_br_cte/tests/test_cte_dacte.py b/l10n_br_cte/tests/test_cte_dacte.py new file mode 100644 index 000000000000..386717d78ad5 --- /dev/null +++ b/l10n_br_cte/tests/test_cte_dacte.py @@ -0,0 +1,39 @@ +# Copyright 2024 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.exceptions import UserError +from odoo.tests.common import TransactionCase + + +class TestDacteGeneration(TransactionCase): + def setUp(self): + super().setUp() + + def test_generate_dacte_brazil_fiscal_report(self): + cte = self.env.ref("l10n_br_cte.demo_cte_lc_modal_rodoviario") + cte.action_document_confirm() + cte.view_pdf() + + self.assertTrue(cte.file_report_id) + + def test_generate_dacte_document_type_error(self): + dacte_report = self.env["ir.actions.report"].search( + [("report_name", "=", "main_template_dacte")] + ) + cte = self.env.ref("l10n_br_cte.demo_cte_lc_modal_rodoviario") + cte.document_type_id = self.env.ref("l10n_br_fiscal.document_01") + cte.action_document_confirm() + with self.assertRaises(UserError) as captured_exception: + dacte_report._render_qweb_pdf([cte.id]) + self.assertEqual( + captured_exception.exception.args[0], + "You can only print a DACTE of a CTe(57).", + ) + + def test_generate_dacte_brazil_fiscal_report_partner(self): + cte = self.env.ref("l10n_br_cte.demo_cte_lc_modal_rodoviario") + cte.action_document_confirm() + cte.issuer = "partner" + cte.view_pdf() + + self.assertTrue(cte.file_report_id) From 82d658d97e70b557f58259a903e113a0f8b0ab45 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Tue, 26 Nov 2024 12:53:50 -0300 Subject: [PATCH 26/27] [IMP] l10n_br_cte: add dacte settings on company form --- l10n_br_cte/views/res_company.xml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/l10n_br_cte/views/res_company.xml b/l10n_br_cte/views/res_company.xml index b4faac397deb..028649e95ea5 100644 --- a/l10n_br_cte/views/res_company.xml +++ b/l10n_br_cte/views/res_company.xml @@ -10,7 +10,7 @@ - + @@ -20,8 +20,13 @@ - - + + + + + + + From fcdac1e392a6a9dbf965e2cde1a2e144311d4c83 Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Wed, 27 Nov 2024 09:04:18 -0300 Subject: [PATCH 27/27] [IMP] l10n_br_cte: add Engenere on CONTRIBUTORS.rst file --- l10n_br_cte/README.rst | 4 ++++ l10n_br_cte/readme/CONTRIBUTORS.rst | 4 ++++ l10n_br_cte/static/description/index.html | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/l10n_br_cte/README.rst b/l10n_br_cte/README.rst index 1470f0b8b525..c7c3458a6ea3 100644 --- a/l10n_br_cte/README.rst +++ b/l10n_br_cte/README.rst @@ -106,6 +106,10 @@ Contributors * Raphaël Valyi +* `Engenere `_: + + * Antônio S. Pereira Neto + Maintainers ~~~~~~~~~~~ diff --git a/l10n_br_cte/readme/CONTRIBUTORS.rst b/l10n_br_cte/readme/CONTRIBUTORS.rst index 451e440cc7a7..b514bba4ef3c 100644 --- a/l10n_br_cte/readme/CONTRIBUTORS.rst +++ b/l10n_br_cte/readme/CONTRIBUTORS.rst @@ -11,3 +11,7 @@ * `AKRETION `_: * Raphaël Valyi + +* `Engenere `_: + + * Antônio S. Pereira Neto diff --git a/l10n_br_cte/static/description/index.html b/l10n_br_cte/static/description/index.html index fbdac83c2104..7ce244bcb41a 100644 --- a/l10n_br_cte/static/description/index.html +++ b/l10n_br_cte/static/description/index.html @@ -453,6 +453,10 @@

Contributors

  • Raphaël Valyi <raphael.valyi@akretion.com.br>
  • +
  • Engenere: +