diff --git a/base_edifact/models/edifact.py b/base_edifact/models/edifact.py index 5291bb3a7b..0c737199df 100644 --- a/base_edifact/models/edifact.py +++ b/base_edifact/models/edifact.py @@ -70,7 +70,11 @@ def pydifact_obj(self, docu): @api.model def _loads_edifact(self, order_file): - interchange = Interchange.from_str(order_file.decode()) + # 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")) return interchange @api.model @@ -82,7 +86,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 @@ -138,22 +146,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] @@ -172,16 +188,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 @@ -193,14 +215,30 @@ 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]) + 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 + + @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 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..87fa0f022f --- /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' 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..01a222b868 --- /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' diff --git a/base_edifact/tests/test_base_edifact.py b/base_edifact/tests/test_base_edifact.py index fafe8dd490..75bf12c309 100644 --- a/base_edifact/tests/test_base_edifact.py +++ b/base_edifact/tests/test_base_edifact.py @@ -24,6 +24,12 @@ def test_pydifact_obj(self): # [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 DP. Party to which goods should be delivered, if not identical with @@ -48,12 +54,33 @@ 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) 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) + + 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") + + def test_map2odoo_description(self): + seg = ["F", "79", ["", "", "", "Description"]] + description = self.base_edifact_model.map2odoo_description(seg) + self.assertEqual(description, "Description")