|
1 |
| -# Copyright (C) 2023 KMEE Informatica LTDA |
2 |
| -# License AGPL-3 or later (http://www.gnu.org/licenses/agpl) |
| 1 | +# Copyright (C) 2025-Today - Engenere (<https://engenere.one>). |
| 2 | +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
| 3 | +from odoo import fields, models |
3 | 4 |
|
4 |
| -import logging |
5 |
| -import re |
6 |
| - |
7 |
| -from erpbrasil.transmissao import TransmissaoSOAP |
8 |
| -from nfelib.nfe.ws.edoc_legacy import NFeAdapter as edoc_nfe |
9 |
| -from requests import Session |
10 |
| - |
11 |
| -from odoo import _, api, fields, models |
12 |
| - |
13 |
| -from ..tools import utils |
14 |
| - |
15 |
| -_logger = logging.getLogger(__name__) |
| 5 | +from ..constants.dfe import ( |
| 6 | + OPERATION_TYPE, |
| 7 | + SITUACAO_NFE, |
| 8 | +) |
16 | 9 |
|
17 | 10 |
|
18 | 11 | class DFe(models.Model):
|
19 | 12 | _name = "l10n_br_fiscal.dfe"
|
20 |
| - _inherit = ["mail.thread", "mail.activity.mixin"] |
21 |
| - _description = "Consult DF-e" |
22 |
| - _order = "id desc" |
23 |
| - _rec_name = "display_name" |
| 13 | + _description = "DF-e" |
24 | 14 |
|
25 |
| - display_name = fields.Char(compute="_compute_display_name") |
| 15 | + dfe_access_key_id = fields.Many2one( |
| 16 | + comodel_name="l10n_br_fiscal.dfe_access_key", string="Chave de Acesso" |
| 17 | + ) |
26 | 18 |
|
27 |
| - company_id = fields.Many2one(comodel_name="res.company", string="Company") |
| 19 | + key = fields.Char(string="Access Key", size=44, related="dfe_access_key_id.key") |
28 | 20 |
|
29 |
| - version = fields.Selection(related="company_id.dfe_version") |
| 21 | + serie = fields.Char(size=3, index=True) |
30 | 22 |
|
31 |
| - environment = fields.Selection(related="company_id.dfe_environment") |
| 23 | + number = fields.Float(string="Document Number", index=True, digits=(18, 0)) |
32 | 24 |
|
33 |
| - last_nsu = fields.Char(string="Last NSU", size=25, default="0") |
| 25 | + emitter = fields.Char(size=60) |
34 | 26 |
|
35 |
| - last_query = fields.Datetime(string="Last query") |
| 27 | + cnpj_cpf = fields.Char(string="CNPJ/CPF", size=18) |
36 | 28 |
|
37 |
| - imported_document_ids = fields.One2many( |
38 |
| - comodel_name="l10n_br_fiscal.document", |
39 |
| - inverse_name="dfe_id", |
40 |
| - string="Imported Documents", |
41 |
| - ) |
| 29 | + nsu = fields.Char(string="NSU", size=25, index=True) |
42 | 30 |
|
43 |
| - use_cron = fields.Boolean( |
44 |
| - default=False, |
45 |
| - string="Download new documents automatically", |
46 |
| - help="If activated, allows new manifestations to be automatically " |
47 |
| - "searched with a Cron", |
| 31 | + operation_type = fields.Selection( |
| 32 | + selection=OPERATION_TYPE, |
48 | 33 | )
|
49 | 34 |
|
50 |
| - @api.depends("company_id.name", "last_nsu") |
51 |
| - def name_get(self): |
52 |
| - return self.mapped(lambda d: (d.id, f"{d.company_id.name} - NSU: {d.last_nsu}")) |
53 |
| - |
54 |
| - @api.model |
55 |
| - def _get_processor(self): |
56 |
| - certificado = self.env.company._get_br_ecertificate() |
57 |
| - session = Session() |
58 |
| - session.verify = False |
59 |
| - return edoc_nfe( |
60 |
| - TransmissaoSOAP(certificado, session), |
61 |
| - self.company_id.state_id.ibge_code, |
62 |
| - versao=self.version, |
63 |
| - ambiente=self.environment, |
64 |
| - ) |
65 |
| - |
66 |
| - @api.model |
67 |
| - def validate_distribution_response(self, result): |
68 |
| - valid = False |
69 |
| - message = result.resposta.xMotivo |
70 |
| - if result.retorno.status_code != 200: |
71 |
| - code = result.retorno.status_code |
72 |
| - elif result.resposta.cStat != "138": |
73 |
| - code = result.resposta.cStat |
74 |
| - else: |
75 |
| - valid = True |
76 |
| - |
77 |
| - if not valid: |
78 |
| - self.message_post( |
79 |
| - body=_( |
80 |
| - _( |
81 |
| - "Error validating document distribution:" |
82 |
| - "\n\n%(code)s - %(message)s", |
83 |
| - code=code, |
84 |
| - message=message, |
85 |
| - ) |
86 |
| - ) |
87 |
| - ) |
88 |
| - |
89 |
| - return valid |
90 |
| - |
91 |
| - @api.model |
92 |
| - def _document_distribution(self): |
93 |
| - maxNSU = "" |
94 |
| - while maxNSU != self.last_nsu: |
95 |
| - try: |
96 |
| - result = self._get_processor().consultar_distribuicao( |
97 |
| - cnpj_cpf=re.sub("[^0-9]", "", self.company_id.cnpj_cpf), |
98 |
| - ultimo_nsu=utils.format_nsu(self.last_nsu), |
99 |
| - ) |
100 |
| - except Exception as e: |
101 |
| - self.message_post( |
102 |
| - body=_("Error on searching documents.\n%(error)s", error=e) |
103 |
| - ) |
104 |
| - break |
105 |
| - |
106 |
| - self.write( |
107 |
| - { |
108 |
| - "last_nsu": result.resposta.ultNSU, |
109 |
| - "last_query": fields.Datetime.now(), |
110 |
| - } |
111 |
| - ) |
| 35 | + document_value = fields.Float( |
| 36 | + string="Document Total Value", |
| 37 | + readonly=True, |
| 38 | + digits=(18, 2), |
| 39 | + ) |
112 | 40 |
|
113 |
| - if not self.validate_distribution_response(result): |
114 |
| - break |
| 41 | + ie = fields.Char(string="Inscrição estadual", size=18) |
115 | 42 |
|
116 |
| - self._process_distribution(result) |
| 43 | + partner_id = fields.Many2one( |
| 44 | + comodel_name="res.partner", |
| 45 | + string="Supplier (partner)", |
| 46 | + ) |
117 | 47 |
|
118 |
| - maxNSU = result.resposta.maxNSU |
| 48 | + company_id = fields.Many2one( |
| 49 | + comodel_name="res.company", |
| 50 | + string="Company", |
| 51 | + default=lambda self: self.env.company, |
| 52 | + readonly=True, |
| 53 | + ) |
119 | 54 |
|
120 |
| - @api.model |
121 |
| - def _process_distribution(self, result): |
122 |
| - """Method to process the distribution data.""" |
| 55 | + emission_datetime = fields.Datetime( |
| 56 | + string="Emission Date", |
| 57 | + index=True, |
| 58 | + default=fields.Datetime.now, |
| 59 | + ) |
123 | 60 |
|
124 |
| - @api.model |
125 |
| - def _parse_xml_document(self, document): |
126 |
| - schema_type = document.schema.split("_")[0] |
127 |
| - method = "parse_%s" % schema_type |
128 |
| - if not hasattr(self, method): |
129 |
| - return |
| 61 | + inclusion_datetime = fields.Datetime( |
| 62 | + string="Inclusion Date", |
| 63 | + index=True, |
| 64 | + default=fields.Datetime.now, |
| 65 | + ) |
130 | 66 |
|
131 |
| - xml = utils.parse_gzip_xml(document.valueOf_) |
132 |
| - return getattr(self, method)(xml) |
| 67 | + inclusion_mode = fields.Char(size=255) |
133 | 68 |
|
134 |
| - @api.model |
135 |
| - def _download_document(self, nfe_key): |
136 |
| - try: |
137 |
| - result = self._get_processor().consultar_distribuicao( |
138 |
| - chave=nfe_key, cnpj_cpf=re.sub("[^0-9]", "", self.company_id.cnpj_cpf) |
139 |
| - ) |
140 |
| - except Exception as e: |
141 |
| - self.message_post( |
142 |
| - body=_("Error on searching documents.\n%(error)s", error=e) |
143 |
| - ) |
144 |
| - return |
| 69 | + document_state = fields.Selection( |
| 70 | + selection=SITUACAO_NFE, |
| 71 | + index=True, |
| 72 | + ) |
145 | 73 |
|
146 |
| - if not self.validate_distribution_response(result): |
147 |
| - return |
| 74 | + cfop_ids = fields.Many2many( |
| 75 | + comodel_name="l10n_br_fiscal.cfop", |
| 76 | + string="CFOPs", |
| 77 | + ) |
148 | 78 |
|
149 |
| - return result.resposta.loteDistDFeInt.docZip[0] |
| 79 | + dfe_nfe_document_type = fields.Selection( |
| 80 | + selection=[ |
| 81 | + ("dfe_nfe_complete", "NF-e Completa"), |
| 82 | + ("dfe_nfe_summary", "Resumo da NF-e"), |
| 83 | + ("dfe_nfe_event", "Evento da NF-e"), |
| 84 | + ], |
| 85 | + string="DFe Document Type", |
| 86 | + ) |
150 | 87 |
|
151 |
| - @api.model |
152 |
| - def _cron_search_documents(self): |
153 |
| - self.search([("use_cron", "=", True)]).search_documents() |
| 88 | + dfe_monitor_id = fields.Many2one( |
| 89 | + comodel_name="l10n_br_fiscal.dfe_monitor", |
| 90 | + string="DFe Monitor", |
| 91 | + ) |
154 | 92 |
|
155 |
| - def search_documents(self): |
156 |
| - for record in self: |
157 |
| - record._document_distribution() |
| 93 | + def name_get(self): |
| 94 | + return [ |
| 95 | + ( |
| 96 | + rec.id, |
| 97 | + f"{rec.emitter} - {rec.key}", |
| 98 | + ) |
| 99 | + for rec in self |
| 100 | + ] |
0 commit comments