''' Created on Apr 27, 2017 Import Glen Raven ASN data into the DB @author: Wes ''' from alert import send_alert_html import datetime, csv, logging, sys, os, StringIO from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from operator import itemgetter from itertools import groupby RECIPIENTS = ['admin@example.com'] SUBJECT = 'Inbound Glen Raven ASN Processed' MESSAGE = None TEST_MODE = False GR_AP_ACCOUNT = '001306' JOMAR_PO_ORDER_TYPE = 'JB' VN = 0 PACK_SLIP = 1 YARN_ITEM = 2 COLOR_DESC = 3 PO_DO = 4 LOT_NUMBER = 5 CASE = 6 CASE_UNITS = 7 GRS_LBS = 8 NET_LBS = 9 TRNS_DATE = 9 SHIP_DATE = 10 SHIP_VIA = 11 logger = None LOG_LEVEL = logging.DEBUG engine = create_engine("mssql+pyodbc://jsisbprod", echo=False) #engine = create_engine("mssql+pyodbc://@JTest", echo=False) Base = declarative_base(engine) metadata = Base.metadata #---------------------------------------------------------------------# class PSPASN00(Base): __tablename__ = 'PSPASN00' #Inboud ASN table __table_args__ = {'autoload':True} class TXEYAT00(Base): __tablename__ = 'TXEYAT00' #Vendor Item Cross reference table __table_args__ = {'autoload':True} class TXUYUF01(Base): __tablename__ = 'TXUYUF01' #Order Detail Table __table_args__ = {'autoload':True} #---------------------------------------------------------------------# class ItemTranslationError(RuntimeError): ''' Raising this if we fail to lookup STM item using GR item ''' class POError(RuntimeError): ''' Raising this if we fail to lookup required info from relevant PO ''' 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 - %(name)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) ''' Query the vendor item xref table to translate GR --> STM Likely reasons this failed: * _gr_item_ not entered as a xref for any of our items * _gr_item_ entered for more than 1 of our items Returns the matching STM item number if found. ''' def GRtoSTM(gr_item): global session global logger q = session.query(TXEYAT00).filter(TXEYAT00.UALNR == GR_AP_ACCOUNT, TXEYAT00.UALAN == gr_item) if(q.count() == 1): for row in q: return row.UAANR else: logger.debug('GRtoSTM() rows returned: %s', q.count()) return None ''' Query the order detail table by _po_ and _item_. Likely reasons this failed: * _po_ doesn't actually exist * _item_ not ordered on _po_ * _item_ appears on more than 1 line on _po_ Returns the line number of _item_ on _po_ if found. ''' def getLineNum(po, item): global session global logger q = session.query(TXUYUF01).filter(TXUYUF01.VBLA == JOMAR_PO_ORDER_TYPE, TXUYUF01.VBLN == po, TXUYUF01.VANR == item) if(q.count() == 1): for row in q: return row.VLFN else: return None def processInboundGRASN(inFile): global MESSAGE global session os.chdir('D:\FTP') initializeLogger('gr_856_inbound_interface') logger.info('****************************************') fileName = inFile.split('\\')[-1] rawLines = [] session = loadSession() logger.info('Loading: %s', inFile) try: f = open(inFile, 'r') except IOError: logger.fatal('Unable to load file. Did you specify the full path? Exiting...') return filename = inFile.split('\\')[-1] try: reader = csv.reader(f) for row in reader: rawLines.append(row) finally: f.close() rawLines.pop(0) #Remove first line as it is just column headers if(len(rawLines) == 0): logger.warning('They sent us an empty file...') logger.info('Moving %s to D:\\FTP\\OLD\\GR\\', inFile) os.rename(inFile, 'OLD\\GR\\' + fileName) session.close() return rawLines.sort(key=itemgetter(PO_DO)) #Ensure raw data is sorted by PO number purchaseOrders = groupby(rawLines, itemgetter(PO_DO)) #Create group by iterator based on PO number #For each unique PO appearing in this transmission for poNumber, lines in purchaseOrders: logger.info('****') logger.info('Processing records for purchase order: %s', poNumber) #For each record of this transmission on poNumber for line in lines: if line[VN] not in ['19']: logger.warn('Encountered record with invalid vendor number!') logger.warn(line) continue #Translate GR item to STM item stm_item = GRtoSTM(line[YARN_ITEM]) if stm_item: #Query order detail by PO and STM item to get the correct PO line number line_num = getLineNum(poNumber, stm_item) if line_num: logger.info('Processing Case: %s (%s)', line[CASE], stm_item) asn = PSPASN00() asn.BUSINESS_UNIT = '001' asn.PLANT = '00' asn.VENDOR = GR_AP_ACCOUNT asn.PO_NUMBER = poNumber asn.PO_ITEM = stm_item asn.PO_LINE = line_num asn.PACKING_SLIP = line[PACK_SLIP] asn.CASE_NUMBER = line[CASE] asn.QTY_GROSS = line[GRS_LBS] asn.QTY_NET = line[NET_LBS] asn.VENDOR_LOT = line[LOT_NUMBER] session.add(asn) else: logger.error('Failed to find a line on PO %s for: %s', poNumber, stm_item) try: send_alert_html(RECIPIENTS, 'Error Processing GR ASN', attachments=[inFile], message=MESSAGE.getvalue()) except: logger.error('Unable to send e-mail. Is the internet out?') raise POError('Failed to retrieve PO info from database', poNumber, stm_item) else: logger.error('Failed to lookup STM item number for GR Item: %s', line[YARN_ITEM]) try: send_alert_html(RECIPIENTS, 'Error Processing GR ASN', attachments=[inFile], message=MESSAGE.getvalue()) except: logger.error('Unable to send e-mail. Is the internet out?') raise ItemTranslationError('Failed to lookup STM item number for GR Item', line[YARN_ITEM]) if not TEST_MODE: logger.info('Writing records to database...') session.commit() else: logger.warn('**TEST MODE ENABLED ROLLING BACK TRANSACTIONS**') session.rollback() session.close() if not TEST_MODE: try: logger.info('Sending e-mail alert...') send_alert_html(RECIPIENTS, SUBJECT, attachments=[inFile], message=MESSAGE.getvalue()) except: logger.error('Unable to send e-mail. Is the internet out?') logger.info('Moving ' + inFile + ' to D:\\FTP\\OLD\\GR\\') os.rename(inFile, 'OLD\\GR\\' + fileName) MESSAGE.truncate(0) if __name__ == '__main__': if(len(sys.argv) < 2): print 'Must supply fully qualified path to csv file ex:' print 'python gr_856_in.py D:\\FTP\\INCOMING\\GR_ASN_0100334639.csv' else: processInboundGRASN(sys.argv[1])