''' Create and send e-mail alert listing Designtex pieces that need to be invoiced. ''' import logging, os, pyodbc, sys from alert import send_alert_html from sqlalchemy import create_engine, or_ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy.sql.expression import update RECIPIENTS = ['admin@example.com', 'user3@example.com', 'user4@example.com', 'user1@example.com', 'user5@example.com'] #RECIPIENTS = ['admin@example.com'] SUBJECT = 'Designtex Reconciliation Report' TEST_MODE = False LOG_LEVEL = logging.DEBUG ROLL_NUM = 1 JOMAR_LINE = 3 ITEM_NUM = 4 AS400_LINE = 5 PATTERN = 6 COLOR = 7 PACKING_LIST = 8 engine = create_engine("mssql+pyodbc://jsisbprod", echo=False) #engine = create_engine("mssql+pyodbc://JTest", echo=False) Base = declarative_base(engine) metadata = Base.metadata class DTXCUTFL(Base): #EDI data from Designtex arrives here __tablename__ = 'DTXCUTFL' __table_args__ = {'autoload':True} class TXRYRT00(Base): #Roll/Tag Master (ticketfl) __tablename__ = 'TXRYRT00' __table_args__ = {'autoload':True} class TXUYUV00(Base): #Order header __tablename__ = 'TXUYUV00' __table_args__ = {'autoload':True} class TXUYUF01(Base): #Order detail __tablename__ = 'TXUYUF01' __table_args__ = {'autoload':True} class TXMYAT03(Base): #Sales item detail __tablename__ = 'TXMYAT03' __table_args__ = {'autoload':True} class AS400OrderLookupError(RuntimeError): ''' Raised if we fail to lookup the AS400 order number using the Jomar order number. ''' ''' This will eventually go away ''' class JomarOrderLookupError(RuntimeError): ''' Raised if we fail to lookup the Jomar order number using the AS400 order number ''' ''' This will eventually go away ''' class AS400LineLookupError(RuntimeError): ''' Raised if AS400 line numbers cannot be retrieved from the Jomar order ''' ''' This will eventually go away ''' class RollNotFoundError(RuntimeError): ''' Raised if no record in TXRYRT00 is found for a given ticket number ''' class ItemLookupError(RuntimeError): ''' Raised if no record is found in TXMYAT03 for a given item number ''' ''' This should never happen ''' def loadSession(): global engine Session = sessionmaker(bind=engine) session = Session() return session def initializeLogger(name='log'): global logger #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) def getAS400PackingList(order, line, ticket): global logger 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 q = "select SHP# from stmdata.XSHPFIL WHERE ORDERA = '" q = q + order + "' and LINE# = " q = q + line + " and TICK# = " q = q + ticket logger.debug(q) c1.execute(q) for row in c1: ret = row[0] c1.close() return str(ret) def dtxInvoiceAlert(): initializeLogger('dtx_cut_alert') session = loadSession() processed_records = [] logger.info('Querying DTXCUTFL for any unprocessed rolls...') tickets = session.query(DTXCUTFL).filter(or_(DTXCUTFL.PROCESSED != 'Y', DTXCUTFL.PROCESSED == None)) #Load in unprocessed roll/order numers for row in tickets: processed_records.append([row.__dict__['STM_ORDER#'], row.__dict__['STM_TICK#']]) if len(processed_records) == 0: logger.warn('Nothing to process. Exiting...') return #Fix/lookup Order Numbers logger.info('Querying order header table for order numbers...') for row in processed_records: #Not sure whether they are transmitting new or old style order numbers, allow for both. if(row[0].startswith('U')): row.append(row[0]) #We want U number to be 3rd column oHeader = session.query(TXUYUV00).filter(TXUYUV00.URBPO == row[0], TXUYUV00.UBLA == 'SO') if oHeader.count() != 1: raise JomarOrderLookupError('Failed to lookup Jomar order number. AS400 Order:', row[0]) for r in oHeader: logger.debug('AS400 Order: %s --> Jomar Order: %s', row[0], r.UBLN) row[0] = r.UBLN #Overwrite contents of first element with Jomar Order else: oHeader = session.query(TXUYUV00).filter(TXUYUV00.UBLN == row[0]) if oHeader.count() != 1: raise AS400OrderLookupError('Failed to lookup AS400 order number. Jomar Order:', row[0]) for r in oHeader: logger.debug('Jomar Order: %s --> AS400 Order: %s', row[0], r.URBPO) row.append(r.URBPO) logger.debug('*') logger.info('Querying roll/tag master for order line numbers and SKUs...') for row in processed_records: rtMaster = session.query(TXRYRT00).filter(TXRYRT00.RTBNR == '001', TXRYRT00.RTRNR == row[ROLL_NUM]) if rtMaster.count() != 1: raise RollNotFoundError('Roll does not exist in Jomar!', row[ROLL_NUM]) for r in rtMaster: logger.debug('Roll: %s --> Line: %s SKU: %s', row[ROLL_NUM], r.RTAFN, r.RTANR) row.append(str(r.RTAFN)) row.append(r.RTANR) logger.debug('*') logger.info('Querying order detail table for AS400 Line numbers...') for row in processed_records: oDetail = session.query(TXUYUF01).filter(TXUYUF01.VBLA == 'SO', TXUYUF01.VBLN == row[0], TXUYUF01.VBNR == '001', TXUYUF01.VLFN == row[JOMAR_LINE], TXUYUF01.VPLT == '00') if oDetail.count() != 1: raise AS400LineLookupError('Failed to retrieve AS400 line number.', row[0], row[JOMAR_LINE]) for r in oDetail: logger.debug('Jomar Order: %s Line: %s --> AS400 Line: %s', row[0], row[JOMAR_LINE], r.VPLIN) row.append(str(r.VPLIN)) logger.debug('*') logger.info('Querying sales item info table for STM pattern and color...') for row in processed_records: itemDetail = session.query(TXMYAT03).filter(TXMYAT03.XANR == row[ITEM_NUM], TXMYAT03.XBNR == '001', TXMYAT03.XPLT == '00') if itemDetail.count() != 1: raise ItemLookupError('Sales item lookup failed? I don\'t even know...', row[ITEM_NUM]) for r in itemDetail: logger.debug('Jomar Item: %s --> %s-%s', row[ITEM_NUM], r.XFLX33, r.XFLX34) row.append(r.XFLX33) row.append(r.XFLX34) logger.debug('*') logger.info('Querying XSHPFIL on the AS400 to get packing list numbers...') for row in processed_records: row.append(getAS400PackingList(row[2], row[AS400_LINE], row[ROLL_NUM])) logger.debug('*') #Build e-mail message msg = 'Designtex has cut into the following pieces:

' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' for row in processed_records: msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '' msg = msg + '
Roll #AS400 Packing ListJomar OrderPatternColor
' + row[ROLL_NUM] + '' + row[PACKING_LIST] + '' + row[0] + '' + row[PATTERN] + '' + row[COLOR] + '
' logger.info('Sending e-mails...') send_alert_html(RECIPIENTS, SUBJECT, message=msg) p = [row[ROLL_NUM].strip() for row in processed_records] if(len(p) > 0 and not TEST_MODE): logger.info('Marking records processed in database...') flag = 'UPDATE DTXCUTFL SET PROCESSED = \'Y\' where STM_TICK# in ' + str(p).replace('[', '(').replace(']', ')') session.execute(flag) session.commit() if __name__ == '__main__': #getAS400PackingList('U58475', '1', '52828') #Test packing number retrieval from AS400 dtxInvoiceAlert()