''' Created on Jul 15, 2015 @author: Wes ''' from alert import send_alert_html from glob import glob import os, sys, logging, time, StringIO import pyodbc 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 = 'Invoices Transmitted to Momentum.' MESSAGE = None TEST_MODE = False LOG_LEVEL = logging.DEBUG ACC_NUMBERS = ['3398', '4318', '5230', '6600', '4063'] engine = create_engine("mssql+pyodbc://@jsisbprod", echo=False) Base = declarative_base(engine) metadata = Base.metadata logger = None ######################################################################## # Jomar Invoice (EDI 810) staging tables # ######################################################################## class CSPINV00(Base): __tablename__ = 'CSPINV00' __table_args__ = {'autoload':True} class CSPINV01(Base): __tablename__ = 'CSPINV01' __table_args__ = {'autoload':True} class TransactionTable(Base): __tablename__ = 'ftp_trans_numbers' __table_args__ = {'autoload':True} ######################################################################## # Jomar Item description tables. Used to look up Pattern name, color, # # style, etc. for a given item number # ######################################################################## class TXMYAT03(Base): __tablename__ = 'TXMYAT03' __table_args__ = {'autoload':True} ######################################################################## # Jomar Customer master file. Used to look up account names # ######################################################################## class CustomerMaster(Base): __tablename__ = 'TXUYKD00' __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() ''' Grabs any unprocessed Maharam invoices from the 810 EDI staging tables in Jomar. Formats data into text document to send to Maharam via FTP. Marks invoices as processed. ''' def createMomentumInvoiceDocument(): global MESSAGE initializeLogger('MOM_810_GENERATE') xmission = [] #List of strings, the individual lines of the transmission file that will be sent invoices_to_flag_processed = [] #First line of transmission is T or P. 'Test' or 'Production' if(TEST_MODE): logger.info('**RUNNING IN TEST MODE**') xmission.append('T~\n') else: logger.info('**RUNNING IN PRODUCTION MODE**') xmission.append('P~\n') logger.debug('Connecting to database...') session = loadSession() trans_no = -1 # t = session.query(mf_transaction_number).filter(mf_transaction_number.doc == '810') # if t.count() == 1: # for row in t: # trans_no = row.txnum # else: # logger.fatal('Failed to retrieve transaction number from database... Something ain\'t right. %s', t.count()) # sys.exit('Failed to retrieve transaction number from database... Something ain\'t right.') trans_no = getTransNumber() #Second line (Control Line) of transmission is essentially a boilerplate logger.info('Building Control Line') ctrlLine = 'TP~12~5702863800~12~9498338886~' ctrlLine += time.strftime('%Y%m%d') ctrlLine += '~' + str(trans_no) xmission.append(ctrlLine + "\n") logger.debug(ctrlLine) logger.info('Querying EDI table for unprocessed invoices...') logger.debug('SELECT * FROM CSPINV00 WHERE IVCUSTB IN (\'3398\', \'4318\', \'5230\', \'6600\') ORDER BY IVIDAT;') q = session.query(CSPINV00,CustomerMaster).order_by(CSPINV00.IVIDAT).filter(CSPINV00.IVCUSTB.in_(ACC_NUMBERS)).filter(CustomerMaster.KKDN == CSPINV00.IVCUSTB) for row in q: if(row[0].IVPROCSS == 'Y'): continue invoices_to_flag_processed.append(row[0].IVINV) businessGroup = row[0].IVBNR plant = row[0].IVPLT invoiceType = row[0].IVITYP logger.info('Building header line for invoice: ' + row[0].IVINV) #Translate Sunbury bill-to account number to Momentum vendor number momVendor = '' if(row[0].IVCUSTB == '3398'): momVendor = '6975' elif(row[0].IVCUSTB == '4318'): momVendor = '0036' elif(row[0].IVCUSTB == '5230'): momVendor = '7738' elif(row[0].IVCUSTB == '6600'): momVendor = '2082' elif(row[0].IVCUSTB == '4063'): momVendor = '2082' logger.debug('Sunbury Account: %s --> Momentum Vendor: %s', row[0].IVCUSTB, momVendor) momPO = row[0].IVCPO[-5:] hLine = 'H~' hLine += (row[0].IVINV + '~') hLine += (str(row[0].IVIDAT) + '~') hLine += (momVendor + '~') hLine += (momPO + '~') hLine += (row[0].IVCUSTB + '~') #hLine += (row[0].IVTERMS + '~') hLine += ('02~') #Changed to constant '02' per Kim Stolmeier @ Momentum hLine += (row[1].KNA2 + '~') hLine += (row[0].IVSADD1 + '~') hLine += (row[0].IVSCTY + '~') hLine += (row[0].IVSST + '~') hLine += (row[0].IVSZIP + '~') hLine += (row[1].KNA2 + '~') hLine += (row[0].IVBADD1 + '~') hLine += (row[0].IVBCTY + '~') hLine += (row[0].IVBST + '~') hLine += (row[0].IVBZIP + '~') hLine += (str(row[0].IVFRT) + '~') xmission.append(hLine + "\n") logger.debug(hLine) logger.info('Querying EDI table for invoice detail lines for invoice: ' + row[0].IVINV) logger.debug('SELECT * FROM CSPINV01 LEFT JOIN TXMYAT03 ON TXMYAT03.XANR = CSPINV01.I1ITEM WHERE CSPINV01.I1BNR = \'' + businessGroup + '\' AND CSPINV01.I1PLT = \'' + plant + '\' AND CSPINV01.I1ITYP = \'' + invoiceType + '\' AND CSPINV01.I1INV = \'' + row[0].IVINV + '\';') q2 = session.query(CSPINV01).filter(CSPINV01.I1BNR == businessGroup, CSPINV01.I1PLT == plant, CSPINV01.I1ITYP == invoiceType, CSPINV01.I1INV == row[0].IVINV).join(TXMYAT03, TXMYAT03.XANR == CSPINV01.I1ITEM).add_entity(TXMYAT03) for row2 in q2: if(row2[0].I1ITEM == 'INSURANCE'): ''' @todo: Does Momentum even pay for insurance? ''' logger.warn('Skipping insurance line!') continue logger.info('Building detail line for line ' + str(row2[0].I1ILINE)) dLine = 'D~' dLine += (row2[1].XFLX33 + '~') dLine += (row2[1].XFLX34 + '~') dLine += (row2[1].XFLX35 + '~') dLine += ("{0:.3f}".format(row2[0].I1QTY) + '~') dLine += ("{0:.2f}".format(row2[0].I1PRI) + '~') dLine += (momPO + '~') xmission.append(dLine + "\n") logger.debug(dLine) if len(xmission) > 2: xmission.append('S~' + str(len(xmission)) + '~') #Write transmission data out to file here logger.info('Creating D:\\FTP\\SEND_MG\\SBR_810_' + str(trans_no) + '.txt') f = open('SEND_MG\\SBR_810_' + str(trans_no) + '.txt', 'w') f.writelines(xmission) f.close() try: logger.info('Sending e-mail alert...') send_alert_html(RECIPIENTS, SUBJECT, attachments=glob('D:\\FTP\\SEND_MG\\SBR_810_*.txt'), message=MESSAGE.getvalue()) except: logger.error('Unable to send e-mail. Is the internet out?') #Update EDI tables and transaction number #mf_transaction_number if(len(invoices_to_flag_processed) > 0 and not TEST_MODE): #Set process flag on these invoices to 'Y' ex = update(CSPINV00.__table__).where(CSPINV00.IVINV.in_(invoices_to_flag_processed)).values(IVPROCSS=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 logger.debug('Closing database connection...') session.close() else: logger.warn('Nothing to send!') session.close() MESSAGE.truncate(0) if __name__ == '__main__': createMomentumInvoiceDocument()