Found more code examples from STM. Used claude to generate a summary (CLAUDE.md)
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
'''
|
||||
Created on Dec 8, 2015
|
||||
|
||||
Working with David Keenan (dkeenan@maharam.com) at Maharam to send Invoices to them as XML documents.
|
||||
|
||||
@author: Wes
|
||||
'''
|
||||
from alert import send_alert_html
|
||||
from glob import glob
|
||||
|
||||
import datetime, sys, re, logging, StringIO
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.sql.expression import update
|
||||
from sqlalchemy.sql.schema import Table, Column
|
||||
from sqlalchemy.sql.sqltypes import Integer
|
||||
|
||||
import xml.etree.cElementTree as ET
|
||||
|
||||
RECIPIENTS = ['admin@example.com']
|
||||
SUBJECT = 'Invoices Transmitted to Maharam.'
|
||||
MESSAGE = None
|
||||
|
||||
TEST_MODE = False
|
||||
LOG_LEVEL = logging.DEBUG
|
||||
ACC_NUMBERS = ['3439', '6590']
|
||||
DESC_PATTERN = "(?P<pName>[a-zA-Z0-9_ \-\']+?)\s(?P<color>\d+)"
|
||||
|
||||
engine = create_engine("mssql+pyodbc://jsisbprod", echo=False) #Production
|
||||
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 TXMYAT03(Base):
|
||||
__tablename__ = 'TXMYAT03'
|
||||
__table_args__ = {'autoload':True}
|
||||
class XREF(Base): #Customer Xref Table
|
||||
__tablename__ = 'TXUYAT01'
|
||||
__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)
|
||||
|
||||
def createMaharamInvoiceDocuments():
|
||||
global MESSAGE
|
||||
initializeLogger('MAH_810_GENERATE')
|
||||
if TEST_MODE:
|
||||
logger.warn('**RUNNING IN TEST MODE**')
|
||||
logger.warn('THIS WILL NOT FLAG INVOICES AS PROCESSED IN CSPINV00!')
|
||||
else:
|
||||
logger.info('**RUNNING IN PRODUCTION MODE**')
|
||||
session = loadSession()
|
||||
invoices = []
|
||||
invoices_to_flag_processed = []
|
||||
#root = ET.Element("MaharamInvoices")
|
||||
|
||||
logger.info('Querying EDI table for unprocessed invoices...')
|
||||
logger.debug('SELECT * FROM CSPINV00 WHERE IVCUSTB IN (\'3439\', \'6590\') ORDER BY IVIDAT;')
|
||||
q = session.query(CSPINV00).order_by(CSPINV00.IVIDAT).filter(CSPINV00.IVCUSTB.in_(ACC_NUMBERS))
|
||||
for row in q:
|
||||
if(row.IVPROCSS == 'Y'):
|
||||
continue
|
||||
businessGroup = row.IVBNR
|
||||
plant = row.IVPLT
|
||||
invoiceType = row.IVITYP
|
||||
invoiceNum = row.IVINV
|
||||
invoices_to_flag_processed.append(invoiceNum)
|
||||
date = datetime.datetime.strptime(str(row.IVIDAT), "%Y%m%d")
|
||||
|
||||
customerBillToAccount = row.IVCUSTB
|
||||
customerPO = row.IVCPO
|
||||
orderType = row.IVSTYP
|
||||
orderNum = row.IVSO
|
||||
terms = row.IVTERMS
|
||||
bAddress1 = row.IVBADD1
|
||||
bAddress2 = row.IVBADD2
|
||||
bAddress3 = row.IVBADD3
|
||||
bCity = row.IVBCTY
|
||||
bState = row.IVBST
|
||||
bZip = row.IVBZIP
|
||||
bCountry = row.IVBCTY
|
||||
|
||||
sAddress1 = row.IVSADD1
|
||||
sAddress2 = row.IVSADD2
|
||||
sAddress3 = row.IVSADD3
|
||||
sCity = row.IVSCTY
|
||||
sState = row.IVSST
|
||||
sZip = row.IVSZIP
|
||||
sCountry = row.IVSCTY
|
||||
|
||||
lineNum = []
|
||||
itemNum = []
|
||||
itemDesc = []
|
||||
cItem = []
|
||||
cItemDesc = []
|
||||
qtyInvoiced = []
|
||||
price = []
|
||||
invAmtDom = []
|
||||
invAmtFor = []
|
||||
invAmtGrossDom = []
|
||||
invAmtGrossFor = []
|
||||
cPatterns = []
|
||||
cColors = []
|
||||
cSKUs = []
|
||||
|
||||
logger.info('Querying EDI table for invoice detail lines for invoice: ' + invoiceNum)
|
||||
logger.debug('SELECT * FROM CSPINV01 WHERE CSPINV01.I1BNR = \'' + businessGroup + '\' AND CSPINV01.I1PLT = \'' + plant + '\' AND CSPINV01.I1ITYP = \'' + invoiceType + '\' AND CSPINV01.I1INV = \'' + invoiceNum + '\';')
|
||||
q2 = session.query(CSPINV01).filter(CSPINV01.I1BNR == businessGroup, CSPINV01.I1PLT == plant, CSPINV01.I1ITYP == invoiceType, CSPINV01.I1INV == invoiceNum)
|
||||
for row2 in q2:
|
||||
lineNum.append(row2.I1ILINE)
|
||||
#itemNum.append(row2.I1ITEM) Sending Maharam our PATTERN-COLOR instead of new item number for the time being
|
||||
itemDesc.append(row2.I1ITEMT)
|
||||
cItem.append(row2.I1ITEMC)
|
||||
cItemDesc.append(row2.I1ITEMCT)
|
||||
qtyInvoiced.append(row2.I1QTY)
|
||||
price.append(row2.I1PRI)
|
||||
invAmtDom.append(row2.I1AMTND)
|
||||
invAmtFor.append(row2.I1AMTNF)
|
||||
invAmtGrossDom.append(row2.I1AMTGD)
|
||||
invAmtGrossFor.append(row2.I1AMTGF)
|
||||
|
||||
if(row2.I1ITEM == 'INSURANCE' or row2.I1ITEM == 'FREIGHT'):
|
||||
cPatterns.append('')
|
||||
cColors.append('')
|
||||
cSKUs.append('')
|
||||
itemNum.append(row2.I1ITEM)
|
||||
continue
|
||||
|
||||
blah = re.match(DESC_PATTERN, row2.I1ITEMT, flags=0)
|
||||
ourPattern = blah.group('pName')
|
||||
ourColor = blah.group('color')
|
||||
|
||||
itemNum.append(ourPattern.strip() + '-' + ourColor.strip()) #Sending Maharam our PATTERN-COLOR instead of new item number for the time being
|
||||
|
||||
'''
|
||||
This queries a view I created to join TXUYAT01 (customer xref) and TXMYAT03 (item info) together.
|
||||
The join basically adds the Sunbury pattern name, and color broken out into their own columns to the customer xref table.
|
||||
|
||||
As far as I know, a customer can only have one 'reference' for a given pattern/color combination so this should be fine.
|
||||
It also handles instances where a finish/backing was changed during order entry, which results in a new item number that may not
|
||||
make it into the customer xref table, which broke the old method that relied on jomar item numbers.
|
||||
'''
|
||||
logger.info('Querying customer item cross reference table for Sunbury item: ' + ourPattern + '-' + ourColor)
|
||||
logger.debug('SELECT * FROM item_xref_wes WHERE item_xref_wes.bGroup = \'' + businessGroup + '\' AND item_xref_wes.plant = \'' + plant + '\' AND item_xref_wes.customer = \'' + customerBillToAccount + '\' AND item_xref_wes.sPatt = \'' + ourPattern + '\' AND item_xref_wes.sCol = \'' + ourColor + '\';')
|
||||
xref_lookup = session.execute('SELECT * FROM item_xref_wes WHERE item_xref_wes.bGroup = :bgroup AND item_xref_wes.plant = :plt AND item_xref_wes.customer = :cust AND item_xref_wes.sPatt = :pat AND item_xref_wes.sCol = :col', {'bgroup' : businessGroup, 'plt' : plant, 'cust' : customerBillToAccount, 'pat' : ourPattern, 'col' : ourColor})
|
||||
for r in xref_lookup:
|
||||
cPatterns.append(r.cPatt)
|
||||
cColors.append(r.cCol)
|
||||
cSKUs.append(r.cPatt.split('/')[0] + '-' + r.cCol.split('/')[0])
|
||||
|
||||
if(len(lineNum) != len(cSKUs)):
|
||||
logger.warning('Failed to lookup customer cross reference! Skipping ' + invoiceNum)
|
||||
logger.warning(itemNum)
|
||||
logger.warning(itemDesc)
|
||||
logger.warning(cSKUs)
|
||||
invoices_to_flag_processed.remove(invoiceNum)
|
||||
send_alert_html(RECIPIENTS, ('Maharam Invoice: ' + invoiceNum + ' not sent!'), message='There is a cross reference issue. Check the log.')
|
||||
continue
|
||||
|
||||
root = ET.Element("MaharamInvoices")
|
||||
invoice = ET.SubElement(root, "Invoice")
|
||||
|
||||
invoiceInfo = ET.SubElement(invoice, "InvoiceInfo")
|
||||
ET.SubElement(invoiceInfo, "InvoiceDate").text = date.strftime('%m-%d-%Y')
|
||||
ET.SubElement(invoiceInfo, "InvoiceNumber").text = invoiceNum
|
||||
billToAddress = ET.SubElement(invoiceInfo, "BillToAddress")
|
||||
ET.SubElement(billToAddress, "Address").text = bAddress1
|
||||
if(len(bAddress2) > 0):
|
||||
ET.SubElement(billToAddress, "Address").text = bAddress2
|
||||
if(len(bAddress3) > 0):
|
||||
ET.SubElement(billToAddress, "Address").text = bAddress3
|
||||
ET.SubElement(invoiceInfo, "BillToCity").text = bCity
|
||||
ET.SubElement(invoiceInfo, "BillToState").text = bState
|
||||
ET.SubElement(invoiceInfo, "BillToPostalCode").text = bZip
|
||||
ET.SubElement(invoiceInfo, "BillToCountry").text = bCountry
|
||||
ET.SubElement(invoiceInfo, "AttnName").text = ''
|
||||
ET.SubElement(invoiceInfo, "AttnEmail").text = ''
|
||||
ET.SubElement(invoiceInfo, "TermsCode").text = ''
|
||||
ET.SubElement(invoiceInfo, "TermsDesc").text = terms
|
||||
|
||||
purchaseOrder = ET.SubElement(invoice, "PurchaseOrder")
|
||||
ET.SubElement(purchaseOrder, "PONum").text = customerPO
|
||||
ET.SubElement(purchaseOrder, "ShipToName").text = ''
|
||||
shipToAddress = ET.SubElement(purchaseOrder, "ShipToAddress")
|
||||
ET.SubElement(shipToAddress, "Address").text = sAddress1
|
||||
if(len(bAddress2) > 0):
|
||||
ET.SubElement(shipToAddress, "Address").text = sAddress2
|
||||
if(len(bAddress3) > 0):
|
||||
ET.SubElement(shipToAddress, "Address").text = sAddress3
|
||||
ET.SubElement(purchaseOrder, "ShipToCity").text = sCity
|
||||
ET.SubElement(purchaseOrder, "ShipToState").text = sState
|
||||
ET.SubElement(purchaseOrder, "ShipToPostalCode").text = sZip
|
||||
ET.SubElement(purchaseOrder, "ShipToCountry").text = sCountry
|
||||
|
||||
for x in range(0, len(lineNum)):
|
||||
line = ET.SubElement(purchaseOrder, "Line")
|
||||
ET.SubElement(line, "LineNumber").text = '' #str(lineNum[x])
|
||||
ET.SubElement(line, "SKU").text = cSKUs[x]
|
||||
ET.SubElement(line, "ResourceSKU").text = str(itemNum[x])
|
||||
ET.SubElement(line, "QuantityBilled").text = str(qtyInvoiced[x])
|
||||
ET.SubElement(line, "UM").text = 'YD'
|
||||
ET.SubElement(line, "Price").text = str(price[x])
|
||||
ET.SubElement(line, "Curr").text = 'USD'
|
||||
|
||||
tree = ET.ElementTree(root)
|
||||
tree.write("SEND_MF\\STM_INVOICE_" + str(invoiceNum) + ".xml")
|
||||
|
||||
if(len(invoices_to_flag_processed) > 0) and not TEST_MODE:
|
||||
ex = update(CSPINV00.__table__).where(CSPINV00.IVINV.in_(invoices_to_flag_processed)).values(IVPROCSS=u"Y")
|
||||
session.execute(ex)
|
||||
session.commit()
|
||||
|
||||
if(len(invoices_to_flag_processed) == 0):
|
||||
logger.warn('Nothing to send!')
|
||||
else:
|
||||
try:
|
||||
logger.info('Sending e-mail alert...')
|
||||
send_alert_html(RECIPIENTS, SUBJECT, attachments=glob('D:\\FTP\\SEND_MF\\*.xml'), message=MESSAGE.getvalue())
|
||||
except:
|
||||
logger.error('Unable to send e-mail. Is the internet out?')
|
||||
session.close()
|
||||
|
||||
MESSAGE.truncate(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
createMaharamInvoiceDocuments()
|
||||
Reference in New Issue
Block a user