From 209da20bdae6e902ab8dbac8e4544414fd0c6b6e Mon Sep 17 00:00:00 2001 From: Julien Van Roy Date: Thu, 30 Mar 2023 07:38:01 +0000 Subject: [PATCH] [IMP] account_edi_ubl_cii: create partner at import if not retrieved When importing a vendor bill/invoice, a matching partner is searched. If no match is found, a new partner will created. task-3141337 closes odoo/odoo#117168 X-original-commit: 08a6bbd92276f92f215d3225f3f14ae8a908f034 Signed-off-by: Laurent Smet Signed-off-by: Julien Van Roy --- .../models/account_edi_common.py | 10 ++ .../models/account_edi_xml_cii_facturx.py | 15 +-- .../models/account_edi_xml_ubl_20.py | 19 ++- .../tests/common.py | 29 ++-- .../from_odoo/facturx_test_import_partner.xml | 127 ++++++++++++++++++ .../from_odoo/ubl_test_import_partner.xml | 102 ++++++++++++++ .../tests/test_xml_cii_fr.py | 28 ++++ .../tests/test_xml_ubl_be.py | 28 ++++ 8 files changed, 333 insertions(+), 25 deletions(-) create mode 100644 addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/facturx_test_import_partner.xml create mode 100644 addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/ubl_test_import_partner.xml diff --git a/addons/account_edi_ubl_cii/models/account_edi_common.py b/addons/account_edi_ubl_cii/models/account_edi_common.py index 70e6242dc45..11100bd3c4a 100644 --- a/addons/account_edi_ubl_cii/models/account_edi_common.py +++ b/addons/account_edi_ubl_cii/models/account_edi_common.py @@ -327,6 +327,16 @@ class AccountEdiCommon(models.AbstractModel): return True + def _import_retrieve_and_fill_partner(self, invoice, name, phone, mail, vat): + """ Retrieve the partner, if no matching partner is found, create it + """ + invoice.partner_id = self.env['res.partner']._retrieve_partner(name=name, phone=phone, mail=mail, vat=vat) + if not invoice.partner_id and name: + invoice.partner_id = self.env['res.partner'].create({'name': name, 'email': mail, 'phone': phone}) + country_code = invoice.partner_id.commercial_partner_id.country_code + if vat and self.env['res.partner']._run_vat_test(vat, country_code, invoice.partner_id.is_company): + invoice.partner_id.vat = vat + def _import_fill_invoice_allowance_charge(self, tree, invoice, qty_factor): logs = [] if '{urn:oasis:names:specification:ubl:schema:xsd' in tree.tag: diff --git a/addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py b/addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py index 11671e21803..55f70d7598c 100644 --- a/addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py +++ b/addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py @@ -220,15 +220,12 @@ class AccountEdiXmlCII(models.AbstractModel): # ==== partner_id ==== - partner_type = 'SellerTradeParty' if invoice.journal_id.type == 'purchase' else 'BuyerTradeParty' - invoice.partner_id = self.env['res.partner']._retrieve_partner( - name=_find_value(f"//ram:{partner_type}/ram:Name"), - mail=_find_value(f"//ram:{partner_type}//ram:URIID[@schemeID='SMTP']"), - vat=_find_value(f"//ram:{partner_type}/ram:SpecifiedTaxRegistration/ram:ID"), - company=invoice.company_id, - ) - if not invoice.partner_id: - logs.append(_("Could not retrieve the %s.", _("customer") if invoice.is_sale_document() else _("vendor"))) + role = invoice.journal_id.type == 'purchase' and 'SellerTradeParty' or 'BuyerTradeParty' + name = _find_value(f"//ram:{role}/ram:Name") + mail = _find_value(f"//ram:{role}//ram:URIID[@schemeID='SMTP']") + vat = _find_value(f"//ram:{role}/ram:SpecifiedTaxRegistration/ram:ID") + phone = _find_value(f"//ram:{role}/ram:DefinedTradeContact/ram:TelephoneUniversalCommunication/ram:CompleteNumber") + self._import_retrieve_and_fill_partner(invoice, name=name, phone=phone, mail=mail, vat=vat) # ==== currency_id ==== diff --git a/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py b/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py index e8cda8b4e28..c0b766b08d0 100644 --- a/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py +++ b/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py @@ -2,7 +2,7 @@ from odoo import models, _ from odoo.osv import expression -from odoo.tools import html2plaintext, cleanup_xml_node +from odoo.tools import html2plaintext, cleanup_xml_node, find_xml_value from lxml import etree @@ -466,6 +466,12 @@ class AccountEdiXmlUBL20(models.AbstractModel): # ------------------------------------------------------------------------- def _import_fill_invoice_form(self, invoice, tree, qty_factor): + + def _find_value(xpath, element=tree): + # avoid 'TypeError: empty namespace prefix is not supported in XPath' + nsmap = {k: v for k, v in tree.nsmap.items() if k is not None} + return find_xml_value(xpath, element, nsmap) + logs = [] if qty_factor == -1: @@ -473,11 +479,12 @@ class AccountEdiXmlUBL20(models.AbstractModel): # ==== partner_id ==== - partner = self._import_retrieve_partner(tree, invoice) - if partner: - invoice.partner_id = partner - else: - logs.append(_("Could not retrieve the %s.", _("customer") if invoice.is_sale_document() else _("vendor"))) + role = "Customer" if invoice.journal_id.type == 'sale' else "Supplier" + vat = _find_value(f'//cac:Accounting{role}Party/cac:Party//cbc:CompanyID') + phone = _find_value(f'//cac:Accounting{role}Party/cac:Party//cbc:Telephone') + mail = _find_value(f'//cac:Accounting{role}Party/cac:Party//cbc:ElectronicMail') + name = _find_value(f'//cac:Accounting{role}Party/cac:Party//cbc:Name') + self._import_retrieve_and_fill_partner(invoice, name=name, phone=phone, mail=mail, vat=vat) # ==== currency_id ==== diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py index ea199df5822..44269f62490 100644 --- a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py @@ -95,6 +95,19 @@ class TestUBLCommon(AccountTestInvoicingCommon): self.assertTrue(new_invoice) self.assert_same_invoice(invoice, new_invoice) + def _update_invoice_from_file(self, module_name, subfolder, filename, invoice): + """ Create an attachment from a file and post it on the invoice + """ + file_path = get_module_resource(module_name, subfolder, filename) + with misc.file_open(file_path, 'rb') as file: + attachment = self.env['ir.attachment'].create({ + 'name': filename, + 'datas': base64.encodebytes(file.read()), + 'res_id': invoice.id, + 'res_model': 'account.move', + }) + invoice.message_post(attachment_ids=[attachment.id]) + def _assert_imported_invoice_from_file(self, subfolder, filename, amount_total, amount_tax, list_line_subtotals, list_line_price_unit=None, list_line_discount=None, list_line_taxes=None, move_type='in_invoice', currency_id=None): @@ -116,16 +129,12 @@ class TestUBLCommon(AccountTestInvoicingCommon): invoice_count = len(self.env['account.move'].search([])) - # Import the file to fill the empty invoice - file_path = get_module_resource('l10n_account_edi_ubl_cii_tests', subfolder, filename) - with misc.file_open(file_path, 'rb') as file: - attachment = self.env['ir.attachment'].create({ - 'name': filename, - 'datas': base64.encodebytes(file.read()), - 'res_id': invoice.id, - 'res_model': 'account.move', - }) - invoice.message_post(attachment_ids=[attachment.id]) + self._update_invoice_from_file( + module_name='l10n_account_edi_ubl_cii_tests', + subfolder=subfolder, + filename=filename, + invoice=invoice, + ) # Checks self.assertEqual(len(self.env['account.move'].search([])), invoice_count) diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/facturx_test_import_partner.xml b/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/facturx_test_import_partner.xml new file mode 100644 index 00000000000..1be1f994a64 --- /dev/null +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/facturx_test_import_partner.xml @@ -0,0 +1,127 @@ + + + + + urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended + + + + INV/2017/01/0001 + 380 + + 20170101 + + + test narration + + + + + + 1 + + + product_a + + + + 0.00 + + + 0.00 + + + + 1.0 + + + + 0.00 + + + + + + partner_1 + + partner_1 + + +1 (650) 555-0111 + + + partner1@yourcompany.com + + + + 75000 + Rue Jean Jaurès, 42 + Paris + FR + + + FR05677404089 + + + + Buyer + + Buyer + + 1111 + + + buyer@yahoo.com + + + + FR89215010646 + + + + INV/2017/01/0001 + + + + + Buyer + + Buyer + + 1111 + + + buyer@yahoo.com + + + + + + 20170101 + + + + + INV/2017/01/0001 + USD + + 42 + + FR15001559627230 + + + + 30% Advance End of Following Month + + + 0.00 + 0.00 + 0.00 + 0.00 + 0.00 + 0.00 + + + + diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/ubl_test_import_partner.xml b/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/ubl_test_import_partner.xml new file mode 100644 index 00000000000..17d7e0fa18d --- /dev/null +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/ubl_test_import_partner.xml @@ -0,0 +1,102 @@ + + + 2.1 + urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0 + + urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 + INV/2017/01/0002 + 2017-01-01 + 380 + test narration + USD + Buyer + + test invoice origin + + + + BE0202239951 + + partner_1 + + + Chaussée de Namur 40 + Ramillies + 1367 + + BE + + + + BE0202239951 + + VAT + + + + partner_1 + BE0202239951 + + + partner_1 + + + + + + BE980737405 + + Buyer + + + BE980737405 + + VAT + + + + Buyer + BE980737405 + + + Buyer + 1111 + buyer@yahoo.com + + + + + 30 + +++000/0000/08282+++ + + BE15001559627230 + + + + 30% Advance End of Following Month + + + -0.00 + + + 0.00 + 0.00 + 0.00 + 0.00 + 0.00 + + + 259 + 1.0 + 0.00 + + product_a + product_a + + + 0.00 + + + diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py index 86f3c5914eb..1d97e7e73ae 100644 --- a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py @@ -304,6 +304,34 @@ class TestCIIFR(TestUBLCommon): ) self._test_import_partner(invoice.ubl_cii_xml_id, self.partner_1, self.partner_2) + def test_import_and_create_partner_facturx(self): + """ Tests whether the partner is created at import if no match is found when decoding the EDI attachment + """ + partner_vals = { + 'name': "Buyer", + 'mail': "buyer@yahoo.com", + 'phone': "1111", + 'vat': "FR89215010646", + } + # assert there is no matching partner + partner_match = self.env['res.partner']._retrieve_partner(**partner_vals) + self.assertFalse(partner_match) + + # Import attachment as an invoice + invoice = self.env['account.move'].create({ + 'move_type': 'out_invoice', + 'journal_id': self.company_data['default_journal_sale'].id, + }) + self._update_invoice_from_file( + module_name='l10n_account_edi_ubl_cii_tests', + subfolder='tests/test_files/from_odoo', + filename='facturx_test_import_partner.xml', + invoice=invoice) + + # assert a new partner has been created + partner_vals['email'] = partner_vals.pop('mail') + self.assertRecordValues(invoice.partner_id, [partner_vals]) + def test_import_tax_included(self): """ Tests whether the tax included / tax excluded are correctly decoded when diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py index 580c50d50ba..f12513fb60f 100644 --- a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py @@ -219,6 +219,34 @@ class TestUBLBE(TestUBLCommon): ) self._test_import_partner(invoice.ubl_cii_xml_id, self.partner_1, self.partner_2) + def test_import_and_create_partner_ubl(self): + """ Tests whether the partner is created at import if no match is found when decoding the EDI attachment + """ + partner_vals = { + 'name': "Buyer", + 'mail': "buyer@yahoo.com", + 'phone': "1111", + 'vat': "BE980737405", + } + # assert there is no matching partner + partner_match = self.env['res.partner']._retrieve_partner(**partner_vals) + self.assertFalse(partner_match) + + # Import attachment as an invoice + invoice = self.env['account.move'].create({ + 'move_type': 'out_invoice', + 'journal_id': self.company_data['default_journal_sale'].id, + }) + self._update_invoice_from_file( + module_name='l10n_account_edi_ubl_cii_tests', + subfolder='tests/test_files/from_odoo', + filename='ubl_test_import_partner.xml', + invoice=invoice) + + # assert a new partner has been created + partner_vals['email'] = partner_vals.pop('mail') + self.assertRecordValues(invoice.partner_id, [partner_vals]) + def test_import_export_invoice_xml(self): """ Test whether the elements only specific to ubl_be are correctly exported