Found more code examples from STM. Used claude to generate a summary (CLAUDE.md)
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
|
||||
'''
|
||||
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()
|
||||
Reference in New Issue
Block a user