|
| 1 | +import logging |
| 2 | + |
1 | 3 | from odoo import _, api, fields, models |
2 | 4 | from odoo.tools import format_date |
3 | 5 |
|
| 6 | +from ..wizard import efattura |
| 7 | + |
| 8 | +_logger = logging.getLogger(__name__) |
| 9 | + |
4 | 10 | SELF_INVOICE_TYPES = ("TD16", "TD17", "TD18", "TD19", "TD20", "TD21", "TD27", "TD28") |
5 | 11 |
|
6 | 12 |
|
@@ -43,8 +49,13 @@ class FatturaPAAttachmentIn(models.Model): |
43 | 49 | compute="_compute_e_invoice_validation_error" |
44 | 50 | ) |
45 | 51 |
|
| 52 | + e_invoice_parsing_error = fields.Text( |
| 53 | + compute="_compute_e_invoice_parsing_error", |
| 54 | + store=True, |
| 55 | + ) |
| 56 | + |
46 | 57 | is_self_invoice = fields.Boolean( |
47 | | - "Contains self invoices", compute="_compute_xml_data", store=True |
| 58 | + "Contains self invoices", compute="_compute_is_self_invoice", store=True |
48 | 59 | ) |
49 | 60 |
|
50 | 61 | inconsistencies = fields.Text(compute="_compute_xml_data", store=True) |
@@ -84,41 +95,114 @@ def _compute_e_invoice_validation_error(self): |
84 | 95 | att.e_invoice_validation_message = "\n\n".join(error_messages) |
85 | 96 |
|
86 | 97 | def recompute_xml_fields(self): |
87 | | - self._compute_xml_data() |
| 98 | + # Pretend the attachment has been modified |
| 99 | + # and trigger a recomputation: |
| 100 | + # this recomputes all fields whose value |
| 101 | + # is extracted from the attachment |
| 102 | + self.modified(["ir_attachment_id"]) |
| 103 | + self.recompute() |
88 | 104 | self._compute_registered() |
89 | 105 |
|
| 106 | + def get_invoice_obj(self): |
| 107 | + """ |
| 108 | + Parse the invoice into a lxml.etree.ElementTree object. |
| 109 | + If the parsing goes wrong: |
| 110 | + - log the error |
| 111 | + - save the parsing error in field `e_invoice_parsing_error` |
| 112 | + - return `False` |
| 113 | + :rtype: lxml.etree.ElementTree or bool. |
| 114 | + """ |
| 115 | + self.ensure_one() |
| 116 | + invoice_obj = False |
| 117 | + try: |
| 118 | + xml_string = self.get_xml_string() |
| 119 | + invoice_obj = efattura.CreateFromDocument(xml_string) |
| 120 | + except Exception as e: |
| 121 | + error_msg = _("Impossible to parse XML for {att_name}: {error_msg}").format( |
| 122 | + att_name=self.display_name, |
| 123 | + error_msg=e, |
| 124 | + ) |
| 125 | + _logger.error(error_msg) |
| 126 | + self.e_invoice_parsing_error = error_msg |
| 127 | + else: |
| 128 | + self.e_invoice_parsing_error = False |
| 129 | + return invoice_obj |
| 130 | + |
| 131 | + @api.depends("ir_attachment_id.datas") |
| 132 | + def _compute_is_self_invoice(self): |
| 133 | + for att in self: |
| 134 | + att.is_self_invoice = False |
| 135 | + if not att.datas: |
| 136 | + return |
| 137 | + fatt = att.get_invoice_obj() |
| 138 | + if fatt: |
| 139 | + for invoice_body in fatt.FatturaElettronicaBody: |
| 140 | + document_type = ( |
| 141 | + invoice_body.DatiGenerali.DatiGeneraliDocumento.TipoDocumento |
| 142 | + ) |
| 143 | + if document_type in SELF_INVOICE_TYPES: |
| 144 | + att.is_self_invoice = True |
| 145 | + break |
| 146 | + |
| 147 | + @api.depends("ir_attachment_id.datas") |
| 148 | + def _compute_e_invoice_parsing_error(self): |
| 149 | + for att in self: |
| 150 | + if not att.datas: |
| 151 | + return |
| 152 | + att.get_invoice_obj() |
| 153 | + |
90 | 154 | @api.depends("ir_attachment_id.datas") |
91 | 155 | def _compute_xml_data(self): |
92 | 156 | for att in self: |
93 | 157 | att.xml_supplier_id = False |
94 | 158 | att.invoices_number = False |
95 | 159 | att.invoices_total = False |
96 | 160 | att.invoices_date = False |
97 | | - att.is_self_invoice = False |
98 | | - if not att.ir_attachment_id.datas: |
| 161 | + if not att.datas: |
| 162 | + return |
| 163 | + fatt = att.get_invoice_obj() |
| 164 | + if not fatt: |
| 165 | + # Set default values and carry on |
99 | 166 | continue |
100 | | - wiz_obj = self.env["wizard.import.fatturapa"].with_context( |
101 | | - from_attachment=att |
102 | | - ) |
103 | | - fatt = wiz_obj.get_invoice_obj(att) |
104 | | - cedentePrestatore = fatt.FatturaElettronicaHeader.CedentePrestatore |
105 | | - partner_id = wiz_obj.getCedPrest(cedentePrestatore) |
106 | | - att.xml_supplier_id = partner_id |
107 | | - att.invoices_number = len(fatt.FatturaElettronicaBody) |
108 | | - att.invoices_total = 0 |
| 167 | + # Look into each invoice to compute the following values |
109 | 168 | invoices_date = [] |
110 | 169 | for invoice_body in fatt.FatturaElettronicaBody: |
111 | | - dgd = invoice_body.DatiGenerali.DatiGeneraliDocumento |
112 | | - att.invoices_total += float(dgd.ImportoTotaleDocumento or 0) |
| 170 | + # Assign this directly so that rounding is applied each time |
| 171 | + att.invoices_total += float( |
| 172 | + invoice_body.DatiGenerali.DatiGeneraliDocumento.ImportoTotaleDocumento |
| 173 | + or 0 |
| 174 | + ) |
| 175 | + |
| 176 | + document_date = invoice_body.DatiGenerali.DatiGeneraliDocumento.Data |
113 | 177 | invoice_date = format_date( |
114 | 178 | att.with_context(lang=att.env.user.lang).env, |
115 | | - fields.Date.from_string(dgd.Data), |
| 179 | + fields.Date.from_string(document_date), |
116 | 180 | ) |
117 | 181 | if invoice_date not in invoices_date: |
118 | 182 | invoices_date.append(invoice_date) |
119 | | - if dgd.TipoDocumento in SELF_INVOICE_TYPES: |
120 | | - att.is_self_invoice = True |
121 | | - att.invoices_date = " ".join(invoices_date) |
| 183 | + |
| 184 | + att.update( |
| 185 | + dict( |
| 186 | + invoices_date=" ".join(invoices_date), |
| 187 | + ) |
| 188 | + ) |
| 189 | + |
| 190 | + # We don't need to look into each invoice |
| 191 | + # for the following fields |
| 192 | + att.invoices_number = len(fatt.FatturaElettronicaBody) |
| 193 | + |
| 194 | + # Partner creation that may happen in `getCedPrest` |
| 195 | + # triggers a recomputation |
| 196 | + # that messes up the cache of some fields if they are set |
| 197 | + # (more properly, put in cache) afterwards; |
| 198 | + # this happens for `is_self_invoice` for instance. |
| 199 | + # That is why we set it as the last field. |
| 200 | + cedentePrestatore = fatt.FatturaElettronicaHeader.CedentePrestatore |
| 201 | + wiz_obj = self.env["wizard.import.fatturapa"].with_context( |
| 202 | + from_attachment=att |
| 203 | + ) |
| 204 | + partner_id = wiz_obj.getCedPrest(cedentePrestatore) |
| 205 | + att.xml_supplier_id = partner_id |
122 | 206 | inconsistencies = wiz_obj.env.context.get("inconsistencies", False) |
123 | 207 | att.inconsistencies = inconsistencies |
124 | 208 |
|
@@ -151,12 +235,12 @@ def extract_attachments(self, AttachmentsData, invoice_id): |
151 | 235 | @api.depends("ir_attachment_id.datas") |
152 | 236 | def _compute_linked_invoice_id_xml(self): |
153 | 237 | for att in self: |
| 238 | + att.linked_invoice_id_xml = "" |
| 239 | + if not att.datas: |
| 240 | + return |
154 | 241 | if isinstance(att.id, int): |
155 | 242 | att.linked_invoice_id_xml = "" |
156 | | - wiz_obj = self.env["wizard.import.fatturapa"].with_context( |
157 | | - from_attachment=att |
158 | | - ) |
159 | | - fatt = wiz_obj.get_invoice_obj(att) |
| 243 | + fatt = att.get_invoice_obj() |
160 | 244 | if fatt: |
161 | 245 | for invoice_body in fatt.FatturaElettronicaBody: |
162 | 246 | if ( |
|
0 commit comments