From dd7fc8f11992dc1d6c7ae7d99a83e6dde43cdac4 Mon Sep 17 00:00:00 2001 From: bleeson Date: Thu, 12 Dec 2024 09:41:34 -0800 Subject: [PATCH] 850 addition and other minor changes --- edi_846.py | 3 +- edi_850.py | 310 +++++++++++++++++++++++++++++++++++++++++++++++++ edi_867.py | 2 + edi_943.py | 40 ++++--- edi_947.py | 2 + import_867s.py | 9 +- 6 files changed, 338 insertions(+), 28 deletions(-) create mode 100644 edi_850.py diff --git a/edi_846.py b/edi_846.py index b5d7955..60b75ff 100644 --- a/edi_846.py +++ b/edi_846.py @@ -47,6 +47,7 @@ def main(): #read in the information from Shandex and store it for edi_filename in X12_DIRECTORY.iterdir(): if SHANDEX_846_FILENAME_RE.match(edi_filename.name): + pprint.pprint(edi_filename.name) shandex_inventory=process_file(edi_filename) # file moved to 997 processing folder to be sent later shutil.move(edi_filename, EDI_997_DIRECTORY / edi_filename.name) @@ -240,7 +241,7 @@ def process_file(edi_filename: pathlib.Path): for fields in tokens_from_edi_file(edi_filename): if fields[0] == "BIA": advice_date = fields[4] - if fields[0] == 'LIN': + if fields[0] == 'LIN' and len(fields) > 5: if product != '': #check loop entry if product not in shandex_inventory: #if we haven't seen the product yet add it # pprint.pprint('product was not found') diff --git a/edi_850.py b/edi_850.py new file mode 100644 index 0000000..f6fcf7e --- /dev/null +++ b/edi_850.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python3 +""" +Consume a 850 file from Shandex, and translate into a Sage X3 +readable file - import template ZPOH. +For Shandex we also need to reply with a 997 +""" +# pylint: disable=too-many-instance-attributes +import dataclasses +import datetime +import decimal +import functools +import pathlib +import re +import shutil +import typing +import pprint +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +import records # type: ignore + +import yamamotoyama # type: ignore +import yamamotoyama.x3_imports # type: ignore + +THIS_DIRECTORY = pathlib.Path(__file__).parent +X12_DIRECTORY = THIS_DIRECTORY / "incoming" +IMPORTS_DIRECTORY = THIS_DIRECTORY / "x3_imports" +EDI_997_DIRECTORY = THIS_DIRECTORY / "997_processing" + +SHANDEX_850_FILENAME_RE = re.compile( + r"\A 850_STASH-YAMAMOTOYAMA_ \S+ [.]edi \Z", re.X | re.M | re.S +) + +def main(): + """ + Do it! + """ + for edi_filename in X12_DIRECTORY.iterdir(): + if SHANDEX_850_FILENAME_RE.match(edi_filename.name): + process_file(edi_filename) + # file moved to 997 processing folder to be sent later + shutil.move(edi_filename, EDI_997_DIRECTORY / edi_filename.name) + combine_zpohs() + + +def new_850_alert(ordref, orddat): + msg = MIMEMultipart() + msg['Subject'] = 'New PO from Shandex' + msg['Precedence'] = 'bulk' + msg['From'] = 'x3report@stashtea.com' + msg['To'] = 'icortes@yamamotoyama.com' + msg['CC'] = 'bleeson@stashtea.com' + emailtext = f'Ref: {ordref}\nDate: {orddat}' + msg.attach(MIMEText(emailtext, 'plain')) + with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp: + smtp.login(user='x3reportmk2@yamamotoyama.com', password=r'n typing.Iterator[typing.List[str]]: + """ + Read tokens from EDI file + """ + with edi_filename.open(encoding="utf-8", newline="") as edi_file: + for record in edi_file.read().split("~"): + fields = record.split("*") + if fields[0] in { + "ISA", + "ST", + "N2", + "N3", + "N4", + }: + continue + yield fields + + +def process_file(edi_filename: pathlib.Path): + """ + Convert a specific EDI file into an import file. + """ + with yamamotoyama.get_connection() as database: + purchase_order = PO() + pohnum = '' + for fields in tokens_from_edi_file(edi_filename): + #BEG*00*SA*PO0040865**20241209 + if fields[0] == "BEG": + _, _, _, ordref, _, orddat = fields[:6] + purchase_order.header.ordref = ordref + purchase_order.header.orddat = datetime.datetime.strptime( + orddat, "%Y%m%d" + ).date() # 20230922 + purchase_order.header.pohfcy='WON' + purchase_order.header.bpsnum='PMW' + purchase_order.header.cur='USD' + #DTM*010*20250109 + if fields[0] == "DTM" and fields[1] == '010': + expected_ship_date = fields[2] + #PO1*1*2688*CA*15.02**VP*C08225*IN*10077652082255 + if fields[0] == "PO1": + _, lineno, qty_str, uom, pricestr, _, _, itmref, gtin = fields[:9] + detail = PODetail( + itmref=itmref, + itmdes=get_itmdes(itmref, database), + prhfcy='WON', + uom=uom, + qtyuom=int(qty_str), + extrcpdat=expected_ship_date, + gropri=pricestr, + ) + purchase_order.append(detail) + time_stamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + new_850_alert(ordref, purchase_order.header.orddat)#TODO new alert type, after it hits X3? + with yamamotoyama.x3_imports.open_import_file( + IMPORTS_DIRECTORY / f"ZPOH_{purchase_order.header.ordref}_{time_stamp}.dat" + ) as import_file: + purchase_order.output(import_file) + + +def get_itmdes(itmref, database): + result = database.query( + """ + select + ITMDES1_0 + from PROD.ITMMASTER + where + ITMREF_0 = :itmref + """, + itmref=itmref, + ).first()['ITMDES1_0'] + return result + +@dataclasses.dataclass +class PODetail: + """ + Information that goes on a PO detail line, taken from ZPOH template. + """ + + itmref: str = "" + itmdes: str = "" + prhfcy: str = "" + uom: str = "" + qtyuom: int = 0 + extrcpdat: datetime.date = datetime.date(1753, 1, 1) + gropri: decimal.Decimal = 0 + discrgval1: decimal.Decimal = 0 + discrgval2: decimal.Decimal = 0 + discrgval3: decimal.Decimal = 0 + pjt: str = "" + vat: str = '' + star91: str = "" + star92: str = "" + + def convert_to_strings(self) -> typing.List[str]: + """ + Convert to strings for X3 import writing. + """ + def fix_uom(uom): + x3_uom = '' + if uom == 'CA': + x3_uom = 'CS' + else: + x3_uom = uom + return x3_uom + + return yamamotoyama.x3_imports.convert_to_strings( + [ + "L", + self.itmref, + self.itmdes, + self.prhfcy, + fix_uom(self.uom), + self.qtyuom, + self.extrcpdat, + self.gropri, + self.discrgval1, + self.discrgval2, + self.discrgval3, + self.pjt, + self.vat, + self.star91, + self.star92, + ] + ) + +@dataclasses.dataclass +class POHeader: + """ + Information that goes on a po header, taken from ZPOH template. + """ + + pohfcy: str = "" + pohnum: str = "" + orddat: datetime.date = datetime.date(1753, 1, 1) + bpsnum: str = "" + ordref: str = "" + bpsnum: str = "" + cur: str = "USD" + star71 = "" + star72 = "" + star81 = "" + star82 = "" + + + def convert_to_strings(self) -> typing.List[str]: + """ + Convert to X3 import line + """ + return yamamotoyama.x3_imports.convert_to_strings( + [ + "E", + self.pohfcy, + '',#self.pohnum, + self.orddat.strftime("%Y%m%d"), + self.bpsnum, + self.ordref, + self.cur, + self.star71, + self.star72, + self.star81, + self.star82, + ] + ) + + +class PODetailList: + """ + List of PO details + """ + + _details: typing.List[PODetail] + _item_set: typing.Set[str] + + def __init__(self): + self._details = [] + self._item_set = set() + + def append( + self, + po_detail: PODetail + ): + """ + Append + """ + self._details.append(po_detail) + + def __iter__(self): + return iter(self._details) + + +class PO: + """ + Warehouse po, both header & details + """ + + header: POHeader + details: PODetailList + + def __init__(self): + self.header = POHeader() + self.details = PODetailList() + + def append( + self, + po_detail: PODetail, + ): + """ + Add detail information. + """ + self.details.append(po_detail) + + def output(self, import_file: typing.TextIO): + """ + Output entire po to import_file. + """ + output = functools.partial( + yamamotoyama.x3_imports.output_with_file, import_file + ) + output(self.header.convert_to_strings()) + for detail in self.details: + output(detail.convert_to_strings()) + + +if __name__ == "__main__": + main() diff --git a/edi_867.py b/edi_867.py index d215063..92c0b28 100644 --- a/edi_867.py +++ b/edi_867.py @@ -81,6 +81,8 @@ X3_CUSTOMER_MAPPING = { 'PURI1000_PURIBC' : 'PURI0003', 'PURI1000_PURIAB' : 'PURI0004', 'AMAZ1200_YEG2' : 'AMAZ0179', + 'HORI1000_0000' : 'HORI0001', + 'NATI1100_NATION' : 'NATI0004', } def main(): diff --git a/edi_943.py b/edi_943.py index 1e81155..34c905f 100644 --- a/edi_943.py +++ b/edi_943.py @@ -99,6 +99,26 @@ def write_943(database: records.Connection, shipment: str): now = datetime.datetime.now() site = str(get_shipment_destination(database, shipment)) datestamp_string = now.strftime("%Y-%m-%d-%H-%M-%S") + #2024-09-25 never sent multiple 943s, mark them as sent before processing + with database.transaction() as _: + database.query( + """ + update [PROD].[SDELIVERY] + set [XX4S_943RDY_0] = 1 + where [SOHNUM_0] = :shipment + """, + shipment=shipment, + ) + order = get_order_for_shipment(database, shipment) + database.query( + """ + update [PROD].[SORDER] + set [XX4S_UDF2_0] = :sent_message + where [SOHNUM_0] = :order + """, + order=order, + sent_message=f"943 Sent {datetime.date.today().isoformat()}", + ) with (X12_SHANDEX / f"{site}-{shipment}-{datestamp_string}-943.edi").open( "w", encoding="utf-8", newline="\n" ) as x12_file: @@ -122,25 +142,7 @@ def write_943(database: records.Connection, shipment: str): package_count += detail.qtystu_0 if header: output(header.footer(package_count, detail_count)) - with database.transaction() as _: - database.query( - """ - update [PROD].[SDELIVERY] - set [XX4S_943RDY_0] = 1 - where [SOHNUM_0] = :shipment - """, - shipment=shipment, - ) - order = get_order_for_shipment(database, shipment) - database.query( - """ - update [PROD].[SORDER] - set [XX4S_UDF2_0] = :sent_message - where [SOHNUM_0] = :order - """, - order=order, - sent_message=f"943 Sent {datetime.date.today().isoformat()}", - ) + def get_shipment_destination(database: records.Connection, shipment: str) -> str: """ diff --git a/edi_947.py b/edi_947.py index b748f95..b9cff5a 100644 --- a/edi_947.py +++ b/edi_947.py @@ -74,6 +74,7 @@ def main(): """ for edi_filename in X12_DIRECTORY.iterdir(): if SHANDEX_947_FILENAME_RE.match(edi_filename.name): + pprint.pprint(edi_filename.name) process_file(edi_filename) # file moved to 997 processing folder to be sent later shutil.move(edi_filename, EDI_997_DIRECTORY / edi_filename.name) @@ -140,6 +141,7 @@ def gtin_lookup(gtin): and [ITF].[STOFCY_0] = 'WON' where replace([ITM].[ZCASEUPC_0],' ','') = :zcaseupc + or replace([ITM].[EANCOD_0],' ','') = :zcaseupc """, zcaseupc=gtin, ).first()["ITMREF_0"] diff --git a/import_867s.py b/import_867s.py index d2b50cb..0aeefcc 100644 --- a/import_867s.py +++ b/import_867s.py @@ -24,13 +24,6 @@ SELECT_STATEMENT = """ and SDH.bpcord <> '' """ -SELECT_STATEMENT_TESTING = """ - select - PO - from staging.dbo.shandex_shipments SDH - where - PO = '4542_O0216777' -""" HEADER_STATEMENT = """ select @@ -178,7 +171,7 @@ SUBDETAIL_NAMES = ['S','sta','pcu','qtypcu','loc','lot','sernum'] def get_shipments(database): with database.transaction(): - result = database.query(SELECT_STATEMENT_TESTING).all()#TODO REMOVE TESTING + result = database.query(SELECT_STATEMENT).all() return result def get_shipment_headers(database, po):