From 6026b0ff4d148d9d27516534dfb84b2aa4be19bf Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Thu, 19 Oct 2023 12:50:35 +0700 Subject: [PATCH 01/11] [IMP] base_edifact: support format latin-1 --- base_edifact/models/edifact.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/base_edifact/models/edifact.py b/base_edifact/models/edifact.py index 5291bb3a7b..23ed0ac16c 100644 --- a/base_edifact/models/edifact.py +++ b/base_edifact/models/edifact.py @@ -70,7 +70,10 @@ def pydifact_obj(self, docu): @api.model def _loads_edifact(self, order_file): - interchange = Interchange.from_str(order_file.decode()) + try: + interchange = Interchange.from_str(order_file.decode()) + except UnicodeDecodeError: + interchange = Interchange.from_str(order_file.decode('latin-1')) return interchange @api.model From 454479ee761f327e913d6a4400349a82cf72e20a Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Fri, 20 Oct 2023 10:51:43 +0700 Subject: [PATCH 02/11] [IMP] base_edifact: Supports more datetime formats --- base_edifact/models/edifact.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/base_edifact/models/edifact.py b/base_edifact/models/edifact.py index 23ed0ac16c..8149cead71 100644 --- a/base_edifact/models/edifact.py +++ b/base_edifact/models/edifact.py @@ -85,7 +85,11 @@ def _get_msg_type(self, interchange): @api.model def map2odoo_date(self, dt): # '102' - dtt = datetime.datetime.strptime(dt[1], "%Y%m%d") + date_format = "%Y%m%d%H%M%S" + length_dt = len(dt[1]) + if (length_dt % 2 == 0 and length_dt in range(8, 13, 2)): + date_format = date_format[0 : length_dt - 2] + dtt = datetime.datetime.strptime(dt[1], date_format) return dtt.date() @api.model From db1db41656d947186cfdb7fe0ae0cb5194c61d61 Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Fri, 20 Oct 2023 10:59:26 +0700 Subject: [PATCH 03/11] [IMP] base_edifact: fallback if address information is missing --- base_edifact/models/edifact.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/base_edifact/models/edifact.py b/base_edifact/models/edifact.py index 8149cead71..73ce094bde 100644 --- a/base_edifact/models/edifact.py +++ b/base_edifact/models/edifact.py @@ -145,22 +145,30 @@ def map2odoo_address(self, seg): nameref = self.MAP_AGENCY_CODE_2_RES_PARTNER_NAMEREF.get(agency_code, "gln") address["partner"][nameref] = party_id d = address["address"] + # Fallback if address information is missing + try: + if isinstance(seg, Segment): + lenght_seg = len(seg.elements) + else: + lenght_seg = len(seg) + except ValueError: + lenght_seg = 0 # PARTY NAME - if bool(seg[2]): + if lenght_seg > 2 and bool(seg[2]): d["name"] = seg[2] - if bool(seg[3]): + if lenght_seg > 3 and bool(seg[3]): d["name"] = "{}{}".format(f"{d['name']}. " if d.get("name") else "", seg[3]) - if bool(seg[4]): + if lenght_seg > 4 and bool(seg[4]): # Street address and/or PO Box number in a structured address: one to three lines. d["street"] = seg[4] - if bool(seg[5]): + if lenght_seg > 5 and bool(seg[5]): d["city"] = seg[5] - if bool(seg[6]): + if lenght_seg > 6 and bool(seg[6]): # Country sub-entity identification d["state_code"] = seg[6] - if bool(seg[7]): + if lenght_seg > 7 and bool(seg[7]): d["zip"] = seg[7] - if bool(seg[8]): + if lenght_seg > 8 and bool(seg[8]): # Country, coded ISO 3166 d["country_code"] = seg[8] From 0999a1629dc6a27bd652cf2098d5809d9b7f20a0 Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Fri, 20 Oct 2023 11:02:33 +0700 Subject: [PATCH 04/11] [IMP] base_edifact: fallback on SA if no EAN given --- base_edifact/models/edifact.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/base_edifact/models/edifact.py b/base_edifact/models/edifact.py index 73ce094bde..c1ff6fe7ec 100644 --- a/base_edifact/models/edifact.py +++ b/base_edifact/models/edifact.py @@ -187,16 +187,22 @@ def map2odoo_currency(self, seg): } @api.model - def map2odoo_product(self, seg): + def map2odoo_product(self, seg, pia = None): """ :seg: LIN segment ['1', '', ['8885583503464', 'EN']] EN. International Article Numbering Association (EAN) UP. UPC (Universal product code) SRV. GTIN + :product_info: PIA segment + ['5', ['1276', 'SA', '', '9']] + SA. Supplier's Article Number """ product = seg[2] pct = product[1] + # Fallback on SA if no EAN given + if not product[0] and pia[1][0]: + return dict(code=pia[1][0]) return dict(code=product[0]) if pct == "SRV" else dict(barcode=product[0]) @api.model From 14c349455252ea7b17ffe0dedebd80cf3a0530eb Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Fri, 20 Oct 2023 11:06:28 +0700 Subject: [PATCH 05/11] [IMP] base_edifact: Add price with Price qualifier is AAB --- base_edifact/models/edifact.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/base_edifact/models/edifact.py b/base_edifact/models/edifact.py index c1ff6fe7ec..d8fb540c40 100644 --- a/base_edifact/models/edifact.py +++ b/base_edifact/models/edifact.py @@ -214,14 +214,18 @@ def map2odoo_qty(self, seg): return float(seg[0][1]) @api.model - def map2odoo_unit_price(self, seg): + def map2odoo_unit_price(self, seg = None): """ 'PRI' EDI segment: [['AAA', '19.75']] Price qualifier: * 'AAA'. Calculation net * 'AAB'. Calculation gross """ - pri = seg[0] - if pri[0] == "AAA": - return float(pri[1]) - return 0.0 + if seg: + pri = seg[0] + if pri[0] == "AAA": + return float(pri[1]) + # TODO: Add price calculation formula + if pri[0] == "AAB": + return float(pri[1]) + return 0.0 \ No newline at end of file From 7f9c7c45c6686b33b204b7ab835d0c12d60cd153 Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Fri, 20 Oct 2023 11:42:44 +0700 Subject: [PATCH 06/11] [IMP] base_edifact: Add new test file and add test with format latin-1 --- .../files/test_orders_-_no_PRI_segments.txt | 24 +++++++ .../test_orders_-_no_ean_in_LIN_segments.txt | 69 +++++++++++++++++++ base_edifact/tests/test_base_edifact.py | 6 ++ 3 files changed, 99 insertions(+) create mode 100644 base_edifact/tests/files/test_orders_-_no_PRI_segments.txt create mode 100644 base_edifact/tests/files/test_orders_-_no_ean_in_LIN_segments.txt diff --git a/base_edifact/tests/files/test_orders_-_no_PRI_segments.txt b/base_edifact/tests/files/test_orders_-_no_PRI_segments.txt new file mode 100644 index 0000000000..df83bd5c06 --- /dev/null +++ b/base_edifact/tests/files/test_orders_-_no_PRI_segments.txt @@ -0,0 +1,24 @@ +UNA:+.? ' +UNB+UNOC:3+ENI-CH:14+2256:14+230320:1910+2' +UNH+000011956901+ORDERS:D:96A:UN:EAN008' +BGM+220+COM-004017+9' +DTM+137:20230320:102' +DTM+2:20230321:102' +NAD+BY+5450534008617::91' +NAD+BY+5450534008143::92++PartyName1+Address1+City1++1964' +NAD+SU+2256::9' +NAD+DP+5450534008617::91++PartyName1+Address1+City1++1964' +RFF+API:5450534008617' +LIN+1++9783898307529:EN' +PIA+1+7076:SA::91+30007:BP::92' +QTY+21:8:6' +LIN+2++9783898307538:EN' +PIA+1+7065:SA::91+38812:BP::92' +QTY+21:4:6' +LIN+3++9783898307645:EN' +PIA+1+7056:SA::91+30008:BP::92' +QTY+21:1:24' +UNS+S' +CNT+2:3' +UNT+267+000011956901' +UNZ+1+2' \ No newline at end of file diff --git a/base_edifact/tests/files/test_orders_-_no_ean_in_LIN_segments.txt b/base_edifact/tests/files/test_orders_-_no_ean_in_LIN_segments.txt new file mode 100644 index 0000000000..b71e062363 --- /dev/null +++ b/base_edifact/tests/files/test_orders_-_no_ean_in_LIN_segments.txt @@ -0,0 +1,69 @@ +UNB+UNOC:3+3027800012009:14+7640142640004:14+230320:1438+003274' +UNH+3269+ORDERS:D:96A:UN:EAN008' +BGM+220::9:ORDERS+467819+9' +DTM+137:202303201433:203' +DTM+2:202303220000:203' +NAD+SU+7640142640004::9++Suppliér1+Address1+City1++1762+CH' +RFF+IT:5020' +NAD+BY+5450534008617::ZZ++' +RFF+IT:5132' +CTA+OC+:RADON' +NAD+SF+++' +NAD+IV+5450534008617::ZZ++Suppliér1+Address1+City1++3110+CH' +RFF+IT:5132' +NAD+DP+5450534008617::ZZ++Suppliér1+Address1+City1++3110+CH' +RFF+IT:5132' +RFF+GN:RCS' +CUX+2:EUR:9' +ALC+C++6++FC::9:FRAIS DE PORT' +MOA+23:0' +LIN+1++:EN' +PIA+5+1276:SA::9' +PIA+5+31136:IN::9' +PIA+5+00:VL' +IMD+F+ANM+:::Product1' +IMD+F+79+:::Product1 description' +QTY+21:12:BOU' +QTY+59:6:PCE' +PRI+AAB:5.22::NTP' +LIN+2++:EN' +PIA+5+46630:SA::9' +PIA+5+27952:IN::9' +PIA+5+00:VL' +IMD+F+ANM+:::Product2' +IMD+F+79+:::Product2 Description' +QTY+21:24:BOU' +QTY+59:24:PCE' +PRI+AAB:27.641::NTP' +LIN+3++:EN' +PIA+5+98891 75:SA::9' +PIA+5+22389:IN::9' +PIA+5+00:VL' +IMD+F+ANM+:::Product3' +IMD+F+79+:::Product3 Description' +QTY+21:12:BOU' +QTY+59:6:PCE' +PRI+AAB:51.03::NTP' +LIN+4++:EN' +PIA+5+37230:SA::9' +PIA+5+16344:IN::9' +PIA+5+00:VL' +IMD+F+ANM+:::Product4' +IMD+F+79+:::Product4 Description' +QTY+21:24:BOU' +QTY+59:24:PCE' +PRI+AAB:29.542::NTP' +LIN+5++:EN' +PIA+5+1076:SA::9' +PIA+5+16270:IN::9' +PIA+5+00:VL' +IMD+F+ANM+:::Product5' +IMD+F+79+:::Product5 Description' +QTY+21:90:BOU' +QTY+59:6:PCE' +PRI+AAB:5.22::NTP' +UNS+S' +MOA+125:247.98' +CNT+2:5' +UNT+67+3269' +UNZ+1+003274' \ No newline at end of file diff --git a/base_edifact/tests/test_base_edifact.py b/base_edifact/tests/test_base_edifact.py index fafe8dd490..bb111f8b89 100644 --- a/base_edifact/tests/test_base_edifact.py +++ b/base_edifact/tests/test_base_edifact.py @@ -23,6 +23,12 @@ def test_pydifact_obj(self): obj = self.base_edifact_model.pydifact_obj(edifact_docu) # [1]: to get the list messages, [0]: to get the first list value of the segments self.assertEqual(obj[1]["segments"][0]["BGM"][1], "1AA1TEST") + + def test_pydifact_obj_latin1(self): + edifact_docu = _get_file_content("test_orders_-_no_ean_in_LIN_segments.txt") + obj = self.base_edifact_model.pydifact_obj(edifact_docu) + # [1]: to get the list messages, [3]: to get the third list value of the segments + self.assertEqual(obj[1]["segments"][3]["NAD"][3], "Suppliér1") def test_map2odoo_address(self): """Address segment From 7385bcf4e7da5baa8c30b527df22099a0296b5ec Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Fri, 20 Oct 2023 11:47:33 +0700 Subject: [PATCH 07/11] [IMP] base_edifact: Add test product with fallback on SA if no EAN given --- base_edifact/tests/test_base_edifact.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base_edifact/tests/test_base_edifact.py b/base_edifact/tests/test_base_edifact.py index bb111f8b89..3d23556044 100644 --- a/base_edifact/tests/test_base_edifact.py +++ b/base_edifact/tests/test_base_edifact.py @@ -54,6 +54,12 @@ def test_map2odoo_product(self): product = self.base_edifact_model.map2odoo_product(seg) self.assertEqual(product["barcode"], "8885583503464") + def test_map2odoo_product_pia(self): + seg = ("1", "", ["", "EN"]) + pia = (['5', ['1276', 'SA', '', '9']]) + product = self.base_edifact_model.map2odoo_product(seg, pia) + self.assertEqual(product["code"], "1276") + def test_map2odoo_qty(self): seg = (["21", "2"],) qty = self.base_edifact_model.map2odoo_qty(seg) From 03882eee2cf50c402b912ea60d5797615009113b Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Fri, 20 Oct 2023 11:49:03 +0700 Subject: [PATCH 08/11] [IMP] base_edifact: Add test with no unit price --- base_edifact/tests/test_base_edifact.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/base_edifact/tests/test_base_edifact.py b/base_edifact/tests/test_base_edifact.py index 3d23556044..80b355d28d 100644 --- a/base_edifact/tests/test_base_edifact.py +++ b/base_edifact/tests/test_base_edifact.py @@ -66,6 +66,10 @@ def test_map2odoo_qty(self): self.assertEqual(qty, 2.0) def test_map2odoo_unit_price(self): + # Test with Price qualifier is AAA seg = (["AAA", "19.75"],) unit_price = self.base_edifact_model.map2odoo_unit_price(seg) self.assertEqual(unit_price, 19.75) + # Test with no unit price + unit_price = self.base_edifact_model.map2odoo_unit_price() + self.assertEqual(unit_price, 0.0) From 9efcb2a48cb30d47b6d65a2e5f9f83df0d9df948 Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Fri, 20 Oct 2023 11:53:18 +0700 Subject: [PATCH 09/11] [IMP] base_edifact: Add test with datetime --- base_edifact/tests/test_base_edifact.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base_edifact/tests/test_base_edifact.py b/base_edifact/tests/test_base_edifact.py index 80b355d28d..8a122d2bf9 100644 --- a/base_edifact/tests/test_base_edifact.py +++ b/base_edifact/tests/test_base_edifact.py @@ -73,3 +73,9 @@ def test_map2odoo_unit_price(self): # Test with no unit price unit_price = self.base_edifact_model.map2odoo_unit_price() self.assertEqual(unit_price, 0.0) + + def test_map2odoo_date(self): + # Test with date format YYYY-MM-DD HH:MM + date_str = (['137', '202303201433', '203']) + date = self.base_edifact_model.map2odoo_date(date_str) + self.assertEqual(str(date), "2023-03-20") From 3f7a7be41d09f834211d2fe29e54614d65e9f885 Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Mon, 23 Oct 2023 16:05:32 +0700 Subject: [PATCH 10/11] [IMP] base_edifact: Add method get description --- base_edifact/models/edifact.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/base_edifact/models/edifact.py b/base_edifact/models/edifact.py index d8fb540c40..7c11880b42 100644 --- a/base_edifact/models/edifact.py +++ b/base_edifact/models/edifact.py @@ -228,4 +228,16 @@ def map2odoo_unit_price(self, seg = None): # TODO: Add price calculation formula if pri[0] == "AAB": return float(pri[1]) - return 0.0 \ No newline at end of file + return 0.0 + + @api.model + def map2odoo_description(self, seg): + """ + 'IMD' EDI segment: ['F', '79', ['', '', '', 'Description']] + F: Label + 79: Other description + """ + if seg: + description = seg[2][3] + return description + return None \ No newline at end of file From 2162cb959254d672d2bb0a3a09ac8c48c73b2187 Mon Sep 17 00:00:00 2001 From: Vo Hong Thien Date: Mon, 23 Oct 2023 16:09:47 +0700 Subject: [PATCH 11/11] [IMP] base_edifact: Add unit test for test_map2odoo_description --- base_edifact/models/edifact.py | 13 +++++++------ .../tests/files/test_orders_-_no_PRI_segments.txt | 2 +- .../files/test_orders_-_no_ean_in_LIN_segments.txt | 2 +- base_edifact/tests/test_base_edifact.py | 11 ++++++++--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/base_edifact/models/edifact.py b/base_edifact/models/edifact.py index 7c11880b42..0c737199df 100644 --- a/base_edifact/models/edifact.py +++ b/base_edifact/models/edifact.py @@ -70,10 +70,11 @@ def pydifact_obj(self, docu): @api.model def _loads_edifact(self, order_file): + # TODO: use chardet library for get encoding try: interchange = Interchange.from_str(order_file.decode()) except UnicodeDecodeError: - interchange = Interchange.from_str(order_file.decode('latin-1')) + interchange = Interchange.from_str(order_file.decode("latin-1")) return interchange @api.model @@ -87,7 +88,7 @@ def map2odoo_date(self, dt): # '102' date_format = "%Y%m%d%H%M%S" length_dt = len(dt[1]) - if (length_dt % 2 == 0 and length_dt in range(8, 13, 2)): + if length_dt % 2 == 0 and length_dt in range(8, 13, 2): date_format = date_format[0 : length_dt - 2] dtt = datetime.datetime.strptime(dt[1], date_format) return dtt.date() @@ -187,7 +188,7 @@ def map2odoo_currency(self, seg): } @api.model - def map2odoo_product(self, seg, pia = None): + def map2odoo_product(self, seg, pia=None): """ :seg: LIN segment ['1', '', ['8885583503464', 'EN']] @@ -214,7 +215,7 @@ def map2odoo_qty(self, seg): return float(seg[0][1]) @api.model - def map2odoo_unit_price(self, seg = None): + def map2odoo_unit_price(self, seg=None): """ 'PRI' EDI segment: [['AAA', '19.75']] Price qualifier: @@ -229,7 +230,7 @@ def map2odoo_unit_price(self, seg = None): if pri[0] == "AAB": return float(pri[1]) return 0.0 - + @api.model def map2odoo_description(self, seg): """ @@ -240,4 +241,4 @@ def map2odoo_description(self, seg): if seg: description = seg[2][3] return description - return None \ No newline at end of file + return None diff --git a/base_edifact/tests/files/test_orders_-_no_PRI_segments.txt b/base_edifact/tests/files/test_orders_-_no_PRI_segments.txt index df83bd5c06..87fa0f022f 100644 --- a/base_edifact/tests/files/test_orders_-_no_PRI_segments.txt +++ b/base_edifact/tests/files/test_orders_-_no_PRI_segments.txt @@ -21,4 +21,4 @@ QTY+21:1:24' UNS+S' CNT+2:3' UNT+267+000011956901' -UNZ+1+2' \ No newline at end of file +UNZ+1+2' diff --git a/base_edifact/tests/files/test_orders_-_no_ean_in_LIN_segments.txt b/base_edifact/tests/files/test_orders_-_no_ean_in_LIN_segments.txt index b71e062363..01a222b868 100644 --- a/base_edifact/tests/files/test_orders_-_no_ean_in_LIN_segments.txt +++ b/base_edifact/tests/files/test_orders_-_no_ean_in_LIN_segments.txt @@ -66,4 +66,4 @@ UNS+S' MOA+125:247.98' CNT+2:5' UNT+67+3269' -UNZ+1+003274' \ No newline at end of file +UNZ+1+003274' diff --git a/base_edifact/tests/test_base_edifact.py b/base_edifact/tests/test_base_edifact.py index 8a122d2bf9..75bf12c309 100644 --- a/base_edifact/tests/test_base_edifact.py +++ b/base_edifact/tests/test_base_edifact.py @@ -23,7 +23,7 @@ def test_pydifact_obj(self): obj = self.base_edifact_model.pydifact_obj(edifact_docu) # [1]: to get the list messages, [0]: to get the first list value of the segments self.assertEqual(obj[1]["segments"][0]["BGM"][1], "1AA1TEST") - + def test_pydifact_obj_latin1(self): edifact_docu = _get_file_content("test_orders_-_no_ean_in_LIN_segments.txt") obj = self.base_edifact_model.pydifact_obj(edifact_docu) @@ -56,7 +56,7 @@ def test_map2odoo_product(self): def test_map2odoo_product_pia(self): seg = ("1", "", ["", "EN"]) - pia = (['5', ['1276', 'SA', '', '9']]) + pia = ["5", ["1276", "SA", "", "9"]] product = self.base_edifact_model.map2odoo_product(seg, pia) self.assertEqual(product["code"], "1276") @@ -76,6 +76,11 @@ def test_map2odoo_unit_price(self): def test_map2odoo_date(self): # Test with date format YYYY-MM-DD HH:MM - date_str = (['137', '202303201433', '203']) + date_str = ["137", "202303201433", "203"] date = self.base_edifact_model.map2odoo_date(date_str) self.assertEqual(str(date), "2023-03-20") + + def test_map2odoo_description(self): + seg = ["F", "79", ["", "", "", "Description"]] + description = self.base_edifact_model.map2odoo_description(seg) + self.assertEqual(description, "Description")