''' Created on May 12, 2015 NOTE: A _packing list_ triggers a new header record in this transmission not a shipment. There can and often will be more header records in a transmission file than there were shipments. @author: Wes ''' from alert import send_alert_html from glob import glob import datetime, logging, os, sys, pyodbc, StringIO from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy.sql.expression import update RECIPIENTS = ['admin@example.com'] SUBJECT = 'Shipment Notices Transmitted to Momentum.' MESSAGE = None TEST_MODE = False LOG_LEVEL = logging.DEBUG ACC_NUMBERS = ['3398', '4318', '5230'] engine = create_engine("mssql+pyodbc://jsisbprod", echo=False) Base = declarative_base(engine) metadata = Base.metadata class CSPASN00(Base): __tablename__ = 'CSPASN00' __table_args__ = {'autoload':True} class CSPASN01(Base): __tablename__ = 'CSPASN01' __table_args__ = {'autoload':True} class CSPASN02(Base): __tablename__ = 'CSPASN02' __table_args__ = {'autoload':True} class CSPASN03(Base): __tablename__ = 'CSPASN03' __table_args__ = {'autoload':True} class CSPASN04(Base): __tablename__ = 'CSPASN04' __table_args__ = {'autoload':True} class XREF(Base): #Customer Xref Table __tablename__ = 'TXUYAT01' __table_args__ = {'autoload':True} class TXRYRT00(Base): #Roll/Tag Master (ticketfl) __tablename__ = 'TXRYRT00' __table_args__ = {'autoload':True} class TXMYAT03(Base): __tablename__ = 'TXMYAT03' __table_args__ = {'autoload':True} class TXRYPS00(Base): __tablename__ = 'TXRYPS00' __table_args__ = {'autoload':True} class TransactionTable(Base): __tablename__ = 'ftp_trans_numbers' __table_args__ = {'autoload':True} def loadSession(): global engine Session = sessionmaker(bind=engine) session = Session() return session def initializeLogger(name='log'): global logger global MESSAGE #logger = logging.getLogger(__name__) logger = logging.getLogger(name) if len(logger.handlers) > 0: return logger logger.setLevel(LOG_LEVEL) handler = logging.FileHandler('D:\\FTP\\logs\\' + name + '.log') handler.setLevel(LOG_LEVEL) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) #Also send logs > DEBUG to stdout ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) #Capture log messages in a string for e-mail alert MESSAGE = StringIO.StringIO() sh = logging.StreamHandler(MESSAGE) sh.setLevel(logging.DEBUG) sh.setFormatter(formatter) logger.addHandler(sh) ''' Gotta get transaction number from AS400 until ALL transmissions are handled on the new system... whoopee ''' def getTransNumber(): connection = pyodbc.connect( driver='{iSeries Access ODBC Driver}', system='SUNBURY', uid=os.environ['AS400_UID'], pwd=os.environ['AS400_PWD']) c1 = connection.cursor() ret = -1 c1.execute('select * from stmdata.MGTX#FL') for row in c1: ret = int(row[1]) + 1 c1.close() return ret ''' Update the Momentum transaction number in the AS400 database ''' def updateTransNumber(txnum): connection = pyodbc.connect( driver='{iSeries Access ODBC Driver}', system='SUNBURY', uid=os.environ['AS400_UID'], pwd=os.environ['AS400_PWD'], autocommit=True) c1 = connection.cursor() c1.execute('UPDATE STMDATA.MGTX#FL SET MGSEQ# = ' + str(txnum)) c1.close() def createMomentumASN(): global MESSAGE initializeLogger('MOM_856_GENERATE') session = loadSession() shipments_to_flag_processed = [] lines = [] trans_no = getTransNumber() logger.info('Querying CSPASN0[0,1] for unprocessed shipments...') shipmentQ = session.query(CSPASN00,CSPASN01).order_by(CSPASN00.MSTBIL).filter(CSPASN00.CUSTNO.in_(ACC_NUMBERS)).filter(CSPASN00.ABNR == CSPASN01.ABNR, CSPASN00.APLT == CSPASN01.APLT, CSPASN00.CUSTNO == CSPASN01.CUSTNO, CSPASN00.MSTBIL == CSPASN01.MSTBIL) for shipment in shipmentQ: if(shipment[0].PROCSS == 'Y'): continue #lines = [] bGroup = shipment[0].ABNR plant = shipment[0].APLT cust = shipment[0].CUSTNO shipmentNumber = shipment[0].MSTBIL shipmentDate = datetime.datetime.strptime(str(shipment[1].SHPDTE), "%Y%m%d") logger.info('Processing shipment number: ' + shipmentNumber) logger.info(' Shipped on: ' + shipmentDate.strftime('%m/%d/%Y')) shipments_to_flag_processed.append(shipmentNumber) logger.info('Querying CSPASN02 for packing lists on this shipment...') packingListQ = session.query(CSPASN02).order_by(CSPASN02.BOLNUM).filter(CSPASN02.ABNR == bGroup, CSPASN02.APLT == plant, CSPASN02.CUSTNO == cust, CSPASN02.MSTBIL == shipmentNumber) for packingList in packingListQ: plNumber = packingList.BOLNUM poNumber = packingList.PONUM stmOrder = packingList.ORDNUM #Build Header Record of transmission hRec = 'H~%s~%s~%s~%s~%s~%s~%s\n' % (plNumber, '', '', shipmentDate.strftime('%m/%d/%Y'), '', poNumber, '') lines.append(hRec) detailQ = session.query(CSPASN03,CSPASN04,TXRYPS00,XREF,TXRYRT00).order_by(CSPASN04.LINE).filter(CSPASN03.ABNR == CSPASN04.ABNR, CSPASN03.APLT == CSPASN04.APLT, CSPASN03.CUSTNO == CSPASN04.CUSTNO, CSPASN03.MSTBIL == CSPASN04.MSTBIL, CSPASN03.BOLNUM == CSPASN04.BOLNUM, CSPASN03.CTN == CSPASN04.CTN).filter(CSPASN03.ABNR == bGroup, CSPASN03.APLT == plant, CSPASN03.CUSTNO == cust, CSPASN03.MSTBIL == shipmentNumber, CSPASN03.BOLNUM == plNumber).filter(CSPASN04.ABNR == TXRYPS00.PSBNR, CSPASN04.APLT == TXRYPS00.PSPLT, CSPASN04.CUSTNO == TXRYPS00.PSKDN, CSPASN04.SB_PATTERNAME == TXRYPS00.PSPRT, CSPASN04.SB_COLORNUM == TXRYPS00.PSANR).filter(CSPASN04.ABNR == XREF.UACBNR, CSPASN04.APLT == XREF.UACPLT, CSPASN04.CUSTNO == XREF.UACCNR, CSPASN04.ITEM == XREF.UACANR).filter(CSPASN04.ABNR == TXRYRT00.RTBNR, CSPASN04.APLT == TXRYRT00.RTPLT, CSPASN04.ITEM == TXRYRT00.RTANR, CSPASN04.ROLNUM == TXRYRT00.RTRNR) detailCount = detailQ.count() for detail in detailQ: poLine = detail[1].LINE momStyle = detail[3].UACPAT momColor = detail[3].UACCOL momProductNum = detail[2].PSFD01[:8] #Momentum product numbers are always 8 digits, it seems like we've left padded them with too many zeros in our DB ydsShipped = "{0:.3f}".format(detail[1].SQTY) weight = "{0:.1f}".format(detail[4].RTSWG) pieceNum = detail[1].ROLNUM ourPattern = detail[1].SB_PATTERNAME.strip() ourColor = detail[1].SB_COLORNUM.strip() dRec = 'D~%s~%s~%s~%s~%s~%s~%s~%s~%s~%s\n' % (momProductNum, momStyle, momColor, '', ydsShipped, 'YD', pieceNum, '', weight, 'LB') # @todo: There is a bug in the nightmare of a query above that was causing duplicate rows # to be created in the tranmission for seemingly random rolls. In every case I've found # the lines that make it into the transmission are identical. So the below 'fix' is good # enough to get us off the ground for the time being. # # 07/11/2016 - I believe this is fixed now by adding "CSPASN03.CTN == CSPASN04.CTN" to the first # .filter() clause for the 03 <-> 04 join. I discovered this while working on # Maharam's 856 document. Need to verify... if not lines[-1].split('~')[7] == pieceNum: lines.append(dRec) else: logger.warn('Query still messed up...') tRec = 'T~' + str(detailCount) + "\n" lines.append(tRec) sRec = 'S~' + str(len(lines)) #+ "\n" lines.append(sRec) if(len(shipments_to_flag_processed) > 0): #Write transmission data out to file here logger.info('Creating D:\\FTP\\SEND_MG\\SBR_856_' + str(trans_no) + '.txt') f = open('D:\\FTP\\SEND_MG\\SBR_856_' + str(trans_no) + '.txt', 'w') f.writelines(lines) f.close() try: logger.info('Sending e-mail alert...') send_alert_html(RECIPIENTS, SUBJECT, attachments=glob('D:\\FTP\\SEND_MG\\SBR_856_*.txt'), message=MESSAGE.getvalue()) except: logger.error('Unable to send e-mail. Is the internet out?') if not TEST_MODE: #Mark shipments as processed ex = update(CSPASN00.__table__).where(CSPASN00.MSTBIL.in_(shipments_to_flag_processed)).values(PROCSS=u"Y") session.execute(ex) ex = None #Might as well keep the table in the new database up to date ex2 = update(TransactionTable.__table__).where(TransactionTable.client.in_(['MOMTEX'])).values(xaction=(trans_no)) session.execute(ex2) ex2 = None logger.debug('Comitting changes to database...') session.commit() updateTransNumber(trans_no) #Update transaction number in AS400 if(len(shipments_to_flag_processed) == 0): logger.info('Nothing to send!') logger.debug('Closing database connection...') session.close() MESSAGE.truncate(0) ''' 00 - List of shipments 01 - Contains shipment date for shipments in 01 02 - List of packing slips on each shipment ''' if __name__ == '__main__': createMomentumASN()