From 570a7ec707262f01e4d9e6d39bc5336d2f20404f Mon Sep 17 00:00:00 2001 From: eLBati Date: Fri, 1 Mar 2024 14:39:30 +0100 Subject: [PATCH 1/3] [FIX] l10n_it_fatturapa_in: Restore original precision after import Otherwise, if another precision is used during import and an exception is raised, the system precision becomes the precision set during import. Co-authored-by: Simone Rubino --- .../wizard/wizard_import_fatturapa.py | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/l10n_it_fatturapa_in/wizard/wizard_import_fatturapa.py b/l10n_it_fatturapa_in/wizard/wizard_import_fatturapa.py index 2578faae1923..12c338312777 100644 --- a/l10n_it_fatturapa_in/wizard/wizard_import_fatturapa.py +++ b/l10n_it_fatturapa_in/wizard/wizard_import_fatturapa.py @@ -6,7 +6,7 @@ import re from datetime import datetime -from odoo import api, fields, models, registry +from odoo import api, fields, models from odoo.exceptions import UserError from odoo.fields import first from odoo.osv import expression @@ -1792,28 +1792,14 @@ def _set_decimal_precision(self, precision_name, field_name): ) different_precisions = original_precision = None if precision: - precision_id = precision.id original_precision = precision.digits different_precisions = self[field_name] != original_precision if different_precisions: - with registry(self.env.cr.dbname).cursor() as new_cr: - # We need a new env (and cursor) because 'digits' property of Float - # fields is retrieved with a new LazyCursor, - # see class Float at odoo.fields, - # so we need to write (commit) to DB in order to make the new - # precision available - new_env = api.Environment(new_cr, self.env.uid, self.env.context) - new_precision = new_env["decimal.precision"].browse(precision_id) - new_precision.sudo().write({"digits": self[field_name]}) - new_cr.commit() + precision.sudo().digits = self[field_name] return precision, different_precisions, original_precision def _restore_original_precision(self, precision, original_precision): - with registry(self.env.cr.dbname).cursor() as new_cr: - new_env = api.Environment(new_cr, self.env.uid, self.env.context) - new_price_precision = new_env["decimal.precision"].browse(precision.id) - new_price_precision.sudo().write({"digits": original_precision}) - new_cr.commit() + precision.sudo().digits = original_precision def _get_invoice_partner_id(self, fatt): cedentePrestatore = fatt.FatturaElettronicaHeader.CedentePrestatore From 7b3db28a516b71a0bd18ef173369ee8b0c80b4ad Mon Sep 17 00:00:00 2001 From: Simone Rubino Date: Wed, 20 Nov 2024 14:39:03 +0100 Subject: [PATCH 2/3] [IMP] l10n_it_fatturapa_in: Computation of prices with many digits When price precision is increased during import, the price of the created lines should have been computed using the new precision --- l10n_it_fatturapa_in/__init__.py | 1 + l10n_it_fatturapa_in/fields.py | 47 ++++++++++ l10n_it_fatturapa_in/models/attachment.py | 18 ++++ .../tests/data/IT01234567890_FPR16.xml | 87 +++++++++++++++++++ .../tests/fatturapa_common.py | 4 + .../tests/test_import_fatturapa_xml.py | 59 +++++++++++++ .../wizard/wizard_import_fatturapa.py | 21 +++-- 7 files changed, 232 insertions(+), 5 deletions(-) create mode 100644 l10n_it_fatturapa_in/fields.py create mode 100644 l10n_it_fatturapa_in/tests/data/IT01234567890_FPR16.xml diff --git a/l10n_it_fatturapa_in/__init__.py b/l10n_it_fatturapa_in/__init__.py index 9b4296142f47..3a1fd6e5d147 100644 --- a/l10n_it_fatturapa_in/__init__.py +++ b/l10n_it_fatturapa_in/__init__.py @@ -1,2 +1,3 @@ +from . import fields from . import models from . import wizard diff --git a/l10n_it_fatturapa_in/fields.py b/l10n_it_fatturapa_in/fields.py new file mode 100644 index 000000000000..e9ffa8f66855 --- /dev/null +++ b/l10n_it_fatturapa_in/fields.py @@ -0,0 +1,47 @@ +# Copyright 2024 Simone Rubino - Aion Tech +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.fields import Float + +orig_convert_to_cache = Float.convert_to_cache +orig_get_digits = Float.get_digits + +E_INVOICE_PRECISION_TO_FIELD = { + "Discount": "discount_decimal_digits", + "Product Price": "price_decimal_digits", + "Product Unit of Measure": "quantity_decimal_digits", +} +# Map a `decimal.precision` to the name of the field in `fatturapa.attachment` +# that stores the value used during import + + +def get_digits(self, env): + digits = orig_get_digits(self, env) + if e_invoice_precision := env.context.get("l10n_it_fatturapa_in_precision"): + digits = digits[0], e_invoice_precision + return digits + + +def convert_to_cache(self, value, record, validate=True): + if record._name in ("account.move", "account.move.line"): + e_invoice = record.fatturapa_attachment_in_id + if e_invoice: + # The invoice [line] has been created by importing an e-invoice. + # If a different precision has been used, + # keep using that precision to read values that have it. + field_precision = self._digits + if isinstance(field_precision, str): + e_invoice_precision_field = E_INVOICE_PRECISION_TO_FIELD.get( + field_precision + ) + if e_invoice_precision_field: + if e_invoice_precision := e_invoice[e_invoice_precision_field]: + record = record.with_context( + l10n_it_fatturapa_in_precision=e_invoice_precision + ) + + return orig_convert_to_cache(self, value, record, validate=validate) + + +Float.convert_to_cache = convert_to_cache +Float.get_digits = get_digits diff --git a/l10n_it_fatturapa_in/models/attachment.py b/l10n_it_fatturapa_in/models/attachment.py index 734f7745f977..b16fca5505e1 100644 --- a/l10n_it_fatturapa_in/models/attachment.py +++ b/l10n_it_fatturapa_in/models/attachment.py @@ -77,6 +77,24 @@ class FatturaPAAttachmentIn(models.Model): compute="_compute_linked_invoice_id_xml", store=True, ) + price_decimal_digits = fields.Integer( + string="Prices decimal digits", + help="Value used during import of this e-invoice " + 'to override "Product Price" precision.', + readonly=True, + ) + quantity_decimal_digits = fields.Integer( + string="Quantities decimal digits", + help="Value used during import of this e-invoice " + 'to override "Product Unit of Measure" precision.', + readonly=True, + ) + discount_decimal_digits = fields.Integer( + string="Discounts decimal digits", + help="Value used during import of this e-invoice " + 'to override "Discount" precision.', + readonly=True, + ) _sql_constraints = [ ( diff --git a/l10n_it_fatturapa_in/tests/data/IT01234567890_FPR16.xml b/l10n_it_fatturapa_in/tests/data/IT01234567890_FPR16.xml new file mode 100644 index 000000000000..55ca5dbe6b49 --- /dev/null +++ b/l10n_it_fatturapa_in/tests/data/IT01234567890_FPR16.xml @@ -0,0 +1,87 @@ + + + + + + IT + 02780790107 + + FPR14 + FPR12 + 0000000 + + 06543534343 + info@yourcompany.example.com + + test@pec.it + + + + + IT + 02780790107 + + + YourCompany + + RF01 + + + Via Milano, 1 + 00100 + Roma + AK + IT + + + 06543534343 + info@yourcompany.example.com + + + + + + IT + 07973780013 + + 07973780013 + + B2B Customer + + + + Via Roma, 1 + 16100 + Genova + AK + IT + + + + + + + TD01 + EUR + 2020-09-30 + 14481 + 81.49 + + + + + 1 + Test precisione decimale + 69.00 + 0.968 + 66.792 + 22.00 + + + 22.00 + 66.79 + 14.69 + + + + diff --git a/l10n_it_fatturapa_in/tests/fatturapa_common.py b/l10n_it_fatturapa_in/tests/fatturapa_common.py index 197081db5e10..e7a52f45e235 100644 --- a/l10n_it_fatturapa_in/tests/fatturapa_common.py +++ b/l10n_it_fatturapa_in/tests/fatturapa_common.py @@ -288,6 +288,8 @@ def run_wizard( ): if module_name is None: module_name = "l10n_it_fatturapa_in" + if wiz_values is None: + wiz_values = dict() attach = self.create_attachment(name, file_name, module_name=module_name) attach.e_invoice_received_date = fields.Datetime.now() attach_id = attach.id @@ -297,6 +299,8 @@ def run_wizard( active_ids=[attach_id], active_model="fatturapa.attachment.in" ) ) + for wiz_field, wiz_value in wiz_values.items(): + setattr(wizard_form, wiz_field, wiz_value) wizard = wizard_form.save() return wizard.importFatturaPA() if mode == "link": diff --git a/l10n_it_fatturapa_in/tests/test_import_fatturapa_xml.py b/l10n_it_fatturapa_in/tests/test_import_fatturapa_xml.py index be8f6ddc2ba7..9f2d36ccde2c 100644 --- a/l10n_it_fatturapa_in/tests/test_import_fatturapa_xml.py +++ b/l10n_it_fatturapa_in/tests/test_import_fatturapa_xml.py @@ -1150,6 +1150,65 @@ def test_ignore_global_discount(self): self.assertEqual(invoice.amount_tax, 5.12) self.assertEqual(invoice.amount_total, 28.39) + def test_increased_decimal_precision(self): + """ + Increase price decimal precision during import: + computation of line's price is more accurate. + """ + res = self.run_wizard( + "increased_decimal_precision", + "IT01234567890_FPR16.xml", + wiz_values={ + "price_decimal_digits": 3, + }, + ) + + # The new precision allows to compute the correct amount + invoice = self.invoice_model.search(res["domain"]) + expected_invoice_values = { + "amount_untaxed": 66.79, + "amount_tax": 14.69, + "amount_total": 81.48, + } + self.assertRecordValues( + invoice, + [ + expected_invoice_values, + ], + ) + invoice_line = invoice.invoice_line_ids + expected_invoice_line_values = { + "price_subtotal": 66.79, + "price_total": 81.48, + } + self.assertRecordValues( + invoice_line, + [ + expected_invoice_line_values, + ], + ) + + # Trigger amounts recomputation because: + # date triggers an update on date_due + # date_due triggers an update on needed_terms + # needed_terms needs amount_total_signed + with Form(invoice) as invoice_form: + invoice_form.date = fields.Date.today() + + # The correct amount is kept + self.assertRecordValues( + invoice, + [ + expected_invoice_values, + ], + ) + self.assertRecordValues( + invoice_line, + [ + expected_invoice_line_values, + ], + ) + class TestFatturaPAEnasarco(FatturapaCommon): def setUp(self): diff --git a/l10n_it_fatturapa_in/wizard/wizard_import_fatturapa.py b/l10n_it_fatturapa_in/wizard/wizard_import_fatturapa.py index 12c338312777..c765d94878ac 100644 --- a/l10n_it_fatturapa_in/wizard/wizard_import_fatturapa.py +++ b/l10n_it_fatturapa_in/wizard/wizard_import_fatturapa.py @@ -1786,7 +1786,7 @@ def create_and_get_line_id(self, invoice_line_ids, invoice_line_model, upd_vals) ) invoice_line_ids.append(invoice_line_id) - def _set_decimal_precision(self, precision_name, field_name): + def _set_decimal_precision(self, precision_name, field_name, attachments): precision = self.env["decimal.precision"].search( [("name", "=", precision_name)], limit=1 ) @@ -1796,6 +1796,11 @@ def _set_decimal_precision(self, precision_name, field_name): different_precisions = self[field_name] != original_precision if different_precisions: precision.sudo().digits = self[field_name] + attachments.update( + { + field_name: self[field_name], + } + ) return precision, different_precisions, original_precision def _restore_original_precision(self, precision, original_precision): @@ -1808,28 +1813,34 @@ def _get_invoice_partner_id(self, fatt): def importFatturaPA(self): self.ensure_one() + fatturapa_attachments = self._get_selected_records() ( price_precision, different_price_precisions, original_price_precision, - ) = self._set_decimal_precision("Product Price", "price_decimal_digits") + ) = self._set_decimal_precision( + "Product Price", "price_decimal_digits", attachments=fatturapa_attachments + ) ( qty_precision, different_qty_precisions, original_qty_precision, ) = self._set_decimal_precision( - "Product Unit of Measure", "quantity_decimal_digits" + "Product Unit of Measure", + "quantity_decimal_digits", + attachments=fatturapa_attachments, ) ( discount_precision, different_discount_precisions, original_discount_precision, - ) = self._set_decimal_precision("Discount", "discount_decimal_digits") + ) = self._set_decimal_precision( + "Discount", "discount_decimal_digits", attachments=fatturapa_attachments + ) new_invoices = [] # convert to dict in order to be able to modify context - fatturapa_attachments = self._get_selected_records() self.env.context = dict(self.env.context) for fatturapa_attachment in fatturapa_attachments: self.reset_inconsistencies() From 4fb4219e54ddbc13e9305b4fc3d499cee900271a Mon Sep 17 00:00:00 2001 From: Simone Rubino Date: Fri, 13 Dec 2024 13:09:04 +0100 Subject: [PATCH 3/3] [FIX] l10n_it_fatturapa_pec: Remove extra fields from message `message_dict` contains values used to track the message, but we use it to create a `mail.message` so the extra values have to be removed because they are not fields of `mail.message`. --- l10n_it_fatturapa_pec/models/mail_thread.py | 1 + 1 file changed, 1 insertion(+) diff --git a/l10n_it_fatturapa_pec/models/mail_thread.py b/l10n_it_fatturapa_pec/models/mail_thread.py index da7be41312f5..6b321cdc3e43 100644 --- a/l10n_it_fatturapa_pec/models/mail_thread.py +++ b/l10n_it_fatturapa_pec/models/mail_thread.py @@ -45,6 +45,7 @@ def clean_message_dict(self, message_dict): del message_dict["bounced_partner"] del message_dict["bounced_msg_id"] del message_dict["bounced_message"] + del message_dict["x_odoo_message_id"] @api.model def message_route(