From 7a139be9e031ebaaa06f0bdd8e45342d0afc81eb Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sun, 28 Aug 2016 00:24:41 +0200 Subject: [PATCH 01/19] Initial check-in of modules purchase_order_import, purchase_order_import_ubl and purchase_order_ubl --- purchase_order_import_ubl/__init__.py | 3 + purchase_order_import_ubl/__openerp__.py | 16 + purchase_order_import_ubl/demo/demo_data.xml | 194 ++++++++++ purchase_order_import_ubl/tests/__init__.py | 3 + .../tests/files/UBL-Order-2.0-Example.xml | 180 +++++++++ .../tests/files/UBL-Order-2.1-Example.xml | 341 ++++++++++++++++++ .../UBL-RequestForQuotation-2.0-Example.xml | 142 ++++++++ .../UBL-RequestForQuotation-2.1-Example.xml | 180 +++++++++ .../tests/test_ubl_order_import.py | 66 ++++ purchase_order_import_ubl/wizard/__init__.py | 3 + .../wizard/purchase_order_import.py | 98 +++++ 11 files changed, 1226 insertions(+) create mode 100644 purchase_order_import_ubl/__init__.py create mode 100644 purchase_order_import_ubl/__openerp__.py create mode 100644 purchase_order_import_ubl/demo/demo_data.xml create mode 100644 purchase_order_import_ubl/tests/__init__.py create mode 100644 purchase_order_import_ubl/tests/files/UBL-Order-2.0-Example.xml create mode 100644 purchase_order_import_ubl/tests/files/UBL-Order-2.1-Example.xml create mode 100644 purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.0-Example.xml create mode 100644 purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.1-Example.xml create mode 100644 purchase_order_import_ubl/tests/test_ubl_order_import.py create mode 100644 purchase_order_import_ubl/wizard/__init__.py create mode 100644 purchase_order_import_ubl/wizard/purchase_order_import.py diff --git a/purchase_order_import_ubl/__init__.py b/purchase_order_import_ubl/__init__.py new file mode 100644 index 0000000000..3b4c3edf09 --- /dev/null +++ b/purchase_order_import_ubl/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import wizard diff --git a/purchase_order_import_ubl/__openerp__.py b/purchase_order_import_ubl/__openerp__.py new file mode 100644 index 0000000000..ea0f9b7f90 --- /dev/null +++ b/purchase_order_import_ubl/__openerp__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# © 2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Quotation Order UBL Import', + 'version': '8.0.1.0.0', + 'category': 'Purchase Management', + 'license': 'AGPL-3', + 'summary': 'Import UBL XML quotation files', + 'author': 'Akretion,Odoo Community Association (OCA)', + 'website': 'http://www.akretion.com', + 'depends': ['purchase_order_import', 'base_ubl'], + 'demo': ['demo/demo_data.xml'], + 'installable': True, +} diff --git a/purchase_order_import_ubl/demo/demo_data.xml b/purchase_order_import_ubl/demo/demo_data.xml new file mode 100644 index 0000000000..63c0e6488c --- /dev/null +++ b/purchase_order_import_ubl/demo/demo_data.xml @@ -0,0 +1,194 @@ + + + + + + + + SEK pricelist + sale + + + + + + Unique Version + + + + + + Line 0 + + + + Johnssons byggvaror + + + + + 5 Rådhusgatan + PoBox123 + 11000 + Stockholm + + + + + + + Pelle Svensson + Boss + pelle@johnsson.se + + + + + + + Swedish trucking + + + + bill@svetruck.se + 5 Rådhusgatan + 2nd floor + 11000 + Stockholm + + + + + Falu Rödfärg + Red paint + SItemNo001 + 55 + consu + + + + + + Pensel 20 mm + Very good pencils for red paint + SItemNo011 + 18 + consu + + + + + IYT pricelist + sale + + + + + + Unique Version + + + + + + Line 0 + + + + IYT Corporation + + + + + 56A Avon Way + Thereabouts + ZZ99 1ZZ + Bridgtow + + + + + + + Fred Churchill + fred@iytcorporation.gov.uk + + + + + + + Beeswax + Acme beeswax + 17589683 + 42 + consu + + + + + + + The Terminus + + + + + 56A Avon Way + Thereabouts + ZZ99 1ZZ + Bridgtow + + + + + + S Massiah + smassiah@the-email.co.uk + + + + + + + + DKK pricelist + sale + + + + + + Unique Version + + + + + + Line 0 + + + + Gentofte Kommune + + + + + 161 Bernstorffsvej + 2920 + Charlottenlund + + + + + + + Joe Delivery + + + delivery + + + + + diff --git a/purchase_order_import_ubl/tests/__init__.py b/purchase_order_import_ubl/tests/__init__.py new file mode 100644 index 0000000000..517f136e78 --- /dev/null +++ b/purchase_order_import_ubl/tests/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import test_ubl_order_import diff --git a/purchase_order_import_ubl/tests/files/UBL-Order-2.0-Example.xml b/purchase_order_import_ubl/tests/files/UBL-Order-2.0-Example.xml new file mode 100644 index 0000000000..953299e59f --- /dev/null +++ b/purchase_order_import_ubl/tests/files/UBL-Order-2.0-Example.xml @@ -0,0 +1,180 @@ + + + 2.0 + urn:oasis:names:specification:ubl:xpath:Order-2.0:sbs-1.0-draft + bpid:urn:oasis:names:draft:bpss:ubl-2-sbs-order-with-simple-response-draft + AEG012345 + CON0095678 + false + 6E09886B-DC6E-439F-82D1-7CCAC7F4E3B1 + 2010-06-20 + sample + + XFB01 + GT00978567 + + + IYT Corporation + + + Avon Way + Thereabouts + 56A + Bridgtow + ZZ99 1ZZ + Avon + + 3rd Floor, Room 5 + + + GB + + + + Bridgtow District Council + 12356478 + Local Authority + + UK VAT + VAT + + + + Mr Fred Churchill + 0127 2653214 + 0127 2653215 + fred@iytcorporation.gov.uk + + + + + CO001 + + + Consortial + + + Busy Street + Thereabouts + 56A + Farthing + AA99 1BB + Heremouthshire + + The Roundabout + + + GB + + + + Farthing Purchasing Consortia + 175 269 2355 + N/A + + VAT + VAT + + + + Mrs Bouquet + 0158 1233714 + 0158 1233856 + bouquet@fpconsortial.co.uk + + + + + + + The Terminus + + + Avon Way + Thereabouts + 56A + Bridgtow + ZZ99 1ZZ + Avon + + 3rd Floor, Room 5 + + + GB + + + + Bridgtow District Council + 12356478 + Local Authority + + UK VAT + VAT + + + + S Massiah + 0127 98876545 + 0127 98876546 + smassiah@the-email.co.uk + + + + + + Avon Way + Thereabouts + 56A + Bridgtow + ZZ99 1ZZ + Avon + + 3rd Floor, Room 5 + + + GB + + + + 2005-06-29 + 09:30:47.0Z + 2005-06-29 + 09:30:47.0Z + + + + 1% deduction for late delivery as per contract + + + order response required; payment is by BACS or by cheque + + + 100.00 + 100.00 + + + this is an illustrative order line + + 1 + A + NoStatus + 100 + 100.00 + 17.50 + + 100.00 + 1 + + + Acme beeswax + beeswax + + 6578489 + + + 17589683 + + + + + diff --git a/purchase_order_import_ubl/tests/files/UBL-Order-2.1-Example.xml b/purchase_order_import_ubl/tests/files/UBL-Order-2.1-Example.xml new file mode 100644 index 0000000000..9c2386be07 --- /dev/null +++ b/purchase_order_import_ubl/tests/files/UBL-Order-2.1-Example.xml @@ -0,0 +1,341 @@ + + + 2.1 + urn:www.cenbii.eu:transaction:biicoretrdm001:ver1.0 + urn:www.cenbii.eu:profile:BII01:ver1.0 + 34 + 2010-01-20 + 12:30:00 + Information text for the whole order + SEK + Project123 + + 2010-01-31 + + + QuoteID123 + + + RjectedOrderID123 + + + MAFO + + + Doc1 + Timesheet + + + http://www.suppliersite.eu/sheet001.html + + + + + Doc2 + Drawing + + UjBsR09EbGhjZ0dTQUxNQUFBUUNBRU1tQ1p0dU1GUXhEUzhi + + + + 34322 + FrameworkAgreementID123 + + + + 7300072311115 + + 7300070011115 + + + PartyID123 + + + Johnssons byggvaror + + + 1234567890123 + PoBox123 + Rådhusgatan + 2nd floor + 5 + Purchasing department + Stockholm + 11000 + RegionX + + SE + + + + Herra Johnssons byggvaror AS + SE1234567801 + + Stockholm + + SE + + + + VAT + + + + Johnssons Byggvaror AB + 5532331183 + + Stockholm + RegionX + + SE + + + + + 123456 + 123456 + pelle@johnsson.se + + + Pelle + Svensson + X + Boss + + + + Eva Johnsson + 1234356 + 123455 + eva@johnsson.se + + + + + 7302347231111 + + SellerPartyID123 + + + Moderna Produkter AB + + + 0987654321123 + 321 + Kungsgatan + suite12 + 22 + Sales department + Stockholm + 11000 + RegionX + + SE + + + + Moderna Produkter AB + 5532332283 + + Stockholm + RegionX + + SE + + + + + 34557 + 3456767 + lars@moderna.se + + + Lars + Petersen + M + Sales manager + + + + + + + 0987678321123 + + + Moderna Produkter AB + + + 346788 + 8567443 + sven@moderna.se + + + Sven + Pereson + N + Stuffuser + + + + + + + 1234567890123 + 123 + Rådhusgatan + 2nd floor + 5 + Purchasing department + Stockholm + 11000 + RegionX + + SE + + + + + 2010-02-10 + 2010-02-25 + + + + 67654328394567 + + + Swedish trucking + + + Per + 987098709 + 34673435 + bill@svetruck.se + + + + + FOT + CAD + + STO + + + + true + Transport documents + 100 + + + false + Total order value discount + 100 + + + 100 + + + 6225 + 100 + 100 + 6225 + + + Freetext note on line 1 + + 1 + 120 + 6000 + 10 + false + ProjectID123 + + + 2010-02-10 + 2010-02-25 + + + + + EmployeeXXX + + + Josef K. + + + + 50 + 1 + + + Red paint + Falu Rödfärg + + SItemNo001 + + + 1234567890123 + + + Paint type + Acrylic + + + Solvant + Water + + + + + + Freetext note on line 2 + + 2 + 15 + 225 + 10 + false + ProjectID123 + + + 2010-02-10 + 2010-02-25 + + + + + EmployeeXXX + + + Josef K. + + + + 15 + 1 + + + Very good pencils for red paint. + Pensel 20 mm + + SItemNo011 + + + 123452340123 + + + Hair color + Black + + + Width + 20mm + + + + + diff --git a/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.0-Example.xml b/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.0-Example.xml new file mode 100644 index 0000000000..82f49dc649 --- /dev/null +++ b/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.0-Example.xml @@ -0,0 +1,142 @@ + + + 2.0 + urn:oasis:names:specification:ubl:xpath:RequestForQuotation-2.0:sbs-1.0-draft + bpid:urn:oasis:names:draft:bpss:ubl-2-sbs-request-for-quotation-draft + G867B + false + 8D076867-AE6D-439F-8281-5AAFC7F4E3B1 + 2005-06-19 + 11:32:26.0Z + sample + + 2005-9A + 2005-11-03 + + + + + The Terminus + + + Avon Way + Thereabouts + 56A + Bridgtow + ZZ99 1ZZ + Avon + + 3rd Floor, Room 5 + + + GB + + + + Bridgtow District Council + 12356478 + Local Authority + + UK VAT + VAT + + + + S Massiah + 0127 98876545 + 0127 98876546 + smassiah@the-email.co.uk + + + + + CO001 + + + Consortial + + + Busy Street + Thereabouts + 56A + Farthing + AA99 1BB + Heremouthshire + + The Roundabout + + + GB + + + + Farthing Purchasing Consortium + 175 269 2355 + N/A + + VAT + VAT + + + + Mrs Bouquet + 0158 1233714 + 0158 1233856 + bouquet@fpconsortial.co.uk + + + + + + Avon Way + Thereabouts + 56A + Bridgtow + ZZ99 1ZZ + Avon + + 3rd Floor, Room 5 + + + GB + + + + 2005-06-29 + 09:30:47.0Z + 2005-06-29 + 09:30:47.0Z + + + + 1% deduction for late delivery as per contract + + + GB + Great Britain + + + + GHJ76849 + 2002-08-13 + + + + 1 + sample + + 1 + 100 + + Acme beeswax + beeswax + + 6578489 + + + 17589683 + + + + + diff --git a/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.1-Example.xml b/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.1-Example.xml new file mode 100644 index 0000000000..b5b8a809fc --- /dev/null +++ b/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.1-Example.xml @@ -0,0 +1,180 @@ + + + + + + + 2.1 + OIOUBL-2.1 + Procurement-QuoSim-1.0 + G867B + false + 93T5G3G5-HYA3-7267-BVG3-GS46SW44WG53 + 2010-04-19 + 11:32:26.0Z + 2008-04-24 + Bestilling af computere + DKK + + + 5798000416604 + + 5798000416604 + + + Gentofte Kommune + + + StructuredDK + Bernstorffsvej + 161 + Charlottenlund + 2920 + + DK + + + + DK12345678 + + 63 + Moms + + + + Gentofte Kommune + DK12345678 + + + 12345678 + Sille Schyberg + + + + + LEV00123 + + DK18296799 + + DK18296799 + + + Delcomputer A/S + + + StructuredDK + Arne Jacobsens Allé + 15 + København S + 2300 + + DK + + + + DK18296799 + + 63 + Moms + + + + Delcomputer A/S + DK18296799 + + + + + + StructuredDK + Bernstorffsvej + 161 + Charlottenlund + 2920 + + 1. sal + + + IT-afdelingen + + + DK + + + + 2008-05-06 + 09:30:47.0Z + 2008-05-10 + 09:30:47.0Z + + + + 1% reduktion i kontraktsummen pr. dags forsinkelse jf. SKI kontrakt + + + + SKI123456 + 2006-01-01 + + + + 1 + Computer + + DELL1052665 + 35 + + Stationær computer + Dell PrecisionTM T3400 + + LAP-E5 + + + + + + 2 + Skærm + + DELL2363463 + 35 + + Fladskærm + FP/BL 1908WFP + + CARD + + + + + + 3 + Mus + + DELL2367452 + 35 + + Mus + Dell Quietkey USB-tastatur, sort - Dansk (QWERTY) + + AK789 + + + + + + 4 + Tastatur + + DELL8436783 + 35 + + Tastatur + Dell Quietkey USB-tastatur, sort - Dansk (QWERTY) + + AK789 + + + + + diff --git a/purchase_order_import_ubl/tests/test_ubl_order_import.py b/purchase_order_import_ubl/tests/test_ubl_order_import.py new file mode 100644 index 0000000000..e38c3d236a --- /dev/null +++ b/purchase_order_import_ubl/tests/test_ubl_order_import.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# © 2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.tests.common import TransactionCase +from openerp.tools import file_open +import base64 + + +class TestUblOrderImport(TransactionCase): + + def test_ubl_order_import(self): + tests = { + 'UBL-Order-2.1-Example.xml': { + 'client_order_ref': '34', + 'date_order': '2010-01-20', + 'partner': self.env.ref('sale_order_import_ubl.johnssons'), + 'shipping_partner': + self.env.ref('sale_order_import_ubl.swedish_trucking'), + 'currency': self.env.ref('base.SEK'), + }, + 'UBL-Order-2.0-Example.xml': { + 'client_order_ref': 'AEG012345', + 'date_order': '2010-06-20', + 'partner': self.env.ref('sale_order_import_ubl.iyt'), + 'shipping_partner': + self.env.ref('sale_order_import_ubl.fred_churchill'), + 'currency': self.env.ref('base.GBP'), + }, + 'UBL-RequestForQuotation-2.0-Example.xml': { + 'partner': self.env.ref('sale_order_import_ubl.terminus'), + 'shipping_partner': + self.env.ref('sale_order_import_ubl.s_massiah'), + }, + 'UBL-RequestForQuotation-2.1-Example.xml': { + 'partner': + self.env.ref('sale_order_import_ubl.gentofte_kommune'), + 'currency': self.env.ref('base.DKK'), + 'shipping_partner': + self.env.ref( + 'sale_order_import_ubl.delivery_gentofte_kommune'), + }, + } + for filename, res in tests.iteritems(): + f = file_open( + 'sale_order_import_ubl/tests/files/' + filename, 'rb') + xml_file = f.read() + wiz = self.env['sale.order.import'].create({ + 'order_file': base64.b64encode(xml_file), + 'order_filename': filename, + }) + f.close() + action = wiz.import_order_button() + so = self.env['sale.order'].browse(action['res_id']) + self.assertEqual( + so.partner_id.commercial_partner_id, + res['partner']) + if res.get('currency'): + self.assertEqual(so.currency_id, res['currency']) + if res.get('client_order_ref'): + self.assertEqual(so.client_order_ref, res['client_order_ref']) + if res.get('date_order'): + self.assertEqual(so.date_order[:10], res['date_order']) + if res.get('shipping_partner'): + self.assertEqual( + so.partner_shipping_id, res['shipping_partner']) diff --git a/purchase_order_import_ubl/wizard/__init__.py b/purchase_order_import_ubl/wizard/__init__.py new file mode 100644 index 0000000000..627d4faa91 --- /dev/null +++ b/purchase_order_import_ubl/wizard/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import purchase_order_import diff --git a/purchase_order_import_ubl/wizard/purchase_order_import.py b/purchase_order_import_ubl/wizard/purchase_order_import.py new file mode 100644 index 0000000000..877604ffb7 --- /dev/null +++ b/purchase_order_import_ubl/wizard/purchase_order_import.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# © 2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, api +from openerp.tools import float_is_zero +import logging + +logger = logging.getLogger(__name__) + + +class PurchaseOrderImport(models.TransientModel): + _name = 'purchase.order.import' + _inherit = ['purchase.order.import', 'base.ubl'] + + @api.model + def parse_xml_quote(self, xml_root): + start_tag = '{urn:oasis:names:specification:ubl:schema:xsd:' + if xml_root.tag == start_tag + 'Quotation-2}Quotation': + return self.parse_ubl_quote(xml_root) + else: + return super(PurchaseOrderImport, self).parse_xml_order(xml_root) + + @api.model + def parse_ubl_quote_line(self, line, ns): + qty_prec = self.env['decimal.precision'].precision_get( + 'Product Unit of Measure') + line_item = line.xpath('cac:LineItem', namespaces=ns)[0] + # line_id_xpath = line_item.xpath('cbc:ID', namespaces=ns) + # line_id = line_id_xpath[0].text + qty_xpath = line_item.xpath('cbc:Quantity', namespaces=ns) + qty = float(qty_xpath[0].text) + price_unit = 0.0 + subtotal_without_tax_xpath = line_item.xpath( + 'cbc:LineExtensionAmount', namespaces=ns) + if subtotal_without_tax_xpath: + subtotal_without_tax = float(subtotal_without_tax_xpath[0].text) + if not float_is_zero(qty, precision_digits=qty_prec): + price_unit = subtotal_without_tax / qty + else: + price_xpath = line_item.xpath( + 'cac:Price/cbc:PriceAmount', namespaces=ns) + if price_xpath: + price_unit = float(price_xpath[0].text) + res_line = { + 'product': self.ubl_parse_product(line_item, ns), + 'qty': qty, + 'uom': {'unece_code': qty_xpath[0].attrib.get('unitCode')}, + 'price_unit': price_unit, + } + return res_line + + @api.model + def parse_ubl_quote(self, xml_root): + ns = xml_root.nsmap + main_xmlns = ns.pop(None) + ns['main'] = main_xmlns + date_xpath = xml_root.xpath( + '/main:Quotation/cbc:IssueDate', namespaces=ns) + currency_xpath = xml_root.xpath( + '/main:Quotation/cbc:PricingCurrencyCode', namespaces=ns) + currency_code = False + if currency_xpath: + currency_code = currency_xpath[0].text + else: + currency_xpath = xml_root.xpath( + '//cbc:LineExtensionAmount', namespaces=ns) + if currency_xpath: + currency_code = currency_xpath[0].attrib.get('currencyID') + supplier_xpath = xml_root.xpath( + '/main:Quotation/cac:SellerSupplierParty', namespaces=ns) + supplier_dict = self.ubl_parse_supplier_party(supplier_xpath[0], ns) + delivery_term_xpath = xml_root.xpath( + "/main:Quotation/cac:DeliveryTerms", namespaces=ns) + if delivery_term_xpath: + incoterm_dict = self.ubl_parse_incoterm(delivery_term_xpath[0], ns) + else: + incoterm_dict = {} + note_xpath = xml_root.xpath( + '/main:Quotation/cbc:Note', namespaces=ns) + lines_xpath = xml_root.xpath( + '/main:Quotation/cac:QuotationLine', namespaces=ns) + res_lines = [] + for line in lines_xpath: + res_lines.append(self.parse_ubl_quote_line(line, ns)) + # TODO : add charges + res = { + 'partner': supplier_dict, + 'currency': {'iso': currency_code}, + 'date': date_xpath[0].text, + 'incoterm': incoterm_dict, + 'note': note_xpath and note_xpath[0].text or False, + 'lines': res_lines, + } + # Stupid hack to remove invalid VAT of sample files + if res['partner']['vat'] in ['DK18296799']: + res['partner'].pop('vat') + return res From 4fc8e4c2d1aa8a4beb00be70f8aa5f2cd87a5ed1 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sun, 4 Sep 2016 21:31:38 +0200 Subject: [PATCH 02/19] FIX copy-paste error in purchase_order_import_ubl: this module now really has unittests --- purchase_order_import_ubl/README.rst | 59 +++ .../tests/files/UBL-Order-2.0-Example.xml | 180 --------- .../tests/files/UBL-Order-2.1-Example.xml | 341 ------------------ .../UBL-RequestForQuotation-2.0-Example.xml | 142 -------- .../UBL-RequestForQuotation-2.1-Example.xml | 180 --------- .../tests/files/quote-PO00004.pdf | Bin 0 -> 27426 bytes .../tests/test_ubl_order_import.py | 64 +--- 7 files changed, 75 insertions(+), 891 deletions(-) create mode 100644 purchase_order_import_ubl/README.rst delete mode 100644 purchase_order_import_ubl/tests/files/UBL-Order-2.0-Example.xml delete mode 100644 purchase_order_import_ubl/tests/files/UBL-Order-2.1-Example.xml delete mode 100644 purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.0-Example.xml delete mode 100644 purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.1-Example.xml create mode 100644 purchase_order_import_ubl/tests/files/quote-PO00004.pdf diff --git a/purchase_order_import_ubl/README.rst b/purchase_order_import_ubl/README.rst new file mode 100644 index 0000000000..493fd4d764 --- /dev/null +++ b/purchase_order_import_ubl/README.rst @@ -0,0 +1,59 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +========================= +Purchase Order Import UBL +========================= + +This module adds support for the import of electronic quotations that comply with the `Universal Business Language (UBL) `_ standard. The UBL standard became the `ISO/IEC 19845 `_ standard in December 2015 (cf the `official announce _`). The file can be in two formats: + +* UBL XML file, +* PDF file with an embedded UBL XML file. + +You can use the OCA module *sale_order_ubl* to generate PDF quotations with an embedded UBL XML file. + +Configuration +============= + +No configuration is needed. + +Usage +===== + +Refer to the README.rst of the module *purchase_order_import* for a detailed usage description. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/142/8.0 + +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 smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Alexis de Lattre + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/purchase_order_import_ubl/tests/files/UBL-Order-2.0-Example.xml b/purchase_order_import_ubl/tests/files/UBL-Order-2.0-Example.xml deleted file mode 100644 index 953299e59f..0000000000 --- a/purchase_order_import_ubl/tests/files/UBL-Order-2.0-Example.xml +++ /dev/null @@ -1,180 +0,0 @@ - - - 2.0 - urn:oasis:names:specification:ubl:xpath:Order-2.0:sbs-1.0-draft - bpid:urn:oasis:names:draft:bpss:ubl-2-sbs-order-with-simple-response-draft - AEG012345 - CON0095678 - false - 6E09886B-DC6E-439F-82D1-7CCAC7F4E3B1 - 2010-06-20 - sample - - XFB01 - GT00978567 - - - IYT Corporation - - - Avon Way - Thereabouts - 56A - Bridgtow - ZZ99 1ZZ - Avon - - 3rd Floor, Room 5 - - - GB - - - - Bridgtow District Council - 12356478 - Local Authority - - UK VAT - VAT - - - - Mr Fred Churchill - 0127 2653214 - 0127 2653215 - fred@iytcorporation.gov.uk - - - - - CO001 - - - Consortial - - - Busy Street - Thereabouts - 56A - Farthing - AA99 1BB - Heremouthshire - - The Roundabout - - - GB - - - - Farthing Purchasing Consortia - 175 269 2355 - N/A - - VAT - VAT - - - - Mrs Bouquet - 0158 1233714 - 0158 1233856 - bouquet@fpconsortial.co.uk - - - - - - - The Terminus - - - Avon Way - Thereabouts - 56A - Bridgtow - ZZ99 1ZZ - Avon - - 3rd Floor, Room 5 - - - GB - - - - Bridgtow District Council - 12356478 - Local Authority - - UK VAT - VAT - - - - S Massiah - 0127 98876545 - 0127 98876546 - smassiah@the-email.co.uk - - - - - - Avon Way - Thereabouts - 56A - Bridgtow - ZZ99 1ZZ - Avon - - 3rd Floor, Room 5 - - - GB - - - - 2005-06-29 - 09:30:47.0Z - 2005-06-29 - 09:30:47.0Z - - - - 1% deduction for late delivery as per contract - - - order response required; payment is by BACS or by cheque - - - 100.00 - 100.00 - - - this is an illustrative order line - - 1 - A - NoStatus - 100 - 100.00 - 17.50 - - 100.00 - 1 - - - Acme beeswax - beeswax - - 6578489 - - - 17589683 - - - - - diff --git a/purchase_order_import_ubl/tests/files/UBL-Order-2.1-Example.xml b/purchase_order_import_ubl/tests/files/UBL-Order-2.1-Example.xml deleted file mode 100644 index 9c2386be07..0000000000 --- a/purchase_order_import_ubl/tests/files/UBL-Order-2.1-Example.xml +++ /dev/null @@ -1,341 +0,0 @@ - - - 2.1 - urn:www.cenbii.eu:transaction:biicoretrdm001:ver1.0 - urn:www.cenbii.eu:profile:BII01:ver1.0 - 34 - 2010-01-20 - 12:30:00 - Information text for the whole order - SEK - Project123 - - 2010-01-31 - - - QuoteID123 - - - RjectedOrderID123 - - - MAFO - - - Doc1 - Timesheet - - - http://www.suppliersite.eu/sheet001.html - - - - - Doc2 - Drawing - - UjBsR09EbGhjZ0dTQUxNQUFBUUNBRU1tQ1p0dU1GUXhEUzhi - - - - 34322 - FrameworkAgreementID123 - - - - 7300072311115 - - 7300070011115 - - - PartyID123 - - - Johnssons byggvaror - - - 1234567890123 - PoBox123 - Rådhusgatan - 2nd floor - 5 - Purchasing department - Stockholm - 11000 - RegionX - - SE - - - - Herra Johnssons byggvaror AS - SE1234567801 - - Stockholm - - SE - - - - VAT - - - - Johnssons Byggvaror AB - 5532331183 - - Stockholm - RegionX - - SE - - - - - 123456 - 123456 - pelle@johnsson.se - - - Pelle - Svensson - X - Boss - - - - Eva Johnsson - 1234356 - 123455 - eva@johnsson.se - - - - - 7302347231111 - - SellerPartyID123 - - - Moderna Produkter AB - - - 0987654321123 - 321 - Kungsgatan - suite12 - 22 - Sales department - Stockholm - 11000 - RegionX - - SE - - - - Moderna Produkter AB - 5532332283 - - Stockholm - RegionX - - SE - - - - - 34557 - 3456767 - lars@moderna.se - - - Lars - Petersen - M - Sales manager - - - - - - - 0987678321123 - - - Moderna Produkter AB - - - 346788 - 8567443 - sven@moderna.se - - - Sven - Pereson - N - Stuffuser - - - - - - - 1234567890123 - 123 - Rådhusgatan - 2nd floor - 5 - Purchasing department - Stockholm - 11000 - RegionX - - SE - - - - - 2010-02-10 - 2010-02-25 - - - - 67654328394567 - - - Swedish trucking - - - Per - 987098709 - 34673435 - bill@svetruck.se - - - - - FOT - CAD - - STO - - - - true - Transport documents - 100 - - - false - Total order value discount - 100 - - - 100 - - - 6225 - 100 - 100 - 6225 - - - Freetext note on line 1 - - 1 - 120 - 6000 - 10 - false - ProjectID123 - - - 2010-02-10 - 2010-02-25 - - - - - EmployeeXXX - - - Josef K. - - - - 50 - 1 - - - Red paint - Falu Rödfärg - - SItemNo001 - - - 1234567890123 - - - Paint type - Acrylic - - - Solvant - Water - - - - - - Freetext note on line 2 - - 2 - 15 - 225 - 10 - false - ProjectID123 - - - 2010-02-10 - 2010-02-25 - - - - - EmployeeXXX - - - Josef K. - - - - 15 - 1 - - - Very good pencils for red paint. - Pensel 20 mm - - SItemNo011 - - - 123452340123 - - - Hair color - Black - - - Width - 20mm - - - - - diff --git a/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.0-Example.xml b/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.0-Example.xml deleted file mode 100644 index 82f49dc649..0000000000 --- a/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.0-Example.xml +++ /dev/null @@ -1,142 +0,0 @@ - - - 2.0 - urn:oasis:names:specification:ubl:xpath:RequestForQuotation-2.0:sbs-1.0-draft - bpid:urn:oasis:names:draft:bpss:ubl-2-sbs-request-for-quotation-draft - G867B - false - 8D076867-AE6D-439F-8281-5AAFC7F4E3B1 - 2005-06-19 - 11:32:26.0Z - sample - - 2005-9A - 2005-11-03 - - - - - The Terminus - - - Avon Way - Thereabouts - 56A - Bridgtow - ZZ99 1ZZ - Avon - - 3rd Floor, Room 5 - - - GB - - - - Bridgtow District Council - 12356478 - Local Authority - - UK VAT - VAT - - - - S Massiah - 0127 98876545 - 0127 98876546 - smassiah@the-email.co.uk - - - - - CO001 - - - Consortial - - - Busy Street - Thereabouts - 56A - Farthing - AA99 1BB - Heremouthshire - - The Roundabout - - - GB - - - - Farthing Purchasing Consortium - 175 269 2355 - N/A - - VAT - VAT - - - - Mrs Bouquet - 0158 1233714 - 0158 1233856 - bouquet@fpconsortial.co.uk - - - - - - Avon Way - Thereabouts - 56A - Bridgtow - ZZ99 1ZZ - Avon - - 3rd Floor, Room 5 - - - GB - - - - 2005-06-29 - 09:30:47.0Z - 2005-06-29 - 09:30:47.0Z - - - - 1% deduction for late delivery as per contract - - - GB - Great Britain - - - - GHJ76849 - 2002-08-13 - - - - 1 - sample - - 1 - 100 - - Acme beeswax - beeswax - - 6578489 - - - 17589683 - - - - - diff --git a/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.1-Example.xml b/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.1-Example.xml deleted file mode 100644 index b5b8a809fc..0000000000 --- a/purchase_order_import_ubl/tests/files/UBL-RequestForQuotation-2.1-Example.xml +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - 2.1 - OIOUBL-2.1 - Procurement-QuoSim-1.0 - G867B - false - 93T5G3G5-HYA3-7267-BVG3-GS46SW44WG53 - 2010-04-19 - 11:32:26.0Z - 2008-04-24 - Bestilling af computere - DKK - - - 5798000416604 - - 5798000416604 - - - Gentofte Kommune - - - StructuredDK - Bernstorffsvej - 161 - Charlottenlund - 2920 - - DK - - - - DK12345678 - - 63 - Moms - - - - Gentofte Kommune - DK12345678 - - - 12345678 - Sille Schyberg - - - - - LEV00123 - - DK18296799 - - DK18296799 - - - Delcomputer A/S - - - StructuredDK - Arne Jacobsens Allé - 15 - København S - 2300 - - DK - - - - DK18296799 - - 63 - Moms - - - - Delcomputer A/S - DK18296799 - - - - - - StructuredDK - Bernstorffsvej - 161 - Charlottenlund - 2920 - - 1. sal - - - IT-afdelingen - - - DK - - - - 2008-05-06 - 09:30:47.0Z - 2008-05-10 - 09:30:47.0Z - - - - 1% reduktion i kontraktsummen pr. dags forsinkelse jf. SKI kontrakt - - - - SKI123456 - 2006-01-01 - - - - 1 - Computer - - DELL1052665 - 35 - - Stationær computer - Dell PrecisionTM T3400 - - LAP-E5 - - - - - - 2 - Skærm - - DELL2363463 - 35 - - Fladskærm - FP/BL 1908WFP - - CARD - - - - - - 3 - Mus - - DELL2367452 - 35 - - Mus - Dell Quietkey USB-tastatur, sort - Dansk (QWERTY) - - AK789 - - - - - - 4 - Tastatur - - DELL8436783 - 35 - - Tastatur - Dell Quietkey USB-tastatur, sort - Dansk (QWERTY) - - AK789 - - - - - diff --git a/purchase_order_import_ubl/tests/files/quote-PO00004.pdf b/purchase_order_import_ubl/tests/files/quote-PO00004.pdf new file mode 100644 index 0000000000000000000000000000000000000000..012de43143ccccfb3b30bd34ce6b68df0a00a117 GIT binary patch literal 27426 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 U%8NWKk|{-U|M2jUDWm-V3*zCY`Tzg` literal 0 HcmV?d00001 diff --git a/purchase_order_import_ubl/tests/test_ubl_order_import.py b/purchase_order_import_ubl/tests/test_ubl_order_import.py index e38c3d236a..90a1b51b65 100644 --- a/purchase_order_import_ubl/tests/test_ubl_order_import.py +++ b/purchase_order_import_ubl/tests/test_ubl_order_import.py @@ -11,56 +11,24 @@ class TestUblOrderImport(TransactionCase): def test_ubl_order_import(self): tests = { - 'UBL-Order-2.1-Example.xml': { - 'client_order_ref': '34', - 'date_order': '2010-01-20', - 'partner': self.env.ref('sale_order_import_ubl.johnssons'), - 'shipping_partner': - self.env.ref('sale_order_import_ubl.swedish_trucking'), - 'currency': self.env.ref('base.SEK'), - }, - 'UBL-Order-2.0-Example.xml': { - 'client_order_ref': 'AEG012345', - 'date_order': '2010-06-20', - 'partner': self.env.ref('sale_order_import_ubl.iyt'), - 'shipping_partner': - self.env.ref('sale_order_import_ubl.fred_churchill'), - 'currency': self.env.ref('base.GBP'), - }, - 'UBL-RequestForQuotation-2.0-Example.xml': { - 'partner': self.env.ref('sale_order_import_ubl.terminus'), - 'shipping_partner': - self.env.ref('sale_order_import_ubl.s_massiah'), - }, - 'UBL-RequestForQuotation-2.1-Example.xml': { - 'partner': - self.env.ref('sale_order_import_ubl.gentofte_kommune'), - 'currency': self.env.ref('base.DKK'), - 'shipping_partner': - self.env.ref( - 'sale_order_import_ubl.delivery_gentofte_kommune'), + 'quote-PO00004.pdf': { + 'po_to_update': self.env.ref('purchase.purchase_order_4'), + 'incoterm': self.env.ref('stock.incoterm_DDU'), }, } + poio = self.env['purchase.order.import'] for filename, res in tests.iteritems(): + po = res['po_to_update'] + f = file_open( - 'sale_order_import_ubl/tests/files/' + filename, 'rb') - xml_file = f.read() - wiz = self.env['sale.order.import'].create({ - 'order_file': base64.b64encode(xml_file), - 'order_filename': filename, - }) + 'purchase_order_import_ubl/tests/files/' + filename, 'rb') + quote_file = f.read() + wiz = poio.with_context( + active_model='purchase.order', active_id=po.id).create({ + 'quote_file': base64.b64encode(quote_file), + 'quote_filename': filename, + }) f.close() - action = wiz.import_order_button() - so = self.env['sale.order'].browse(action['res_id']) - self.assertEqual( - so.partner_id.commercial_partner_id, - res['partner']) - if res.get('currency'): - self.assertEqual(so.currency_id, res['currency']) - if res.get('client_order_ref'): - self.assertEqual(so.client_order_ref, res['client_order_ref']) - if res.get('date_order'): - self.assertEqual(so.date_order[:10], res['date_order']) - if res.get('shipping_partner'): - self.assertEqual( - so.partner_shipping_id, res['shipping_partner']) + self.assertEqual(wiz.purchase_id, po) + wiz.update_rfq_button() + self.assertEqual(po.incoterm_id, res['incoterm']) From 36c5919441a63d0020946184f952d0d9c16f8f29 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 18 Oct 2016 23:02:55 +0200 Subject: [PATCH 03/19] 8.0 Add support for partner bank matching on invoice update (#6) Add support for partner bank matching on invoice update (before, it was only supported on invoice creation) --- purchase_order_import_ubl/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/purchase_order_import_ubl/README.rst b/purchase_order_import_ubl/README.rst index 493fd4d764..ee9df0038a 100644 --- a/purchase_order_import_ubl/README.rst +++ b/purchase_order_import_ubl/README.rst @@ -25,13 +25,13 @@ Refer to the README.rst of the module *purchase_order_import* for a detailed usa .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/142/8.0 + :target: https://runbot.odoo-community.org/runbot/226/8.0 Bug Tracker =========== Bugs are tracked on `GitHub Issues -`_. In case of trouble, please +`_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed feedback. From be662dbdd2785848b6a794e930bd22516ba586e2 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 15 Feb 2017 15:11:22 +0100 Subject: [PATCH 04/19] Prepare v10 branch Rename __openerp__.py to __manifest__.py and set installable to False --- purchase_order_import_ubl/{__openerp__.py => __manifest__.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename purchase_order_import_ubl/{__openerp__.py => __manifest__.py} (95%) diff --git a/purchase_order_import_ubl/__openerp__.py b/purchase_order_import_ubl/__manifest__.py similarity index 95% rename from purchase_order_import_ubl/__openerp__.py rename to purchase_order_import_ubl/__manifest__.py index ea0f9b7f90..b70910b842 100644 --- a/purchase_order_import_ubl/__openerp__.py +++ b/purchase_order_import_ubl/__manifest__.py @@ -12,5 +12,5 @@ 'website': 'http://www.akretion.com', 'depends': ['purchase_order_import', 'base_ubl'], 'demo': ['demo/demo_data.xml'], - 'installable': True, + 'installable': False, } From d324150b728c337dda1219854184732478cc9227 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 28 Feb 2017 21:51:43 +0100 Subject: [PATCH 05/19] Port purchase_order_import* to v10.0 Add ubl invoice generation option in accounting config page --- purchase_order_import_ubl/README.rst | 2 +- purchase_order_import_ubl/__manifest__.py | 6 +- purchase_order_import_ubl/demo/demo_data.xml | 75 +++--------------- .../tests/files/quote-PO00004.pdf | Bin 27426 -> 36962 bytes .../tests/test_ubl_order_import.py | 6 +- .../wizard/purchase_order_import.py | 6 +- 6 files changed, 20 insertions(+), 75 deletions(-) diff --git a/purchase_order_import_ubl/README.rst b/purchase_order_import_ubl/README.rst index ee9df0038a..efde43328d 100644 --- a/purchase_order_import_ubl/README.rst +++ b/purchase_order_import_ubl/README.rst @@ -25,7 +25,7 @@ Refer to the README.rst of the module *purchase_order_import* for a detailed usa .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/226/8.0 + :target: https://runbot.odoo-community.org/runbot/226/10.0 Bug Tracker =========== diff --git a/purchase_order_import_ubl/__manifest__.py b/purchase_order_import_ubl/__manifest__.py index b70910b842..d4733c016a 100644 --- a/purchase_order_import_ubl/__manifest__.py +++ b/purchase_order_import_ubl/__manifest__.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -# © 2016 Akretion (Alexis de Lattre ) +# © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Quotation Order UBL Import', - 'version': '8.0.1.0.0', + 'version': '10.0.1.0.0', 'category': 'Purchase Management', 'license': 'AGPL-3', 'summary': 'Import UBL XML quotation files', @@ -12,5 +12,5 @@ 'website': 'http://www.akretion.com', 'depends': ['purchase_order_import', 'base_ubl'], 'demo': ['demo/demo_data.xml'], - 'installable': False, + 'installable': True, } diff --git a/purchase_order_import_ubl/demo/demo_data.xml b/purchase_order_import_ubl/demo/demo_data.xml index 63c0e6488c..7699bcb693 100644 --- a/purchase_order_import_ubl/demo/demo_data.xml +++ b/purchase_order_import_ubl/demo/demo_data.xml @@ -1,26 +1,8 @@ - - + - - SEK pricelist - sale - - - - - - Unique Version - - - - - - Line 0 - - Johnssons byggvaror @@ -32,7 +14,7 @@ 11000 Stockholm - + @@ -41,8 +23,8 @@ Boss pelle@johnsson.se - - + contact + @@ -77,23 +59,6 @@ - - IYT pricelist - sale - - - - - - Unique Version - - - - - - Line 0 - - IYT Corporation @@ -105,7 +70,7 @@ ZZ99 1ZZ Bridgtow - + @@ -113,8 +78,7 @@ Fred Churchill fred@iytcorporation.gov.uk - - + contact @@ -146,28 +110,11 @@ S Massiah smassiah@the-email.co.uk - + contact - - DKK pricelist - sale - - - - - - Unique Version - - - - - - Line 0 - - Gentofte Kommune @@ -178,17 +125,15 @@ 2920 Charlottenlund - + Joe Delivery - - delivery + contact - - + diff --git a/purchase_order_import_ubl/tests/files/quote-PO00004.pdf b/purchase_order_import_ubl/tests/files/quote-PO00004.pdf index 012de43143ccccfb3b30bd34ce6b68df0a00a117..acef818fb03ad34d18dee8ba608da85912295bcb 100644 GIT binary patch delta 27396 zcmagE190Tsw=SGaIN@Ys+wRylCbn%`olI;ynb@{%+qN~a@4WB)&#iM$efPV&YX8<= zde-W$uIlb*uiQn@-Rv(h^Irgrv5WnnfcSJ!CQyd>DNrilnd-F`LDt|P4KEyyT!lm>g z;IPztr~7D_7W#eoGN0;(ZuL)!LPwnyba(fO&0)=l6rQxKhj&^_kJ~99_vZ|4PtMfO ziMd1a+p13>G%;V=kE~((uCyjZXY>cyDs}an_4}#up{`bZ7W&_U<~_yF-i&W%U2moj zw`}`$JAI6_lB1;e?Qp!*Wc`_~L@zJvhu{b%BvfXcVEI-zw`)G%%!!4{f`Ko{{;7GTg3 z3~n_4HO^MQGBC-#q*!GG4M8)&az@596Akl5`b1-#xV9di%RXB#Jjkgu)$}qe5`b(j zbn=VSrk#_MW?c}%nqaN@z@`KgGdujx1Grd~g4Z36#z9L}Ie4wm@_QA7(zR5i8x4^@ z6;>)A!$0I`l`SGW*O5|kiCw8J1qAQ1;%CQ3eo_kqZwK|=n&`0yMFhOjZ-lT16M4oR z(@Vv03CR`YIk0{*Jcs^nGrwOso19d~bdP;ZjR;8^^#!M?JjSnk5e9Wxj;LRu3V|{P zi@3`p=H+lt=CaOPPlaw?{KS>^JjQ&}pz;`q6iN=;mvL#G#Ctp!9{+=VTUk56wGe7 z_!yys&vHTkdIkM*B4mLnz9EE6O$03G!885S2h}1rhDbRB4)PDH;k2=@U}<`Bv}ou4 zjPgo#jIr+2IrWS|*5Luqp!FOPn<>ry?&+DTGWRNiAc9vv`{3q~96O)vSQoI9G$Ayr zUHw|*yY0}^;e&*%VGYMe&4X4eyem;GG=E+(%_kWn$~1jj#x~>s=EunX$_dO%AzVR5 z9=F|UO7W`3PrJVP*@0KiE2B=FVFWNMI`{c3u_v$-3e-;-$VULxg#gng;2r|krvV#Z z%ea!kv$#h%Wgn3%gTj)+`&)o&L1+$)*>l;!;-))zqGC0N?Q4Jx4OvQAMp}y3GEUmw zOmAWKU47qSF4JZAI3Dnpwdq$nr^nalOFBtwh;3oem1~Pz8`|J&jv5$ia4z5;kFKF#yI#xSpR(34O2?f_r~rG7)DKD zigiQeY_O%DB~dZhn+N`yQFJrD>kLb7P=ez+)a8Nu0|u5J?JPhZHSr-*=*o27Ew4E0h6Jb|F24 z@7UXdPLT3djj&&ELWgM+=@1Maq^At%v=>Ks_Ki)X$VMni*v#tSR>~M~L`rTRg#jp|*ch?E@W&i;t#k0%EkZRp zMRp)6cN2h#1cK8dw#H80RE=g?l1WihrCNNin6~V-4Ih^EH`P#P+i*z==kg5;eGFWpAMd*_rg{#PW+W;(9@yC8d!6$&?loMF!JSyYHe|qF>awuhsw)Wf zgviCE;`pPBR5;nB*751;jNBtuC46^ybQN4XNA=n+w9|nncXgvRVnz%I8n&;Y*oA%s z9T){dm_UPZVMT)18+RU473wohG%9}3<)D7!=j zLeSuY=nj@pJ-N~iD*L`^zMfBWpgX2pc|9k14J^EPsUy z%3mrx_K1Dn<1#%7sq>Pom6^Bo>s-0kIe-J#!jp$o66DjCEy-Sh08eE^;?S(w1Gn+dJ#G5WJwh-nBfU!N3~qVI2Ed8(UJ6dx3GZ zA0~=0G;)4L(vG1*l6@xa_Z0>_TU6{<(}p8w0KwrrIK@!}o?jobvA@|@mBiEcTJ%O! z$ItV6cKG6k&Vk_FZf7vYHb#z44#xWNb}+V-^b*#Lj0Efi34F8(xMcD`#S>j(|u)hEp;xS-F;%7in;t{_?0STA^j0`aJLbg`64vKdAhQT1-((4EeN*Fqn*6tWYZy0R#zp3a%^oXq zkoS)cUz~FrnxW^_Jgpy5tu^ao`9sXJAy-uTewtr9fy z`(v1KeLH60ApeiG%_p2z_{^A-1?~F@BM~P)f;upgh&u3gLU{+A1HB$`ck`K?oL}k; z@P>Pj0P!+O0I8$nQqy(E8?rwh0^fG5Rip8s*=|!zGtS9E@tu-5VyJf1dkXL)1VPA# zmeZDq3-D0`;UyFX4VxiihW3ShDKy9y09glv|MCsZ?3-^jzduL-1Qs{}%`^dsz8@qg zG%PbTzuKRW-Y>xa5yKBg7)&z;Ll`+As73V|_k@1KA~W~;>iHrj;1BXm9lAx?(;QpA zzN=HJ6uP~Z7;HG*)G3@U#Xx|^CZt9J8IrrOW^a(rGl0x zlDe$i$U`7PXnOnYO`GNFiR=H6A5>mKfX>EFzy~i8{igj&)jR*yfI%%zTNDJ$t#B$-vb-4?Ff-J z?uXkTx8|VJlXE{(Vx9{VUaanu)E9(&*u5~w+N$dA*-^>L>&d!ZNo{iPjuMML7DF%E zv^Hk(o@cWy%E)Bz3qD9-6F`+iNLjGS?ocGRyM$Mvm*hEPObvN7RWZMs4^_pkfv5Mr zl@LZlf#OllbU+67u-J1+Bm=Q-UlejJ#YD7E^fE5+KAt&U$vdJ}(uWtWTjbtb&|!-! zRCwS$>dv2gxEgf0ib#JeE3W8|j5vqZQ!a3u?hE-w3ZS^sbg8QtFk0QGnoXy_MA4Z7 zBKDT+@^UMkVth*C{Mqgs%JrSMhBcNFK489#KVvyD-Wh85HZ>`W!2myr1M@7!P4zA= z0a=Y#yT<3qFsghJsne}$SP7PCU=(Q;1H$r(LdR5f`f!M!9gWR*dZhx$n57W4+Z72j zAj4Smy2N!pD;UxhDrGxPiR#BJ@5wj*{smC)QgngGqt(#b>F29O=5m9RtiMcqd;2;4si7PK7!gMnsU{$V3Qv+dNVHv+tv;5`>Tr#=P`{%!ea$j z)3FN|@7Xllu`Ch07c7SAqmybU43^qBa$8HEm)+QJa{e+ckfhy*v+Od<&uN7c&N#gp z?KPNdzwyqDd5smMq?pPCgW9-G^;W7iQ0rF8kI;Gvl|1T~BZ0tTfpjvVdy>W_X?0{Q zXX(tyM-|u5V0PI~QZ#e4l4mDT8BOLj|1o1n?nvlXlk&+sR=r&^)o(>FR7gvAf~Y;R&iS8%w;4|l z$An%yRn&aoKwy|cNNvc^;!|-v{0bEETU3fR{0sPe!agxqT~ofQOV1bn8{!*S;5+Zu zdP+*K5zmKS^WEKD{_|nkU>>f#t?@^Zm_HeEyUZ35ciFEmVVoKjDnW#7yV0RTDQVId zCJVo0Zn}9ob16!mN~mIUDjpda=G8)~3aJk&3pO&N`hag_iDl?M$l>;uQ{-nI*3{ma z&l*Nqkj{K^Q|WPSt@W^i&3P`#aY!!Gu0(k&&dT(5tsCZC6D>N$SjQE%uR&7ywGm0! zJ~*;0Fs{ORvKNqq=}s-l%qchM#0f8Ono+;yS;FSJ@CANslPOlguPXAt$Abl3 z51H9V!vLGD$S5B{1K^tG+>eUP=gY&rjb6ZJ>EKUgwA640j_;^C3gyJ0TMw^rCglC= zMp7r-Cf6b8#ehj!e%66)S^6W)KUH29WCQMJ93{C}hMm{65~PHJRv<=(OE7$1qiG|D z6AZHn`^E+A$^|i5eq&TmAu{h+1CD5AL`OUvPJ#Tjy5(c$rX3|Yj_?kT(<7OUHZCEK zo{8tZXA2BxbVNgG-pqHM0|SFFn5ca>U2Cgy_J$WN?1J^k0K ze=O;tbZxYA4N`Fs-W$KHR*aPHI>OfyU#??6CNK$k;yF+%bLU$atQenN#$)_oBooRt z;`<$o5Y=GW+@Uj-)_k4X?8_qNkt-mSw+E~gzANIy6sq}H{E?<;BmHwgvrFdCiI~D? zWw*j}dE?^vvL=JLR6)lMeds+wqz4PNk)?U->ZDG37qR1l`=Fv7=MfH0Y~Ia2UYY1c zQT_bv_%=omrO`&xgTdjrnWA@VCX2Wdvg3$aPX>(zYkvape5E#wxOj;_QcE$&5+q++IU10Z#s!oY+wQSFsT8g8?THt$@3wV@WYZXJZ2)kR}O{BbE ze<#}rV)?W>h_Yq-YA-lAlk3HIXAVqth`SW?^l(I_pT4aekCj|JnzaC@MA@)oMw8k5 z(cUBJZBFXq56%Cn%J?czac;Dr6Yf8Vrw((27S}dQy zp~y+JY8RA;?!LE9g1tu+DbobIydXvzHThbwXZF=Ew{#KDc7*PTWzh#rRco+hj4vMV=9^JIu0;-nj+N($wJZN3W#7 z&d+0`ozTsiC;a`WhFY9aIJesBkRz%R1WWpNRmT&?8d zuFE>Mw#247TjPJ*8VQPDDdCo_7<4#+h*DT!sqXdVBHvff+BVG?lUj32GLB839uW#P z66Pr~t@d=?jBS|#*OO{h^(v{rOAOhI zYE8FUy>MwSAJ9JOG_LAh{XDA`#?sCRAJRKmQ2}U)KCl7VtB7@eS>KG)?_HK?*tl4p z(czRuE=<|A;Oia8yvxmyRR=jx(d4{3KDjsIJ}?HSeVu3JL7fc5=#skWcLapBw9>Ym}eW~9Q-mfe@z>#w1k)Kq9pBJ z+)v85*e?tBC2>$G0cZ|#a}GhX!z}9BgHS)%JAyvcGf>OvnmUs=FNS>#OI-_dZcrjO zQ`;R9Hqv7yKBe{RzK4$P{>qt47?zAML-dlmwE9I02kl z5asHBmUD)dT{6#=&K-*OM`?d5t@qhq%(t3v$PYoO&?ZgS*iC7gCsQl{LQ45&XkuSC z^beYZ_t?(|X<)u~ZN%)h>e}o|UX9n8tm;s3xGC#mRh+6&Ng*nKRyJq2S}hi&6)YiH z>Q@4VLig8DC$7$5Vkq3bk8(=U<#GE{6x>CFB#bjN({@5G61?#)Yv2~1e=87I$+v&} zem^+XS?0P?j@SqgRIb#+w9WHOhIeGzvuUc!OXv%(r;kS*H}C6qywh%05lAqmDP6cN zH{i`Gvt8W?f1o|KsXT9v^^rZ%B!!6ojU8IyrJyIYpt(eoRmc;?=Dd=o$$ zWfEA4IIbOlEI&uil7?9)C0q4yO*`-E(4sGJ!0XgWJ0df z`G?q02bIMLJ;1bb);qZF%c|wYMG<}8FDg!49Vw_mb7tua{C%R9%+BVoFmVA70lzt6 zD>^MWe5@RcMCIL|K&e&*iWi0l1A>5wQWF|#HCXBmA>SI=a{x82wH*bAd`?c*MGuY= zBNHb+xEzEiu4Phpj`}z(s%NRm+)I=4_NA4xz@S#2q?y|G8J!<&%|`rbeIlvsQ7nd% zsm^JIC1;$8Z3aSWLLGXjFan}&biKXSHTV~ZJ+z%#(`Vx(aET#id>%Dz`7pqW@hrAv z(SdT0+g-q8i{*Y#vkBBHfC6@A zTC$b0zfdu~_2wza43&bC?Iw^+;ilkpIw?n5C{p97W^tnVI4VntPk|49f&H}xuw4O0 z*H@v!HXpkWi96=Wpc%p>h9mD2Z+<;y-w{zcz9J$anmD33t9Xk2Dw{JU;+A;hKmrkb&}5caAeQJ%aRS_VY}qOA`sT1{ z@+P;b6c?djUlMEBA*M56r5f(Nd*%*c-UdCfGEF^}o~81ZQMsV9s;o~tQqrs!&bpOi zR&p=h({B@dzn9A>%u;G2_h~`G|9_AH|EC-0zp0?le2CCmH~*Vlv5udHMfry)0scR^ z4ryZ>(|^bg4)*^~E=9~i-~Im}ME(br@_*+?GA^{;y?`=9czlnCCQT%@A;RU9x$*{I zf0f3d33wk{5{slisrhFwqIq*`p5#*fpskoykdmY=kV#~vB80Yx)F7&9jBA!4Azb6R z%1GZod^;S^NR5Bwjegwp`eeFIw>9Z}%t+mOJ7fzcu=}6uZ7X--E)cc|$oHD>d9wv_ z#gdEa>7C{DSu81Ii(rXOs-kWn!{^F42!3^NtVF7SS| za{XYLP0)B>f0A%LDiN(zDAR3L(HP#-VDz^fK&V9JYF0Vl)~NZvDka;ijZ?tu9eG>f z2OHx6R4r=Xmpr!5U&fR4G!!&6!$7;w-;2xHr=y>w6{mqyTlSoqc*7Njy0S2USxtU7 zSx(AQ{V04n^(ryyt&qnqsd#Me&QuJxR&$vkVrUlu!zD7Ec*8eD`b%BFaDxdA1MIqD{7K>`$zh4U8c#-MnRHBvW^DRqg)R7q5LRUbg(Aa^3%Y1Sfivp!}Q~C zP`RH;UnT@hDmc)NamuZR#}fZ` zZmIa(4IS7-LHGz2?T76xUQ*e1N$i-z&09nu5T=ZyO2~6ghLF)%mW3^1|sW6mr=b3v1fr%fH=dfBUwYODlc5 zI#nEfhI+`^7~*@CWm5koh4ZaKqoA6tL(HKm0?hf%PM702W|U8UYWa5_14<#12L(vB z3hvaI`6~f97r`rAaaqmx;AQVQvaVBXEuH){!x&x|QeTzK%418QR~fdAaGsOPj+m=x zN&DAHEX+sGJe-TKx5s&&n5mdRi#o~%7;f@YEy<}S`RHRAZ-UhujVNr3oW3s=7 z|7d=Ac5ra8x5bFobrA{z969mKmf?!aR9wg+7Vrgq7?O2Vh$@0^CTl4)WL znw8@E*(- zJ*Bi5A!?F3CqRPmRcgUN+2X(*gt9Brevj%}fY2ZfXi;|h6A~mU#(OI?6sCBNP^{3Ba`OJmKJo8-07{TpkL?^(?#Pg>f_>u(%To#8b<|spFRD>0p z#ibkyBHV^A8W_US_+)!i_T6GIcjd6S9dCck#{kA7?J(E0eo#4XfsW!e<=H{^5@o17 z%w_L;Qj%<5PbJw_C#`1voUf8s(+~n3?Hw)zru&f+F6a8%l9}0QE)L*NKBt@GC_u%E z|5T=)(&bmT0mG8Q$mpMP8wrdgytTm!{mL7hL?4<&tCw#E_5RI_Q>(%lSWkP7Cnf%1 z$S$SPU;&B05(r0=-P7Ucuwt}~`J$hz3ox^bH)98RV_r#%H-|XSUTU9JWa&9e%%nqYNVp*TZ7?S3_fp`_ne6*6CBz>GJRjsYcnYqScqaRqI;2vd4!X93xIR^nisRR0 zZNkNa2!;fuPEB4t(l(gDKSLq3LMF57j{u&TK9-=u@WpBq0l6H@e+v+9p76!%01`7} zlJe6#fzp8N$+tjbaTIB~(lifrCz7v^)D^EffGO4qLf~vJfJdJI+N5whQZB#`cqz@m z=|_9q6oJW7%Jg#R>jHy4fpy@a!7RL*N-cqh^b;4FJBk8Zn@E~y>PwY3_8NtEPxj%e zK!wVJQrV*K18O5mQpu>Juv9%kxk{Xoh4l_9ND;j1S0;I}YNska!$v6oG=H;()8Sb6J@jROZ_#u2<�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 NKomMUX%!ii{{!piT8;n! delta 18632 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 N$ec@6)z#mP3jlgiy#oLM diff --git a/purchase_order_import_ubl/tests/test_ubl_order_import.py b/purchase_order_import_ubl/tests/test_ubl_order_import.py index 90a1b51b65..014c08d6c6 100644 --- a/purchase_order_import_ubl/tests/test_ubl_order_import.py +++ b/purchase_order_import_ubl/tests/test_ubl_order_import.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# © 2016 Akretion (Alexis de Lattre ) +# © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.tests.common import TransactionCase -from openerp.tools import file_open +from odoo.tests.common import TransactionCase +from odoo.tools import file_open import base64 diff --git a/purchase_order_import_ubl/wizard/purchase_order_import.py b/purchase_order_import_ubl/wizard/purchase_order_import.py index 877604ffb7..8853863398 100644 --- a/purchase_order_import_ubl/wizard/purchase_order_import.py +++ b/purchase_order_import_ubl/wizard/purchase_order_import.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# © 2016 Akretion (Alexis de Lattre ) +# © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, api -from openerp.tools import float_is_zero +from odoo import models, api +from odoo.tools import float_is_zero import logging logger = logging.getLogger(__name__) From e6d87491838844a2a632000ca56f895db4d468c8 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sat, 14 Oct 2017 16:28:28 +0200 Subject: [PATCH 06/19] Remove demo data that were shipped both with sale_order_import_ubl and purchase_order_import_ubl --- purchase_order_import_ubl/demo/demo_data.xml | 28 -------------------- 1 file changed, 28 deletions(-) diff --git a/purchase_order_import_ubl/demo/demo_data.xml b/purchase_order_import_ubl/demo/demo_data.xml index 7699bcb693..32b01a113f 100644 --- a/purchase_order_import_ubl/demo/demo_data.xml +++ b/purchase_order_import_ubl/demo/demo_data.xml @@ -40,24 +40,6 @@ - - Falu Rödfärg - Red paint - SItemNo001 - 55 - consu - - - - - - Pensel 20 mm - Very good pencils for red paint - SItemNo011 - 18 - consu - - IYT Corporation @@ -81,16 +63,6 @@ contact - - Beeswax - Acme beeswax - 17589683 - 42 - consu - - - - The Terminus From 1eb179f1f34f922089a3d00b615eab7e89a8f4fb Mon Sep 17 00:00:00 2001 From: OCA Transbot Date: Sat, 31 Mar 2018 05:31:32 +0200 Subject: [PATCH 07/19] OCA Transbot updated translations from Transifex --- purchase_order_import_ubl/i18n/es.po | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 purchase_order_import_ubl/i18n/es.po diff --git a/purchase_order_import_ubl/i18n/es.po b/purchase_order_import_ubl/i18n/es.po new file mode 100644 index 0000000000..80722030c9 --- /dev/null +++ b/purchase_order_import_ubl/i18n/es.po @@ -0,0 +1,24 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_order_import_ubl +# +# Translators: +# enjolras , 2018 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-03-12 01:43+0000\n" +"PO-Revision-Date: 2018-03-12 01:43+0000\n" +"Last-Translator: enjolras , 2018\n" +"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: purchase_order_import_ubl +#: model:ir.model,name:purchase_order_import_ubl.model_purchase_order_import +msgid "Purchase Order Import from Files" +msgstr "Importar pedido de compra desde archivos" From 182c978a8d431c91afc58ef56a59d63fe7d43e3a Mon Sep 17 00:00:00 2001 From: oca-travis Date: Tue, 31 Jul 2018 14:04:30 +0000 Subject: [PATCH 08/19] [UPD] Update purchase_order_import_ubl.pot --- purchase_order_import_ubl/i18n/es.po | 4 ++-- .../i18n/purchase_order_import_ubl.pot | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 purchase_order_import_ubl/i18n/purchase_order_import_ubl.pot diff --git a/purchase_order_import_ubl/i18n/es.po b/purchase_order_import_ubl/i18n/es.po index 80722030c9..c174db9d83 100644 --- a/purchase_order_import_ubl/i18n/es.po +++ b/purchase_order_import_ubl/i18n/es.po @@ -1,7 +1,7 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * purchase_order_import_ubl -# +# # Translators: # enjolras , 2018 msgid "" @@ -12,10 +12,10 @@ msgstr "" "PO-Revision-Date: 2018-03-12 01:43+0000\n" "Last-Translator: enjolras , 2018\n" "Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. module: purchase_order_import_ubl diff --git a/purchase_order_import_ubl/i18n/purchase_order_import_ubl.pot b/purchase_order_import_ubl/i18n/purchase_order_import_ubl.pot new file mode 100644 index 0000000000..095cf7d7b1 --- /dev/null +++ b/purchase_order_import_ubl/i18n/purchase_order_import_ubl.pot @@ -0,0 +1,20 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_order_import_ubl +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: purchase_order_import_ubl +#: model:ir.model,name:purchase_order_import_ubl.model_purchase_order_import +msgid "Purchase Order Import from Files" +msgstr "" + From ea87403c01d2cfd1456d49326b0d66ed423f5858 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sun, 26 Aug 2018 00:05:16 +0200 Subject: [PATCH 09/19] Check the VAT number of the destination partner, to make sure the business document is imported in the right company --- purchase_order_import_ubl/wizard/purchase_order_import.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/purchase_order_import_ubl/wizard/purchase_order_import.py b/purchase_order_import_ubl/wizard/purchase_order_import.py index 8853863398..829bd18fe5 100644 --- a/purchase_order_import_ubl/wizard/purchase_order_import.py +++ b/purchase_order_import_ubl/wizard/purchase_order_import.py @@ -70,6 +70,13 @@ def parse_ubl_quote(self, xml_root): supplier_xpath = xml_root.xpath( '/main:Quotation/cac:SellerSupplierParty', namespaces=ns) supplier_dict = self.ubl_parse_supplier_party(supplier_xpath[0], ns) + customer_xpath_party = xml_root.xpath( + '/main:Quotation/cac:BuyerCustomerParty/cac:Party', namespaces=ns) + company_dict_full = self.ubl_parse_party(customer_xpath_party[0], ns) + company_dict = {} + # We only take the "official references" for company_dict + if company_dict_full.get('vat'): + company_dict = {'vat': company_dict_full['vat']} delivery_term_xpath = xml_root.xpath( "/main:Quotation/cac:DeliveryTerms", namespaces=ns) if delivery_term_xpath: @@ -86,6 +93,7 @@ def parse_ubl_quote(self, xml_root): # TODO : add charges res = { 'partner': supplier_dict, + 'company': company_dict, 'currency': {'iso': currency_code}, 'date': date_xpath[0].text, 'incoterm': incoterm_dict, From 4c7eba32f47517d06acebc838ba2e35b1e489006 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 3 Apr 2019 02:46:08 +0000 Subject: [PATCH 10/19] [ADD] icon.png --- .../static/description/icon.png | Bin 0 -> 9455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 purchase_order_import_ubl/static/description/icon.png diff --git a/purchase_order_import_ubl/static/description/icon.png b/purchase_order_import_ubl/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 From f69daa72bdd61500d51bb62cf752fa7e0041f728 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Wed, 12 Feb 2020 11:30:12 +0100 Subject: [PATCH 11/19] [ADD] Add support for UBL OrderResponse The ORderResponce document allows a supplier to communicate the ack and the confirmation of an Order documnet. Depending of its content, the PO will be cancelled or confirmed. If the OrderResponse document contains some amended line, the import process will update the picking to reflect the changes --- purchase_order_import_ubl/tests/__init__.py | 3 +- .../tests/files/order_response_tmpl.xml | 143 ++++++++++++++ .../tests/test_order_response_import.py | 95 +++++++++ purchase_order_import_ubl/wizard/__init__.py | 3 +- .../wizard/order_response_import.py | 183 ++++++++++++++++++ 5 files changed, 423 insertions(+), 4 deletions(-) create mode 100644 purchase_order_import_ubl/tests/files/order_response_tmpl.xml create mode 100644 purchase_order_import_ubl/tests/test_order_response_import.py create mode 100644 purchase_order_import_ubl/wizard/order_response_import.py diff --git a/purchase_order_import_ubl/tests/__init__.py b/purchase_order_import_ubl/tests/__init__.py index 517f136e78..6a208fe0da 100644 --- a/purchase_order_import_ubl/tests/__init__.py +++ b/purchase_order_import_ubl/tests/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- - from . import test_ubl_order_import +from . import test_order_response_import diff --git a/purchase_order_import_ubl/tests/files/order_response_tmpl.xml b/purchase_order_import_ubl/tests/files/order_response_tmpl.xml new file mode 100644 index 0000000000..f52e2ad017 --- /dev/null +++ b/purchase_order_import_ubl/tests/files/order_response_tmpl.xml @@ -0,0 +1,143 @@ + + + 2.1 + PO07337 + 2020-02-04 + 22:10:30 + {order_response_code} + Note1 + Note2 + EUR + + {order_id} + + + 79201 + + + BE0477472701 + + + My supplier company BELGIUM + + + French (BE) / Français (BE) + fr_BE + + + straat 20 + ZAVENTEM + 1930 + + BE + Belgique + + + + My supplier company BELGIUM + BE0401953350 + + VAT + + + + 12345 + 12345 + orderahbelux@my-supplier-company.com + + + + + + http://www.my-company.be + + BE0421801233 + + + My Componay Belux SA + + + English + en_US + + + Rue , 17 + Villers-le-Bouillet + 4530 + + BE + Belgique + + + + My Componay Belux SA + BE0421801233 + + VAT + + + + My Componay Belux SA + + Rue , 17 + Villers-le-Bouillet + 4530 + + BE + Belgique + + + + + +32 (0)4 123 34 90 + +32 (0)4 123 27 83 + secretariat@my-compy.be + + + + + + {line_1_id} + line_1 Note1 + line_1 Note2 + {line_1_qty} + {line_1_backorder_qty} + {line_1_status_code} + 228.2 + + 45.64 + 1 + + + [10016098] CYTOPOINT 10MG 2x1ml + CYTOPOINT 10MG 2x1ml + + 10016098 + + + + + + + + {line_2_id} + line_2 Note1 + line_2 Note2 + {line_2_qty} + {line_2_backorder_qty} + {line_2_status_code} + 1117.07 + + 65.71 + 1 + + + [10016099] CYTOPOINT 20MG 2x1ml + CYTOPOINT 20MG 2x1ml + + 10016099 + + + + + diff --git a/purchase_order_import_ubl/tests/test_order_response_import.py b/purchase_order_import_ubl/tests/test_order_response_import.py new file mode 100644 index 0000000000..aa8fe2d9ab --- /dev/null +++ b/purchase_order_import_ubl/tests/test_order_response_import.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tools import file_open +from odoo.addons.purchase_order_import.wizard.order_response_import import ( + ORDER_RESPONSE_STATUS_ACK, + LINE_STATUS_ACCEPTED, +) +from odoo.addons.purchase_order_import.tests.test_order_response_import import ( + TestOrderResponseImportCommon, +) +from ..wizard.order_response_import import ( + _ORDER_RESPONSE_CODE_TO_STATUS, + _ORDER_LINE_STATUS_TO_STATUS, +) + +_STATUS_TO_RESPONSE_CODE = { + p[1]: p[0] for p in _ORDER_RESPONSE_CODE_TO_STATUS.items() +} + +_STATUS_TO_LINE_STATUS = { + p[1]: p[0] for p in _ORDER_LINE_STATUS_TO_STATUS.items() +} + + +class TestOrderResponseImport(TestOrderResponseImportCommon): + @classmethod + def setUpClass(cls): + super(TestOrderResponseImport, cls).setUpClass() + with file_open( + "purchase_order_import_ubl/tests/files/order_response_tmpl.xml", + "rb", + ) as f: + cls.order_response_xml = f.read() + + def test_01(self): + """ + Data: + An UBL2 OrderResponse with all the information expected by the + parser + Test case: + Convert to xml document to the internal data structure + Expected result: + All the fields are filled into the internal data structure. + """ + xml_content = self.order_response_xml.format( + order_response_code=_STATUS_TO_RESPONSE_CODE[ + ORDER_RESPONSE_STATUS_ACK + ], + order_id=self.purchase_order.name, + line_1_id=self.line1.id, + line_1_qty=self.line1.product_qty, + line_1_backorder_qty=0, + line_1_status_code=_STATUS_TO_LINE_STATUS[LINE_STATUS_ACCEPTED], + line_2_id=self.line2.id, + line_2_qty=self.line2.product_qty, + line_2_backorder_qty=0, + line_2_status_code=_STATUS_TO_LINE_STATUS[LINE_STATUS_ACCEPTED], + ) + result = self.OrderResponseImport.parse_order_response( + xml_content, "test.xml" + ) + attachments = result.pop("attachments") + self.assertTrue(attachments.get("test.xml")) + expected = { + "status": ORDER_RESPONSE_STATUS_ACK, + "company": {"vat": "BE0421801233"}, + "currency": {"iso": "EUR"}, + "date": "2020-02-04", + "chatter_msg": [], + "lines": [ + { + "status": LINE_STATUS_ACCEPTED, + "backorder_qty": 0, + "qty": self.line1.product_qty, + "note": "line_1 Note1\nline_1 Note2", + "line_id": str(self.line1.id), + "uom": {"unece_code": "C62"}, + }, + { + "status": LINE_STATUS_ACCEPTED, + "backorder_qty": 0, + "qty": self.line2.product_qty, + "note": "line_2 Note1\nline_2 Note2", + "line_id": str(self.line2.id), + "uom": {"unece_code": "C62"}, + }, + ], + "note": "Note1\nNote2", + "time": "22:10:30", + "supplier": {"vat": "BE0401953350"}, + "ref": str(self.purchase_order.name), + } + self.assertDictEqual(expected, result) diff --git a/purchase_order_import_ubl/wizard/__init__.py b/purchase_order_import_ubl/wizard/__init__.py index 627d4faa91..5d8ab4abcd 100644 --- a/purchase_order_import_ubl/wizard/__init__.py +++ b/purchase_order_import_ubl/wizard/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- - from . import purchase_order_import +from . import order_response_import diff --git a/purchase_order_import_ubl/wizard/order_response_import.py b/purchase_order_import_ubl/wizard/order_response_import.py new file mode 100644 index 0000000000..57624dcbb7 --- /dev/null +++ b/purchase_order_import_ubl/wizard/order_response_import.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models, _ +from odoo.exceptions import UserError +from odoo.addons.purchase_order_import.wizard.order_response_import import ( + ORDER_RESPONSE_STATUS_ACK, + ORDER_RESPONSE_STATUS_ACCEPTED, + ORDER_RESPONSE_STATUS_REJECTED, + ORDER_RESPONSE_STATUS_CONDITIONAL, + LINE_STATUS_ACCEPTED, + LINE_STATUS_REJECTED, + LINE_STATUS_AMEND, +) + +import logging + +logger = logging.getLogger(__name__) + + +_ORDER_RESPONSE_CODE_TO_STATUS = { + "AB": ORDER_RESPONSE_STATUS_ACK, + "AP": ORDER_RESPONSE_STATUS_ACCEPTED, + "RE": ORDER_RESPONSE_STATUS_REJECTED, + "CA": ORDER_RESPONSE_STATUS_CONDITIONAL, +} + +_ORDER_LINE_STATUS_TO_STATUS = { + "5": LINE_STATUS_ACCEPTED, + "7": LINE_STATUS_REJECTED, + "3": LINE_STATUS_AMEND, +} + + +class OrderResponseImport(models.TransientModel): + _name = "order.response.import" + _inherit = ["order.response.import", "base.ubl"] + + @api.model + def parse_xml_order_document(self, xml_root): + start_tag = "{urn:oasis:names:specification:ubl:schema:xsd:" + if xml_root.tag == start_tag + "OrderResponse-2}OrderResponse": + return self.parse_ubl_order_response(xml_root) + else: + return super(OrderResponseImport, self).parse_xml_order(xml_root) + + @api.model + def parse_note_path(self, note_xpath): + return "\n".join([n.text for n in note_xpath or [] if n.text]) + + @api.model + def parse_response_code(self, xml_root, ns): + code_xpath = xml_root.xpath( + "/main:OrderResponse/cbc:OrderResponseCode", namespaces=ns + ) + code = code_xpath and len(code_xpath) and code_xpath[0].text + status = _ORDER_RESPONSE_CODE_TO_STATUS.get(code) + if not status: + raise UserError(_("Unknown response code found '%s'") % code) + return status + + @api.model + def parse_line_status_code(self, line, ns): + code_xpath = line.xpath("cbc:LineStatusCode", namespaces=ns) + code = code_xpath and len(code_xpath) and code_xpath[0].text + status = _ORDER_LINE_STATUS_TO_STATUS.get(code) + if not status: + raise UserError( + _("Unsupported line status code found '%s'") % code + ) + return status + + @api.model + def parse_ubl_order_response_line(self, line, ns): + line_item = line.xpath("cac:LineItem", namespaces=ns)[0] + line_id_xpath = line_item.xpath("cbc:ID", namespaces=ns) + qty_xpath = line_item.xpath("cbc:Quantity", namespaces=ns) + qty = float(qty_xpath[0].text) + note_xpath = line_item.xpath("cbc:Note", namespaces=ns) + backorder_qty_xpath = line_item.xpath( + "cbc:MaximumBackorderQuantity", namespaces=ns + ) + backorder_qty = None + if backorder_qty_xpath and len(backorder_qty_xpath): + backorder_qty = float(backorder_qty_xpath[0].text) + + res_line = { + "line_id": line_id_xpath[0].text, + "qty": qty, + "uom": {"unece_code": qty_xpath[0].attrib.get("unitCode")}, + "note": self.parse_note_path(note_xpath), + "status": self.parse_line_status_code(line_item, ns), + "backorder_qty": backorder_qty + } + return res_line + + # Format of parsed order response + # { + # 'ref': 'SO01234' # the buyer party identifier + # # (specified into the Order document -> po's name) + # 'supplier': {'vat': 'FR25499247138'}, + # 'company': {'vat': 'FR12123456789'}, # Only used to check we are not + # # importing the quote in the + # # wrong company by mistake + # 'status': 'acknowledgement | accepted | rejected | + # conditionally_accepted' + # 'currency': {'iso': 'EUR', 'symbol': u'€'}, + # 'note': 'some notes', + # 'chatter_msg': ['msg1', 'msg2'] + # 'lines': [{ + # 'id': 123456, + # 'qty': 2.5, + # 'uom': {'unece_code': 'C62'}, + # 'status': 5, + # 'note': 'my note' + # 'backorder_qty: None # if provided and qty != expected + # # the backorder qty will be delivered + # # in a next shipping + # }] + @api.model + def parse_ubl_order_response(self, xml_root): + ns = xml_root.nsmap + main_xmlns = ns.pop(None) + ns["main"] = main_xmlns + date_xpath = xml_root.xpath( + "/main:OrderResponse/cbc:IssueDate", namespaces=ns + ) + time_xpath = xml_root.xpath( + "/main:OrderResponse/cbc:IssueTime", namespaces=ns + ) + order_reference_xpath = xml_root.xpath( + "/main:OrderResponse/cac:OrderReference/cbc:ID", namespaces=ns + ) + + currency_xpath = xml_root.xpath( + "/main:OrderResponse/cbc:DocumentCurrencyCode", namespaces=ns + ) + currency_code = False + if currency_xpath: + currency_code = currency_xpath[0].text + else: + currency_xpath = xml_root.xpath( + "//cbc:LineExtensionAmount", namespaces=ns + ) + if currency_xpath: + currency_code = currency_xpath[0].attrib.get("currencyID") + supplier_xpath = xml_root.xpath( + "/main:OrderResponse/cac:SellerSupplierParty", namespaces=ns + ) + supplier_dict = self.ubl_parse_supplier_party(supplier_xpath[0], ns) + # We only take the "official references" for supplier_dict + supplier_dict = {"vat": supplier_dict.get("vat")} + customer_xpath_party = xml_root.xpath( + "/main:OrderResponse/cac:BuyerCustomerParty/cac:Party", + namespaces=ns, + ) + company_dict_full = self.ubl_parse_party(customer_xpath_party[0], ns) + company_dict = {} + # We only take the "official references" for company_dict + if company_dict_full.get("vat"): + company_dict = {"vat": company_dict_full["vat"]} + note_xpath = xml_root.xpath( + "/main:OrderResponse/cbc:Note", namespaces=ns + ) + lines_xpath = xml_root.xpath( + "/main:OrderResponse/cac:OrderLine", namespaces=ns + ) + res_lines = [] + for line in lines_xpath: + res_lines.append(self.parse_ubl_order_response_line(line, ns)) + res = { + "ref": order_reference_xpath[0].text, + "supplier": supplier_dict, + "company": company_dict, + "currency": {"iso": currency_code}, + "date": len(date_xpath) and date_xpath[0].text, + "time": len(time_xpath) and time_xpath[0].text, + "status": self.parse_response_code(xml_root, ns), + "note": self.parse_note_path(note_xpath), + "lines": res_lines, + } + return res From b120ac51b9c9a271e7f67a6cef2513e381f803a5 Mon Sep 17 00:00:00 2001 From: David Beal Date: Mon, 13 Jul 2020 19:25:24 +0200 Subject: [PATCH 12/19] FIX purchase_order_import_*: version number after manual merge --- purchase_order_import_ubl/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/purchase_order_import_ubl/__manifest__.py b/purchase_order_import_ubl/__manifest__.py index d4733c016a..36135ecd7e 100644 --- a/purchase_order_import_ubl/__manifest__.py +++ b/purchase_order_import_ubl/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Quotation Order UBL Import', - 'version': '10.0.1.0.0', + 'version': '10.0.2.0.0', 'category': 'Purchase Management', 'license': 'AGPL-3', 'summary': 'Import UBL XML quotation files', From 484e1106ef934adebccfa97b2aff7d837a5a8eb2 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Wed, 15 Jul 2020 13:32:13 +0000 Subject: [PATCH 13/19] [UPD] Update purchase_order_import_ubl.pot Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: edi-10.0/edi-10.0-purchase_order_import_ubl Translate-URL: https://translation.odoo-community.org/projects/edi-10-0/edi-10-0-purchase_order_import_ubl/ --- purchase_order_import_ubl/i18n/es.po | 19 +++++++++++++++++++ .../i18n/purchase_order_import_ubl.pot | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/purchase_order_import_ubl/i18n/es.po b/purchase_order_import_ubl/i18n/es.po index c174db9d83..e289886c52 100644 --- a/purchase_order_import_ubl/i18n/es.po +++ b/purchase_order_import_ubl/i18n/es.po @@ -22,3 +22,22 @@ msgstr "" #: model:ir.model,name:purchase_order_import_ubl.model_purchase_order_import msgid "Purchase Order Import from Files" msgstr "Importar pedido de compra desde archivos" + +#. module: purchase_order_import_ubl +#: model:ir.model,name:purchase_order_import_ubl.model_order_response_import +#, fuzzy +#| msgid "Purchase Order Import from Files" +msgid "Purchase Order Response Import from Files" +msgstr "Importar pedido de compra desde archivos" + +#. module: purchase_order_import_ubl +#: code:addons/purchase_order_import_ubl/wizard/order_response_import.py:60 +#, python-format +msgid "Unknown response code found '%s'" +msgstr "" + +#. module: purchase_order_import_ubl +#: code:addons/purchase_order_import_ubl/wizard/order_response_import.py:70 +#, python-format +msgid "Unsupported line status code found '%s'" +msgstr "" diff --git a/purchase_order_import_ubl/i18n/purchase_order_import_ubl.pot b/purchase_order_import_ubl/i18n/purchase_order_import_ubl.pot index 095cf7d7b1..0b46e71d17 100644 --- a/purchase_order_import_ubl/i18n/purchase_order_import_ubl.pot +++ b/purchase_order_import_ubl/i18n/purchase_order_import_ubl.pot @@ -18,3 +18,20 @@ msgstr "" msgid "Purchase Order Import from Files" msgstr "" +#. module: purchase_order_import_ubl +#: model:ir.model,name:purchase_order_import_ubl.model_order_response_import +msgid "Purchase Order Response Import from Files" +msgstr "" + +#. module: purchase_order_import_ubl +#: code:addons/purchase_order_import_ubl/wizard/order_response_import.py:60 +#, python-format +msgid "Unknown response code found '%s'" +msgstr "" + +#. module: purchase_order_import_ubl +#: code:addons/purchase_order_import_ubl/wizard/order_response_import.py:70 +#, python-format +msgid "Unsupported line status code found '%s'" +msgstr "" + From 9a7b42f08e6afa7b75731e462ca39c772f533532 Mon Sep 17 00:00:00 2001 From: Lindsay Date: Wed, 12 Aug 2020 15:36:41 +0200 Subject: [PATCH 14/19] [BUGFIX] Fix method name in purchase order --- purchase_order_import_ubl/wizard/purchase_order_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/purchase_order_import_ubl/wizard/purchase_order_import.py b/purchase_order_import_ubl/wizard/purchase_order_import.py index 829bd18fe5..faf8bbc601 100644 --- a/purchase_order_import_ubl/wizard/purchase_order_import.py +++ b/purchase_order_import_ubl/wizard/purchase_order_import.py @@ -19,7 +19,7 @@ def parse_xml_quote(self, xml_root): if xml_root.tag == start_tag + 'Quotation-2}Quotation': return self.parse_ubl_quote(xml_root) else: - return super(PurchaseOrderImport, self).parse_xml_order(xml_root) + return super(PurchaseOrderImport, self).parse_xml_quote(xml_root) @api.model def parse_ubl_quote_line(self, line, ns): From 7f8a0996f64d2ae9eaf00528603e23787aac48d0 Mon Sep 17 00:00:00 2001 From: Lindsay Date: Wed, 12 Aug 2020 15:30:50 +0200 Subject: [PATCH 15/19] [BUGFIX] Fix method name for order response import Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: edi-10.0/edi-10.0-purchase_order_import_ubl Translate-URL: https://translation.odoo-community.org/projects/edi-10-0/edi-10-0-purchase_order_import_ubl/ --- purchase_order_import_ubl/i18n/es.po | 1 - purchase_order_import_ubl/wizard/order_response_import.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/purchase_order_import_ubl/i18n/es.po b/purchase_order_import_ubl/i18n/es.po index e289886c52..6c790a80b0 100644 --- a/purchase_order_import_ubl/i18n/es.po +++ b/purchase_order_import_ubl/i18n/es.po @@ -26,7 +26,6 @@ msgstr "Importar pedido de compra desde archivos" #. module: purchase_order_import_ubl #: model:ir.model,name:purchase_order_import_ubl.model_order_response_import #, fuzzy -#| msgid "Purchase Order Import from Files" msgid "Purchase Order Response Import from Files" msgstr "Importar pedido de compra desde archivos" diff --git a/purchase_order_import_ubl/wizard/order_response_import.py b/purchase_order_import_ubl/wizard/order_response_import.py index 57624dcbb7..4d13919174 100644 --- a/purchase_order_import_ubl/wizard/order_response_import.py +++ b/purchase_order_import_ubl/wizard/order_response_import.py @@ -43,7 +43,7 @@ def parse_xml_order_document(self, xml_root): if xml_root.tag == start_tag + "OrderResponse-2}OrderResponse": return self.parse_ubl_order_response(xml_root) else: - return super(OrderResponseImport, self).parse_xml_order(xml_root) + return super(OrderResponseImport, self).parse_xml_order_document(xml_root) @api.model def parse_note_path(self, note_xpath): From d76ef569f43d625b1999538342b278aa684ce871 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 25 Aug 2020 08:06:45 +0000 Subject: [PATCH 16/19] purchase_order_import_ubl 10.0.2.0.1 --- purchase_order_import_ubl/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/purchase_order_import_ubl/__manifest__.py b/purchase_order_import_ubl/__manifest__.py index 36135ecd7e..d902b65fbe 100644 --- a/purchase_order_import_ubl/__manifest__.py +++ b/purchase_order_import_ubl/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Quotation Order UBL Import', - 'version': '10.0.2.0.0', + 'version': '10.0.2.0.1', 'category': 'Purchase Management', 'license': 'AGPL-3', 'summary': 'Import UBL XML quotation files', From 504f8a0c5ea6cfa4c950fdc3dbca3c285fabb077 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 25 Aug 2020 08:19:42 +0000 Subject: [PATCH 17/19] purchase_order_import_ubl 10.0.2.0.2 --- purchase_order_import_ubl/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/purchase_order_import_ubl/__manifest__.py b/purchase_order_import_ubl/__manifest__.py index d902b65fbe..61f3dcee86 100644 --- a/purchase_order_import_ubl/__manifest__.py +++ b/purchase_order_import_ubl/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Quotation Order UBL Import', - 'version': '10.0.2.0.1', + 'version': '10.0.2.0.2', 'category': 'Purchase Management', 'license': 'AGPL-3', 'summary': 'Import UBL XML quotation files', From b971d10fefc6f87c593115921454b9c5dbd6158f Mon Sep 17 00:00:00 2001 From: Robin Conjour Date: Mon, 12 Apr 2021 09:36:31 +0200 Subject: [PATCH 18/19] [IMP] purchase_order_import_ubl: black, isort, prettier --- purchase_order_import_ubl/__init__.py | 2 - purchase_order_import_ubl/__manifest__.py | 21 +++-- purchase_order_import_ubl/demo/demo_data.xml | 75 ++++++++-------- .../tests/files/order_response_tmpl.xml | 14 ++- .../tests/test_order_response_import.py | 29 +++--- .../tests/test_ubl_order_import.py | 35 ++++---- .../wizard/order_response_import.py | 44 ++++----- .../wizard/purchase_order_import.py | 89 ++++++++++--------- .../odoo/__init__.py | 1 + .../odoo/addons/__init__.py | 1 + .../odoo/addons/purchase_order_import_ubl | 1 + setup/purchase_order_import_ubl/setup.py | 6 ++ 12 files changed, 156 insertions(+), 162 deletions(-) create mode 100644 setup/purchase_order_import_ubl/odoo/__init__.py create mode 100644 setup/purchase_order_import_ubl/odoo/addons/__init__.py create mode 120000 setup/purchase_order_import_ubl/odoo/addons/purchase_order_import_ubl create mode 100644 setup/purchase_order_import_ubl/setup.py diff --git a/purchase_order_import_ubl/__init__.py b/purchase_order_import_ubl/__init__.py index 3b4c3edf09..40272379f7 100644 --- a/purchase_order_import_ubl/__init__.py +++ b/purchase_order_import_ubl/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import wizard diff --git a/purchase_order_import_ubl/__manifest__.py b/purchase_order_import_ubl/__manifest__.py index 61f3dcee86..9a2ff25044 100644 --- a/purchase_order_import_ubl/__manifest__.py +++ b/purchase_order_import_ubl/__manifest__.py @@ -1,16 +1,15 @@ -# -*- coding: utf-8 -*- # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Quotation Order UBL Import', - 'version': '10.0.2.0.2', - 'category': 'Purchase Management', - 'license': 'AGPL-3', - 'summary': 'Import UBL XML quotation files', - 'author': 'Akretion,Odoo Community Association (OCA)', - 'website': 'http://www.akretion.com', - 'depends': ['purchase_order_import', 'base_ubl'], - 'demo': ['demo/demo_data.xml'], - 'installable': True, + "name": "Quotation Order UBL Import", + "version": "10.0.2.0.2", + "category": "Purchase Management", + "license": "AGPL-3", + "summary": "Import UBL XML quotation files", + "author": "Akretion,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/edi", + "depends": ["purchase_order_import", "base_ubl"], + "demo": ["demo/demo_data.xml"], + "installable": True, } diff --git a/purchase_order_import_ubl/demo/demo_data.xml b/purchase_order_import_ubl/demo/demo_data.xml index 32b01a113f..2a0a3b787e 100644 --- a/purchase_order_import_ubl/demo/demo_data.xml +++ b/purchase_order_import_ubl/demo/demo_data.xml @@ -1,87 +1,86 @@ - - + Johnssons byggvaror - - - - + + + + 5 Rådhusgatan PoBox123 11000 Stockholm - - + + - + Pelle Svensson Boss pelle@johnsson.se - + contact - + Swedish trucking - - - + + + bill@svetruck.se 5 Rådhusgatan 2nd floor 11000 Stockholm - + IYT Corporation - - - - + + + + 56A Avon Way Thereabouts ZZ99 1ZZ Bridgtow - - + + - + Fred Churchill fred@iytcorporation.gov.uk - + contact The Terminus - - - - + + + + 56A Avon Way Thereabouts ZZ99 1ZZ Bridgtow - + - + S Massiah smassiah@the-email.co.uk - + contact @@ -89,21 +88,21 @@ Gentofte Kommune - - - - + + + + 161 Bernstorffsvej 2920 Charlottenlund - - + + - + Joe Delivery - + contact diff --git a/purchase_order_import_ubl/tests/files/order_response_tmpl.xml b/purchase_order_import_ubl/tests/files/order_response_tmpl.xml index f52e2ad017..22044d527a 100644 --- a/purchase_order_import_ubl/tests/files/order_response_tmpl.xml +++ b/purchase_order_import_ubl/tests/files/order_response_tmpl.xml @@ -1,5 +1,9 @@ - - + + 2.1 PO07337 2020-02-04 @@ -101,7 +105,8 @@ line_1 Note1 line_1 Note2 {line_1_qty} - {line_1_backorder_qty} + {line_1_backorder_qty} {line_1_status_code} 228.2 @@ -124,7 +129,8 @@ line_2 Note1 line_2 Note2 {line_2_qty} - {line_2_backorder_qty} + {line_2_backorder_qty} {line_2_status_code} 1117.07 diff --git a/purchase_order_import_ubl/tests/test_order_response_import.py b/purchase_order_import_ubl/tests/test_order_response_import.py index aa8fe2d9ab..cefff490c2 100644 --- a/purchase_order_import_ubl/tests/test_order_response_import.py +++ b/purchase_order_import_ubl/tests/test_order_response_import.py @@ -1,27 +1,24 @@ -# -*- coding: utf-8 -*- # Copyright 2020 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo.tools import file_open -from odoo.addons.purchase_order_import.wizard.order_response_import import ( - ORDER_RESPONSE_STATUS_ACK, - LINE_STATUS_ACCEPTED, -) + from odoo.addons.purchase_order_import.tests.test_order_response_import import ( TestOrderResponseImportCommon, ) +from odoo.addons.purchase_order_import.wizard.order_response_import import ( + LINE_STATUS_ACCEPTED, + ORDER_RESPONSE_STATUS_ACK, +) + from ..wizard.order_response_import import ( - _ORDER_RESPONSE_CODE_TO_STATUS, _ORDER_LINE_STATUS_TO_STATUS, + _ORDER_RESPONSE_CODE_TO_STATUS, ) -_STATUS_TO_RESPONSE_CODE = { - p[1]: p[0] for p in _ORDER_RESPONSE_CODE_TO_STATUS.items() -} +_STATUS_TO_RESPONSE_CODE = {p[1]: p[0] for p in _ORDER_RESPONSE_CODE_TO_STATUS.items()} -_STATUS_TO_LINE_STATUS = { - p[1]: p[0] for p in _ORDER_LINE_STATUS_TO_STATUS.items() -} +_STATUS_TO_LINE_STATUS = {p[1]: p[0] for p in _ORDER_LINE_STATUS_TO_STATUS.items()} class TestOrderResponseImport(TestOrderResponseImportCommon): @@ -45,9 +42,7 @@ def test_01(self): All the fields are filled into the internal data structure. """ xml_content = self.order_response_xml.format( - order_response_code=_STATUS_TO_RESPONSE_CODE[ - ORDER_RESPONSE_STATUS_ACK - ], + order_response_code=_STATUS_TO_RESPONSE_CODE[ORDER_RESPONSE_STATUS_ACK], order_id=self.purchase_order.name, line_1_id=self.line1.id, line_1_qty=self.line1.product_qty, @@ -58,9 +53,7 @@ def test_01(self): line_2_backorder_qty=0, line_2_status_code=_STATUS_TO_LINE_STATUS[LINE_STATUS_ACCEPTED], ) - result = self.OrderResponseImport.parse_order_response( - xml_content, "test.xml" - ) + result = self.OrderResponseImport.parse_order_response(xml_content, "test.xml") attachments = result.pop("attachments") self.assertTrue(attachments.get("test.xml")) expected = { diff --git a/purchase_order_import_ubl/tests/test_ubl_order_import.py b/purchase_order_import_ubl/tests/test_ubl_order_import.py index 014c08d6c6..5a2be42df1 100644 --- a/purchase_order_import_ubl/tests/test_ubl_order_import.py +++ b/purchase_order_import_ubl/tests/test_ubl_order_import.py @@ -1,34 +1,35 @@ -# -*- coding: utf-8 -*- # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import base64 + from odoo.tests.common import TransactionCase from odoo.tools import file_open -import base64 class TestUblOrderImport(TransactionCase): - def test_ubl_order_import(self): tests = { - 'quote-PO00004.pdf': { - 'po_to_update': self.env.ref('purchase.purchase_order_4'), - 'incoterm': self.env.ref('stock.incoterm_DDU'), - }, - } - poio = self.env['purchase.order.import'] + "quote-PO00004.pdf": { + "po_to_update": self.env.ref("purchase.purchase_order_4"), + "incoterm": self.env.ref("stock.incoterm_DDU"), + }, + } + poio = self.env["purchase.order.import"] for filename, res in tests.iteritems(): - po = res['po_to_update'] + po = res["po_to_update"] - f = file_open( - 'purchase_order_import_ubl/tests/files/' + filename, 'rb') + f = file_open("purchase_order_import_ubl/tests/files/" + filename, "rb") quote_file = f.read() wiz = poio.with_context( - active_model='purchase.order', active_id=po.id).create({ - 'quote_file': base64.b64encode(quote_file), - 'quote_filename': filename, - }) + active_model="purchase.order", active_id=po.id + ).create( + { + "quote_file": base64.b64encode(quote_file), + "quote_filename": filename, + } + ) f.close() self.assertEqual(wiz.purchase_id, po) wiz.update_rfq_button() - self.assertEqual(po.incoterm_id, res['incoterm']) + self.assertEqual(po.incoterm_id, res["incoterm"]) diff --git a/purchase_order_import_ubl/wizard/order_response_import.py b/purchase_order_import_ubl/wizard/order_response_import.py index 4d13919174..87c1129c0f 100644 --- a/purchase_order_import_ubl/wizard/order_response_import.py +++ b/purchase_order_import_ubl/wizard/order_response_import.py @@ -1,21 +1,21 @@ -# -*- coding: utf-8 -*- # Copyright 2020 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, models, _ +import logging + +from odoo import _, api, models from odoo.exceptions import UserError + from odoo.addons.purchase_order_import.wizard.order_response_import import ( - ORDER_RESPONSE_STATUS_ACK, - ORDER_RESPONSE_STATUS_ACCEPTED, - ORDER_RESPONSE_STATUS_REJECTED, - ORDER_RESPONSE_STATUS_CONDITIONAL, LINE_STATUS_ACCEPTED, - LINE_STATUS_REJECTED, LINE_STATUS_AMEND, + LINE_STATUS_REJECTED, + ORDER_RESPONSE_STATUS_ACCEPTED, + ORDER_RESPONSE_STATUS_ACK, + ORDER_RESPONSE_STATUS_CONDITIONAL, + ORDER_RESPONSE_STATUS_REJECTED, ) -import logging - logger = logging.getLogger(__name__) @@ -66,9 +66,7 @@ def parse_line_status_code(self, line, ns): code = code_xpath and len(code_xpath) and code_xpath[0].text status = _ORDER_LINE_STATUS_TO_STATUS.get(code) if not status: - raise UserError( - _("Unsupported line status code found '%s'") % code - ) + raise UserError(_("Unsupported line status code found '%s'") % code) return status @api.model @@ -91,7 +89,7 @@ def parse_ubl_order_response_line(self, line, ns): "uom": {"unece_code": qty_xpath[0].attrib.get("unitCode")}, "note": self.parse_note_path(note_xpath), "status": self.parse_line_status_code(line_item, ns), - "backorder_qty": backorder_qty + "backorder_qty": backorder_qty, } return res_line @@ -123,12 +121,8 @@ def parse_ubl_order_response(self, xml_root): ns = xml_root.nsmap main_xmlns = ns.pop(None) ns["main"] = main_xmlns - date_xpath = xml_root.xpath( - "/main:OrderResponse/cbc:IssueDate", namespaces=ns - ) - time_xpath = xml_root.xpath( - "/main:OrderResponse/cbc:IssueTime", namespaces=ns - ) + date_xpath = xml_root.xpath("/main:OrderResponse/cbc:IssueDate", namespaces=ns) + time_xpath = xml_root.xpath("/main:OrderResponse/cbc:IssueTime", namespaces=ns) order_reference_xpath = xml_root.xpath( "/main:OrderResponse/cac:OrderReference/cbc:ID", namespaces=ns ) @@ -140,9 +134,7 @@ def parse_ubl_order_response(self, xml_root): if currency_xpath: currency_code = currency_xpath[0].text else: - currency_xpath = xml_root.xpath( - "//cbc:LineExtensionAmount", namespaces=ns - ) + currency_xpath = xml_root.xpath("//cbc:LineExtensionAmount", namespaces=ns) if currency_xpath: currency_code = currency_xpath[0].attrib.get("currencyID") supplier_xpath = xml_root.xpath( @@ -160,12 +152,8 @@ def parse_ubl_order_response(self, xml_root): # We only take the "official references" for company_dict if company_dict_full.get("vat"): company_dict = {"vat": company_dict_full["vat"]} - note_xpath = xml_root.xpath( - "/main:OrderResponse/cbc:Note", namespaces=ns - ) - lines_xpath = xml_root.xpath( - "/main:OrderResponse/cac:OrderLine", namespaces=ns - ) + note_xpath = xml_root.xpath("/main:OrderResponse/cbc:Note", namespaces=ns) + lines_xpath = xml_root.xpath("/main:OrderResponse/cac:OrderLine", namespaces=ns) res_lines = [] for line in lines_xpath: res_lines.append(self.parse_ubl_order_response_line(line, ns)) diff --git a/purchase_order_import_ubl/wizard/purchase_order_import.py b/purchase_order_import_ubl/wizard/purchase_order_import.py index faf8bbc601..01e1404273 100644 --- a/purchase_order_import_ubl/wizard/purchase_order_import.py +++ b/purchase_order_import_ubl/wizard/purchase_order_import.py @@ -1,106 +1,107 @@ -# -*- coding: utf-8 -*- # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, api -from odoo.tools import float_is_zero import logging +from odoo import api, models +from odoo.tools import float_is_zero + logger = logging.getLogger(__name__) class PurchaseOrderImport(models.TransientModel): - _name = 'purchase.order.import' - _inherit = ['purchase.order.import', 'base.ubl'] + _name = "purchase.order.import" + _inherit = ["purchase.order.import", "base.ubl"] @api.model def parse_xml_quote(self, xml_root): - start_tag = '{urn:oasis:names:specification:ubl:schema:xsd:' - if xml_root.tag == start_tag + 'Quotation-2}Quotation': + start_tag = "{urn:oasis:names:specification:ubl:schema:xsd:" + if xml_root.tag == start_tag + "Quotation-2}Quotation": return self.parse_ubl_quote(xml_root) else: return super(PurchaseOrderImport, self).parse_xml_quote(xml_root) @api.model def parse_ubl_quote_line(self, line, ns): - qty_prec = self.env['decimal.precision'].precision_get( - 'Product Unit of Measure') - line_item = line.xpath('cac:LineItem', namespaces=ns)[0] + qty_prec = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + line_item = line.xpath("cac:LineItem", namespaces=ns)[0] # line_id_xpath = line_item.xpath('cbc:ID', namespaces=ns) # line_id = line_id_xpath[0].text - qty_xpath = line_item.xpath('cbc:Quantity', namespaces=ns) + qty_xpath = line_item.xpath("cbc:Quantity", namespaces=ns) qty = float(qty_xpath[0].text) price_unit = 0.0 subtotal_without_tax_xpath = line_item.xpath( - 'cbc:LineExtensionAmount', namespaces=ns) + "cbc:LineExtensionAmount", namespaces=ns + ) if subtotal_without_tax_xpath: subtotal_without_tax = float(subtotal_without_tax_xpath[0].text) if not float_is_zero(qty, precision_digits=qty_prec): price_unit = subtotal_without_tax / qty else: - price_xpath = line_item.xpath( - 'cac:Price/cbc:PriceAmount', namespaces=ns) + price_xpath = line_item.xpath("cac:Price/cbc:PriceAmount", namespaces=ns) if price_xpath: price_unit = float(price_xpath[0].text) res_line = { - 'product': self.ubl_parse_product(line_item, ns), - 'qty': qty, - 'uom': {'unece_code': qty_xpath[0].attrib.get('unitCode')}, - 'price_unit': price_unit, - } + "product": self.ubl_parse_product(line_item, ns), + "qty": qty, + "uom": {"unece_code": qty_xpath[0].attrib.get("unitCode")}, + "price_unit": price_unit, + } return res_line @api.model def parse_ubl_quote(self, xml_root): ns = xml_root.nsmap main_xmlns = ns.pop(None) - ns['main'] = main_xmlns - date_xpath = xml_root.xpath( - '/main:Quotation/cbc:IssueDate', namespaces=ns) + ns["main"] = main_xmlns + date_xpath = xml_root.xpath("/main:Quotation/cbc:IssueDate", namespaces=ns) currency_xpath = xml_root.xpath( - '/main:Quotation/cbc:PricingCurrencyCode', namespaces=ns) + "/main:Quotation/cbc:PricingCurrencyCode", namespaces=ns + ) currency_code = False if currency_xpath: currency_code = currency_xpath[0].text else: - currency_xpath = xml_root.xpath( - '//cbc:LineExtensionAmount', namespaces=ns) + currency_xpath = xml_root.xpath("//cbc:LineExtensionAmount", namespaces=ns) if currency_xpath: - currency_code = currency_xpath[0].attrib.get('currencyID') + currency_code = currency_xpath[0].attrib.get("currencyID") supplier_xpath = xml_root.xpath( - '/main:Quotation/cac:SellerSupplierParty', namespaces=ns) + "/main:Quotation/cac:SellerSupplierParty", namespaces=ns + ) supplier_dict = self.ubl_parse_supplier_party(supplier_xpath[0], ns) customer_xpath_party = xml_root.xpath( - '/main:Quotation/cac:BuyerCustomerParty/cac:Party', namespaces=ns) + "/main:Quotation/cac:BuyerCustomerParty/cac:Party", namespaces=ns + ) company_dict_full = self.ubl_parse_party(customer_xpath_party[0], ns) company_dict = {} # We only take the "official references" for company_dict - if company_dict_full.get('vat'): - company_dict = {'vat': company_dict_full['vat']} + if company_dict_full.get("vat"): + company_dict = {"vat": company_dict_full["vat"]} delivery_term_xpath = xml_root.xpath( - "/main:Quotation/cac:DeliveryTerms", namespaces=ns) + "/main:Quotation/cac:DeliveryTerms", namespaces=ns + ) if delivery_term_xpath: incoterm_dict = self.ubl_parse_incoterm(delivery_term_xpath[0], ns) else: incoterm_dict = {} - note_xpath = xml_root.xpath( - '/main:Quotation/cbc:Note', namespaces=ns) - lines_xpath = xml_root.xpath( - '/main:Quotation/cac:QuotationLine', namespaces=ns) + note_xpath = xml_root.xpath("/main:Quotation/cbc:Note", namespaces=ns) + lines_xpath = xml_root.xpath("/main:Quotation/cac:QuotationLine", namespaces=ns) res_lines = [] for line in lines_xpath: res_lines.append(self.parse_ubl_quote_line(line, ns)) # TODO : add charges res = { - 'partner': supplier_dict, - 'company': company_dict, - 'currency': {'iso': currency_code}, - 'date': date_xpath[0].text, - 'incoterm': incoterm_dict, - 'note': note_xpath and note_xpath[0].text or False, - 'lines': res_lines, + "partner": supplier_dict, + "company": company_dict, + "currency": {"iso": currency_code}, + "date": date_xpath[0].text, + "incoterm": incoterm_dict, + "note": note_xpath and note_xpath[0].text or False, + "lines": res_lines, } # Stupid hack to remove invalid VAT of sample files - if res['partner']['vat'] in ['DK18296799']: - res['partner'].pop('vat') + if res["partner"]["vat"] in ["DK18296799"]: + res["partner"].pop("vat") return res diff --git a/setup/purchase_order_import_ubl/odoo/__init__.py b/setup/purchase_order_import_ubl/odoo/__init__.py new file mode 100644 index 0000000000..de40ea7ca0 --- /dev/null +++ b/setup/purchase_order_import_ubl/odoo/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/purchase_order_import_ubl/odoo/addons/__init__.py b/setup/purchase_order_import_ubl/odoo/addons/__init__.py new file mode 100644 index 0000000000..de40ea7ca0 --- /dev/null +++ b/setup/purchase_order_import_ubl/odoo/addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/purchase_order_import_ubl/odoo/addons/purchase_order_import_ubl b/setup/purchase_order_import_ubl/odoo/addons/purchase_order_import_ubl new file mode 120000 index 0000000000..b23d88086c --- /dev/null +++ b/setup/purchase_order_import_ubl/odoo/addons/purchase_order_import_ubl @@ -0,0 +1 @@ +../../../../purchase_order_import_ubl \ No newline at end of file diff --git a/setup/purchase_order_import_ubl/setup.py b/setup/purchase_order_import_ubl/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/purchase_order_import_ubl/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 56d7a4225a8a48e35b4f748bab3e500b6a916c58 Mon Sep 17 00:00:00 2001 From: Robin Conjour Date: Mon, 12 Apr 2021 11:07:58 +0200 Subject: [PATCH 19/19] [MIG] purchase_order_import_ubl: Migration to 14.0 --- purchase_order_import_ubl/__manifest__.py | 2 +- purchase_order_import_ubl/demo/demo_data.xml | 51 ++++++++++++++----- .../readme/CONTRIBUTORS.rst | 2 + .../readme/DESCRIPTION.rst | 6 +++ .../tests/files/order_response_tmpl.xml | 20 ++++---- .../tests/test_order_response_import.py | 30 ++++++----- .../tests/test_ubl_order_import.py | 8 ++- .../odoo/__init__.py | 1 - .../odoo/addons/__init__.py | 1 - 9 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 purchase_order_import_ubl/readme/CONTRIBUTORS.rst create mode 100644 purchase_order_import_ubl/readme/DESCRIPTION.rst delete mode 100644 setup/purchase_order_import_ubl/odoo/__init__.py delete mode 100644 setup/purchase_order_import_ubl/odoo/addons/__init__.py diff --git a/purchase_order_import_ubl/__manifest__.py b/purchase_order_import_ubl/__manifest__.py index 9a2ff25044..fce981f1d6 100644 --- a/purchase_order_import_ubl/__manifest__.py +++ b/purchase_order_import_ubl/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Quotation Order UBL Import", - "version": "10.0.2.0.2", + "version": "14.0.1.0.0", "category": "Purchase Management", "license": "AGPL-3", "summary": "Import UBL XML quotation files", diff --git a/purchase_order_import_ubl/demo/demo_data.xml b/purchase_order_import_ubl/demo/demo_data.xml index 2a0a3b787e..5aaaf87461 100644 --- a/purchase_order_import_ubl/demo/demo_data.xml +++ b/purchase_order_import_ubl/demo/demo_data.xml @@ -5,8 +5,8 @@ Johnssons byggvaror - - + 1 + 0 5 Rådhusgatan PoBox123 @@ -21,7 +21,7 @@ Pelle Svensson Boss pelle@johnsson.se - + 1 contact @@ -29,8 +29,8 @@ Swedish trucking - - + 0 + 0 bill@svetruck.se 5 Rådhusgatan 2nd floor @@ -43,8 +43,8 @@ IYT Corporation - - + 1 + 0 56A Avon Way Thereabouts @@ -58,7 +58,7 @@ Fred Churchill fred@iytcorporation.gov.uk - + 1 contact @@ -66,8 +66,8 @@ The Terminus - - + 1 + 0 56A Avon Way Thereabouts @@ -80,7 +80,7 @@ S Massiah smassiah@the-email.co.uk - + 1 contact @@ -89,8 +89,8 @@ Gentofte Kommune - - + 1 + 0 161 Bernstorffsvej 2920 @@ -102,9 +102,32 @@ Joe Delivery - + 1 contact + + Delta PC + + 1 + info@yourcompany.example.com + + + + + + DDU + DELIVERED DUTY UNPAID + + + + PROD_DEL02 + PROD_DEL02 + + + + MBi9 + MBi9 + diff --git a/purchase_order_import_ubl/readme/CONTRIBUTORS.rst b/purchase_order_import_ubl/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..53a1c0a2b6 --- /dev/null +++ b/purchase_order_import_ubl/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Alexis de Lattre +* Robin Conjour diff --git a/purchase_order_import_ubl/readme/DESCRIPTION.rst b/purchase_order_import_ubl/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..d3bd76d415 --- /dev/null +++ b/purchase_order_import_ubl/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module adds support for the import of electronic quotations that comply with the `Universal Business Language (UBL) `_ standard. The UBL standard became the `ISO/IEC 19845 `_ standard in December 2015 (cf the `official announce _`). The file can be in two formats: + +* UBL XML file, +* PDF file with an embedded UBL XML file. + +You can use the OCA module *sale_order_ubl* to generate PDF quotations with an embedded UBL XML file. diff --git a/purchase_order_import_ubl/tests/files/order_response_tmpl.xml b/purchase_order_import_ubl/tests/files/order_response_tmpl.xml index 22044d527a..abe1d544eb 100644 --- a/purchase_order_import_ubl/tests/files/order_response_tmpl.xml +++ b/purchase_order_import_ubl/tests/files/order_response_tmpl.xml @@ -8,12 +8,12 @@ PO07337 2020-02-04 22:10:30 - {order_response_code} + %(order_response_code)s Note1 Note2 EUR - {order_id} + %(order_id)s 79201 @@ -101,13 +101,13 @@ - {line_1_id} + %(line_1_id)s line_1 Note1 line_1 Note2 - {line_1_qty} + %(line_1_qty)s {line_1_backorder_qty} - {line_1_status_code} + >%(line_1_backorder_qty)s + %(line_1_status_code)s 228.2 45.64 @@ -125,13 +125,13 @@ - {line_2_id} + %(line_2_id)s line_2 Note1 line_2 Note2 - {line_2_qty} + %(line_2_qty)s {line_2_backorder_qty} - {line_2_status_code} + >%(line_2_backorder_qty)s + %(line_2_status_code)s 1117.07 65.71 diff --git a/purchase_order_import_ubl/tests/test_order_response_import.py b/purchase_order_import_ubl/tests/test_order_response_import.py index cefff490c2..ac8a9654e4 100644 --- a/purchase_order_import_ubl/tests/test_order_response_import.py +++ b/purchase_order_import_ubl/tests/test_order_response_import.py @@ -41,18 +41,24 @@ def test_01(self): Expected result: All the fields are filled into the internal data structure. """ - xml_content = self.order_response_xml.format( - order_response_code=_STATUS_TO_RESPONSE_CODE[ORDER_RESPONSE_STATUS_ACK], - order_id=self.purchase_order.name, - line_1_id=self.line1.id, - line_1_qty=self.line1.product_qty, - line_1_backorder_qty=0, - line_1_status_code=_STATUS_TO_LINE_STATUS[LINE_STATUS_ACCEPTED], - line_2_id=self.line2.id, - line_2_qty=self.line2.product_qty, - line_2_backorder_qty=0, - line_2_status_code=_STATUS_TO_LINE_STATUS[LINE_STATUS_ACCEPTED], - ) + xml_content = self.order_response_xml % { + b"order_response_code": _STATUS_TO_RESPONSE_CODE[ + ORDER_RESPONSE_STATUS_ACK + ].encode("utf8"), + b"order_id": self.purchase_order.name.encode("utf8"), + b"line_1_id": str(self.line1.id).encode("utf8"), + b"line_1_qty": str(self.line1.product_qty).encode("utf8"), + b"line_1_backorder_qty": b"0", + b"line_1_status_code": _STATUS_TO_LINE_STATUS[LINE_STATUS_ACCEPTED].encode( + "utf8" + ), + b"line_2_id": str(self.line2.id).encode("utf8"), + b"line_2_qty": str(self.line2.product_qty).encode("utf8"), + b"line_2_backorder_qty": b"0", + b"line_2_status_code": _STATUS_TO_LINE_STATUS[LINE_STATUS_ACCEPTED].encode( + "utf8" + ), + } result = self.OrderResponseImport.parse_order_response(xml_content, "test.xml") attachments = result.pop("attachments") self.assertTrue(attachments.get("test.xml")) diff --git a/purchase_order_import_ubl/tests/test_ubl_order_import.py b/purchase_order_import_ubl/tests/test_ubl_order_import.py index 5a2be42df1..ede59eee86 100644 --- a/purchase_order_import_ubl/tests/test_ubl_order_import.py +++ b/purchase_order_import_ubl/tests/test_ubl_order_import.py @@ -9,14 +9,18 @@ class TestUblOrderImport(TransactionCase): def test_ubl_order_import(self): + # Modify partner of used purchase order + self.env.ref("purchase.purchase_order_4").write( + {"partner_id": self.env.ref("purchase_order_import_ubl.deltapc").id} + ) tests = { "quote-PO00004.pdf": { "po_to_update": self.env.ref("purchase.purchase_order_4"), - "incoterm": self.env.ref("stock.incoterm_DDU"), + "incoterm": self.env.ref("purchase_order_import_ubl.incoterm_DDU"), }, } poio = self.env["purchase.order.import"] - for filename, res in tests.iteritems(): + for filename, res in tests.items(): po = res["po_to_update"] f = file_open("purchase_order_import_ubl/tests/files/" + filename, "rb") diff --git a/setup/purchase_order_import_ubl/odoo/__init__.py b/setup/purchase_order_import_ubl/odoo/__init__.py deleted file mode 100644 index de40ea7ca0..0000000000 --- a/setup/purchase_order_import_ubl/odoo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/purchase_order_import_ubl/odoo/addons/__init__.py b/setup/purchase_order_import_ubl/odoo/addons/__init__.py deleted file mode 100644 index de40ea7ca0..0000000000 --- a/setup/purchase_order_import_ubl/odoo/addons/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__('pkg_resources').declare_namespace(__name__)