233 lines
8.6 KiB
Python
233 lines
8.6 KiB
Python
|
|
'''
|
|
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:<br><br>'
|
|
msg = msg + '<table border="1">'
|
|
msg = msg + '<tr>'
|
|
msg = msg + '<th>Roll #</th>'
|
|
msg = msg + '<th>AS400 Packing List</th>'
|
|
msg = msg + '<th>Jomar Order</th>'
|
|
msg = msg + '<th>Pattern</th>'
|
|
msg = msg + '<th>Color</th>'
|
|
msg = msg + '</tr>'
|
|
|
|
for row in processed_records:
|
|
msg = msg + '<tr>'
|
|
msg = msg + '<td>' + row[ROLL_NUM] + '</td>'
|
|
msg = msg + '<td>' + row[PACKING_LIST] + '</td>'
|
|
msg = msg + '<td>' + row[0] + '</td>'
|
|
msg = msg + '<td>' + row[PATTERN] + '</td>'
|
|
msg = msg + '<td>' + row[COLOR] + '</td>'
|
|
msg = msg + '</tr>'
|
|
|
|
msg = msg + '</table>'
|
|
|
|
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()
|