Files
Python_Examples/stm_etl/Finished_Goods_Interface.py
T

601 lines
27 KiB
Python

'''
Created on Jun 24, 2015
If no arguments supplied script looks for CSV's in the folder specified by PATH.
Can be run for single file by supplying file path as arg. This is the preferred way of doing things
if the filewatcher powershell script is enabled to avoid collisions.
Creates various inventory transactions needed to keep Jomar inventory up to date with what is entering
shipping dept from the AS400
Transaction data is written to TXRYBW10 and processed immediatley.
See 'Inventory Control' -> 'Visibility' -> 'Inventory Errors' for transactions that failed to process
TEST_MODE, when True:
* Prevents files being moved to the PROCESSED folder.
* Will _NOT_ e-mail Aaron
* Does not commit any transactions to the database
@author: Wes
'''
import ConfigParser
import csv, os, smtplib
from email import Encoders
from email.MIMEBase import MIMEBase
from email.MIMEImage import MIMEImage
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from glob import glob
import logging
from operator import itemgetter
import sys, sqlite3, re
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
TEST_MODE = False
LOG_LEVEL = logging.DEBUG
PATH = 'D:\\INTERFACE\\'
PULL_ORDERS = ['U17435', 'U32614', 'U37151', 'U38029', 'U39702', 'U38664', 'U30473', 'U61478', 'U64631', 'U74634', 'U89496']
IN_FILE_FORMAT = "[\S\s]+as400_jsi_(?P<txnum>[\d][\d][\d][\d][\d]).csv"
ROLL_ERROR_RECIPIENTS = ['user1@example.com', 'user2@example.com']
TX_ERROR_RECIPIENTS = ['admin@example.com', 'user1@example.com']
ORDER = 0
LINE = 1
TICKET = 2
FINISHED_YARDS = 3
INVOICED_YARDS = 4
BIN = 5
WAREHOUSE = 6
WEIGHT = 7
SPAT = 8
SCOL = 9
#This script must be run as SUNBURYTEXTILE\Administrator
#engine = create_engine("mssql+pyodbc://jsisbprod", echo=False)
engine = create_engine("mssql+pyodbc://{}:{}@jsisbprod".format(
os.environ['JOMAR_DB_UID'], os.environ['JOMAR_DB_PWD']), echo=False)
Base = declarative_base(engine)
metadata = Base.metadata
class TXRYBW10(Base):
__tablename__ = 'TXRYBW10' #Finished good interface table
__table_args__ = {'autoload':True}
class TXUYUV00(Base):
__tablename__ = 'TXUYUV00' #Order Header
__table_args__ = {'autoload':True}
class TXUYUF01(Base):
__tablename__ = 'TXUYUF01' #Order Detail
__table_args__ = {'autoload':True}
class TXRYRT00(Base):
__tablename__ = 'TXRYRT00' #Roll/tag master
__table_args__ = {'autoload':True}
class ItemInfo(Base):
__tablename__ = 'TXMYAT03'
__table_args__ = {'autoload':True}
'''
MODIFIED - 01/04/2018 - SB-20
This table is refreshed from the AS400 every hour.
Need this to determine NAFTA qualification
'''
class HTSCDSFL(Base):
__tablename__ = 'as400_HTSCDSFL'
__table_args__ = {'autoload':True}
def load_email_credentials():
vault = sqlite3.connect('D:\\FTP\\ftp.db')
cursor = vault.execute("SELECT * from accounts where code = 'DNR'")
for row in cursor:
return [row[1], row[2]]
'''
I found the bulk of this function already written on someone's blog for sending mail
via outlook 365. I made a few modifications for our purposes.
'''
def send_mail(send_from, send_to, subject, text, files=None,
data_attachments=None, server="smtp.example.com", port=25,
tls=False, html=False, images=None,
username=None, password=None,
config_file=None, config=None):
if files is None:
files = []
if images is None:
images = []
if data_attachments is None:
data_attachments = []
if config_file is not None:
config = ConfigParser.ConfigParser()
config.read(config_file)
if config is not None:
server = config.get('smtp', 'server')
port = config.get('smtp', 'port')
tls = config.get('smtp', 'tls').lower() in ('true', 'yes', 'y')
username = config.get('smtp', 'username')
password = config.get('smtp', 'password')
msg = MIMEMultipart('related')
msg['From'] = send_from
msg['To'] = send_to if isinstance(send_to, basestring) else COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text, 'html' if html else 'plain') )
for f in files:
part = MIMEBase('application', "octet-stream")
part.set_payload( open(f,"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
msg.attach(part)
for f in data_attachments:
part = MIMEBase('application', "octet-stream")
part.set_payload( f['data'] )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % f['filename'])
msg.attach(part)
for (n, i) in enumerate(images):
fp = open(i, 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
msgImage.add_header('Content-ID', '<image{0}>'.format(str(n+1)))
msg.attach(msgImage)
smtp = smtplib.SMTP(server, int(port))
if tls:
smtp.starttls()
#if username is not None:
# smtp.login(username, password)
smtp.sendmail(send_from, send_to, msg.as_string())
smtp.close()
'''
Opens connection to database returning a session object.
'''
def loadSession():
global engine
Session = sessionmaker(bind=engine)
session = Session()
return session
def initializeLogger(name='log'):
global logger
logger = logging.getLogger(__name__)
logger.setLevel(LOG_LEVEL)
handler = logging.FileHandler('D:\\INTERFACE\\' + 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)
'''
MODIFIED - 01/04/2018 - SB-20
This function is the brains of the operation regarding the NAFTA changes.
Simple lookup
'''
def NAFTA(session, pattern, color):
logger.debug('Attempting lookup in HTSCDSFL to determine NAFTA qualification')
logger.debug('SELECT * FROM as400_HTSCDSFL WHERE as400_HTSCDSFL.HTSPAT = \'%s\' AND as400_HTSCDSFL.HTSCLR = \'%s\';', pattern, color)
p = session.query(HTSCDSFL).filter(HTSCDSFL.HTSPAT == pattern, HTSCDSFL.HTSCLR == color)
for r in p:
logger.debug('Record found in HTSCDSFL. Value of HTSNQC is \'%s\'', r.HTSNQC)
if r.HTSNQC == 'X':
return 'Y'
else:
return 'N'
logger.debug('No record found in HTSCDSFL!')
return 'N'
if __name__ == '__main__':
os.chdir('D:\INTERFACE')
initializeLogger('Finished_Goods_Interface')
logger.info('********************************************************************************')
if(len(sys.argv) < 2):
logger.info('No args supplied, processing all files in folder...')
logger.debug('Obtaining list of CSV files in ' + PATH + '\\')
files = glob(PATH + '\*.csv')
# Verify file name and check for any previous transactions hung up in folder
elif re.match(IN_FILE_FORMAT, sys.argv[1], re.IGNORECASE):
logger.info('Processing single file: ' + sys.argv[1])
files = [sys.argv[1]]
in_file_tx = int(re.match(IN_FILE_FORMAT, sys.argv[1], re.IGNORECASE).group('txnum')) #parse transaction num from file name and cast to int
ls = glob(PATH + '\*.csv')
for f in ls:
test = int(re.match(IN_FILE_FORMAT, f, re.IGNORECASE).group('txnum'))
if test < (in_file_tx - 1):
logger.error('Trying to process: %s but %s is still in folder.', sys.argv[1], f)
try:
if not TEST_MODE:
logger.info('Alerting relevant people to this situation...')
msg = 'For whatever reason <b>' + f + '</b> has not finished processing before <b>' + sys.argv[1] + '</b> was submitted.'
dnr_account = load_email_credentials()
send_mail('donotreply@example.com', TX_ERROR_RECIPIENTS, 'TRANSACTION HUNG UP IN FGI!', msg,
username=dnr_account[0], password=dnr_account[1], html=True)
except:
logger.error('Could not send e-mail. Check internet connection.')
logger.error('THERE ARE OLDER TRANSACTIONS THAT HAVE NOT PROCESSED!! Exting...')
sys.exit()
# Invalid file specified. print error msg and exit.
else:
logger.error('File name: %s does not conform to as400_jsi_#####.csv', sys.argv[1])
sys.exit()
session = loadSession()
'''
Process each CSV file
as400_jsi_00001.csv
'''
for f in files:
if not('as400_jsi' in f):
logger.debug('Skipping ' + f + ' wrong file name...')
continue
rows = []
eRows = []
jLot = f.split('_')[2].split('.')[0]
ERRORS = False #This can be set True in a number of places, This conditions whether or not an e-mail is sent
logger.info('****************************************')
logger.info('Loading file ' + f)
#Read file fully into rows
with open(f, 'rb') as g:
reader = csv.reader(g)
for row in reader:
rows.append(row)
#rows = sorted(rows, key=itemgetter(0, 1, 2)) #sort rows read by Order,Line,Piece since AS400 seems to supply data in random order
logger.info(str(len(rows)) + ' records read.')
tickets_processed = []
'''
Process each record in file
'''
for row in reversed(rows):
#print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN]
if row[TICKET] in tickets_processed:
logger.warn('Encountered ticket: ' + row[TICKET] + ' more than once in single transmission!')
row.append('DUPLICATE TICKET ROW. DOUBLE CHECK YARDAGE/WEIGHT IN JOMAR!!!')
eRows.append(row)
ERRORS = True
continue
jOrder = 'null'
jLine = -1
jItem = -1
#if(row[ORDER] in PULL_ORDERS):
#logger.warn(row[ORDER] + ' is a pull order. Ignoring for now! Ticket: ' + row[TICKET])
#row.append('**IGNORING PULL ORDERS FOR NOW**')
#eRows.append(row)
#ERRORS = True
#continue
#Neil says no pieces in HLD bin should make it this far; just in case...
if(row[BIN] == 'HLD' or (row[WAREHOUSE] == 'SH' and row[BIN] == ' ')):
#print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN]
logger.warn(row[WAREHOUSE] + '-' + row[BIN] + ' is not a valid bin! Ticket: ' + row[TICKET])
row.append('INVALID BIN')
eRows.append(row)
ERRORS = True
continue
if(float(row[FINISHED_YARDS]) <= 0):
#print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN]
logger.warn('Finished Yards must be > 0. Ticket: ' + row[TICKET])
row.append('Finished Yards must be > 0')
eRows.append(row)
ERRORS = True
continue
if(float(row[INVOICED_YARDS]) <= 0):
#print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN]
logger.warn('Invoiced Yards must be > 0. Ticket: ' + row[TICKET])
row.append('Invoiced Yards must be > 0')
eRows.append(row)
ERRORS = True
continue
#Get Jomar order number
logger.info('Querying order header table for Jomar order number...')
logger.debug('SELECT * FROM TXUYUV00 WHERE TXUYUV00.UBNR = \'001\' AND TXUYUV00.UPLT = \'00\' AND TXUYUV00.UBLA = \'SO\' AND (TXUYUV00.URBPO = \'' + row[ORDER] + '\' OR TXUYUV00.URBPO = \'' + row[ORDER][1:] + '\');')
q = session.query(TXUYUV00).order_by(TXUYUV00.UBLN).filter(TXUYUV00.UBNR == '001', TXUYUV00.UPLT == '00', TXUYUV00.UBLA == 'SO', ((TXUYUV00.URBPO == row[ORDER]) | (TXUYUV00.URBPO == row[ORDER][1:])))
if(q.count() != 1 and row[ORDER]):
#print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN]
logger.warn('Cannot locate order (' + row[ORDER] + ') in Jomar system. Ticket: ' + row[TICKET])
row.append('Cannot locate order in Jomar system.')
eRows.append(row)
ERRORS = True
continue
for r in q:
jOrder = r.UBLN
logger.info('Jomar order: ' + jOrder + ' Ticket: ' + row[TICKET])
#print 'Jomar Order:', jOrder, 'Matches Sunbury Order:', row[ORDER]
#Get Jomar Line Number
logger.info('Querying order detail table for Jomar line number...')
logger.debug('SELECT VLFN, VANR, XFLX33, XFLX34 FROM TXUYUF01 LEFT JOIN TXMYAT03 ON TXUYUF01.VBNR = TXMYAT03.XBNR AND TXUYUF01.VPLT = TXMYAT03.XPLT AND TXUYUF01.VANR = TXMYAT03.XANR WHERE TXUYUF01.VBNR = \'001\' AND TXUYUF01.VPLT = \'00\' AND TXUYUF01.VBLA = \'SO\' AND TXUYUF01.VBLN = \'' + jOrder + '\' AND TXUYUF01.VPLIN = ' + str(int(row[LINE])) + ';')
q = session.query(TXUYUF01,ItemInfo).order_by(TXUYUF01.VBLN).filter(TXUYUF01.VBNR == ItemInfo.XBNR, TXUYUF01.VPLT == ItemInfo.XPLT, TXUYUF01.VANR == ItemInfo.XANR).filter(TXUYUF01.VBNR == '001', TXUYUF01.VPLT == '00', TXUYUF01.VBLA == 'SO', TXUYUF01.VBLN == jOrder, TXUYUF01.VPLIN == int(row[LINE]))
if(q.count() != 1 and row[ORDER]):
#print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN]
logger.warn('AS400 Line: ' + row[LINE] + ' does not exist on Jomar order: ' + jOrder)
row.append('Jomar Order: ' + jOrder + ' - There are no lines on this order where AS400 Line Number = ' + row[LINE] + ' Ticket: ' + row[TICKET])
eRows.append(row)
ERRORS = True
continue
skip = False
#Verify pattern and color match b/t AS400 and Jomar
for r in q:
if(r[1].XFLX33.strip().upper() != row[SPAT].strip() and row[ORDER]):
skip = True
ERRORS = True
logger.warn('Pattern Mismatch! Jomar: ' + r[1].XFLX33 + ' AS400: ' + row[SPAT].strip() + ' Ticket: ' + row[TICKET])
row.append('Pattern Mismatch! Jomar: ' + r[1].XFLX33 + ' AS400: ' + row[SPAT].strip())
eRows.append(row)
if(int(r[1].XFLX34) != int(row[SCOL].strip()) and row[ORDER]):
skip = True
ERRORS = True
logger.warn('Color Mismatch! Jomar: ' + r[1].XFLX34 + ' AS400: ' + row[SCOL].strip() + ' Ticket: ' + row[TICKET])
row.append('Color Mismatch! Jomar: ' + r[1].XFLX34 + ' AS400: ' + row[SCOL].strip())
eRows.append(row)
if(skip == False):
jLine = r[0].VLFN
jItem = r[0].VANR
logger.info('Jomar line: ' + str(jLine) + ' Ticket: ' + row[TICKET])
logger.info('Jomar item: ' + str(jItem) + ' Ticket: ' + row[TICKET])
#print ' Jomar Line:', jLine, 'Matches Sunbury Line:', row[LINE]
if(skip):
continue
'''
Not doing this anymore for obvious reasons... Wish I would've gotten the information
concerning the other possible inventory transactions before 04/20/16......
'''
#If a roll with this ticket number exists in the inventory, get rid of it
#session.query(TXRYRT00).filter(TXRYRT00.RTRNR == row[TICKET]).delete()
tickets_processed.append(row[TICKET]) #Made it past all of the error checks, add this ticket to processed list.
logger.info('Querying Roll/Tag master to see if this ticket exists in Jomar already. Ticket: ' + row[TICKET])
logger.debug('SELECT COUNT(*) FROM TXRYRT00 WHERE TXRYRT00.RTRNR = ' + row[TICKET] + ';')
q = session.query(TXRYRT00).filter(TXRYRT00.RTRNR == row[TICKET]).count()
if(q == 0): #Do an 08 transaction...
logger.info('No previous record of roll. Adding production receipt (08) transaction. Ticket: ' + row[TICKET])
i = TXRYBW10()
i.RWAPL = '00'
i.RWSPL = '00'
i.RWSLA = 'SO'
i.RWPLT = '00'
i.RWBNR = '001'
i.RWALA = 'SO'
i.RWBEW = '08'
'''
MODIFIED - 01/04/2018 - SB-20
Set RTGRML to 'US' for all rolls
'''
i.RWGRML = 'US'
if(row[ORDER] not in PULL_ORDERS):
i.RWALN = jOrder
i.RWSLN = jOrder
i.RWAFN = jLine
i.RWSFN = jLine
else:
i.RWALN = None
i.RWSLN = None
i.RWAFN = None
i.RWSFN = None
i.RWANR = jItem
i.RWCHA = jLot
i.RWRNR = row[TICKET]
i.RWPGLG = row[FINISHED_YARDS]
i.RWPLG = row[INVOICED_YARDS]
if(row[BIN] == 'BAG'):
logger.debug('BAG bin = BG Warehouse. Ticket: ' + row[TICKET])
i.RWLOC = 'BG'
i.RWLPL = ''
elif(row[BIN].strip() == '08'):
logger.debug('08 bin = B8 Warehouse. Ticket: ' + row[TICKET])
i.RWLOC = 'B8'
i.RWLPL = ''
else:
if(row[WAREHOUSE] == 'SH'):
i.RWLPL = row[BIN]
else:
i.RWLPL = ''
i.RWLOC = row[WAREHOUSE]
i.RWPWG = int(float(row[WEIGHT]))
#i.RWTWG = int(float(row[WEIGHT])) #This was screwing up proformas. This is a tare weight field and so it should be zero for our purposes
i.RWTWG = 0
i.RWQAL = '01'
'''
MODIFIED - 01/04/2018 - SB-20
Determine if roll is NAFTA qualified.
'''
i.RWORCD = NAFTA(session, row[SPAT].strip(), str(int(row[SCOL].strip())))
session.add(i)
else:
logger.debug('Roll already exists. Querying roll/tag master again to determine what to do...')
logger.debug('SELECT * FROM TXRYRT00 WHERE TXRYRT00.RTRNR = ' + row[TICKET] + ';')
p = session.query(TXRYRT00).filter(TXRYRT00.RTRNR == row[TICKET])
for r in p:
'''
MODIFIED 01/23/17
Ran across a scenario where rolls already shipped in Jomar came through the interface again.
Now checking for shipped yardage here so the script can kick out an error instead of compounding issue.
'''
if float(r.RTSLG) > 0:
tickets_processed.remove(row[TICKET]) #Remove this ticket from processed list
logger.warn('Shipped yards greater than zero! Ticket: %s', row[TICKET])
row.append('Shipped yards > 0')
eRows.append(row)
ERRORS = True
continue
current_lot = r.RTCHA
current_inv_yards = r.RTPLG
current_fin_yards = r.RTPGLG
current_inv_lbs = r.RTPWG
current_bin = r.RTLPL
current_warehouse = r.RTLOC
dest_bin = row[BIN]
dest_warehouse = row[WAREHOUSE]
#Adjust bins and warehouses for bagging machine nonsense....
if(row[BIN].strip() == '08'):
dest_bin = ''
dest_warehouse = 'B8'
if(row[BIN].strip() == 'BAG'):
dest_bin = ''
dest_warehouse = 'BG'
if((float(current_inv_yards) != float(row[INVOICED_YARDS])) or (int(current_inv_lbs) != int(float(row[WEIGHT]))) or (float(current_fin_yards) != float(row[FINISHED_YARDS]))):
logger.info('Roll exists but length/weight differ. Adjusting via a Production Receipt transaction (08) Ticket: ' + row[TICKET])
if (float(current_inv_yards) != float(row[INVOICED_YARDS])):
logger.debug('Current Invoiced Yards = %s Transmitted = %s Adjustment = %s', current_inv_yards, row[INVOICED_YARDS], str(float(row[INVOICED_YARDS]) - float(current_inv_yards)))
if (int(current_inv_lbs) != int(float(row[WEIGHT]))):
logger.debug('Current Weight = %s Transmitted = %s Adjustment = %s', current_inv_lbs, row[WEIGHT], (int(float(row[WEIGHT])) - int(current_inv_lbs)))
if (float(current_fin_yards) != float(row[FINISHED_YARDS])):
logger.debug('Current Finished Yards = %s Transmitted = %s Adjustment = %s', current_fin_yards, row[FINISHED_YARDS], str(float(row[FINISHED_YARDS]) - float(current_fin_yards)))
i = TXRYBW10()
i.RWAPL = '00'
i.RWSPL = '00'
i.RWSLA = 'SO'
i.RWPLT = '00'
i.RWBNR = '001'
i.RWALA = 'SO'
i.RWBEW = '08'
if(row[ORDER] not in PULL_ORDERS):
i.RWALN = jOrder
i.RWSLN = jOrder
i.RWAFN = jLine
i.RWSFN = jLine
else:
i.RWALN = None
i.RWSLN = None
i.RWAFN = None
i.RWSFN = None
i.RWANR = jItem
i.RWCHA = current_lot
i.RWRNR = row[TICKET]
i.RWPGLG = str(float(row[FINISHED_YARDS]) - float(current_fin_yards))
i.RWPLG = str(float(row[INVOICED_YARDS]) - float(current_inv_yards))
i.RWLOC = current_warehouse.strip()
i.RWLPL = current_bin.strip()
i.RWPWG = (int(float(row[WEIGHT])) - int(current_inv_lbs))
#i.RWTWG = (int(float(row[WEIGHT])) - int(current_inv_lbs))
i.RWTWG = 0
i.RWQAL = '01'
session.add(i)
if((current_bin.strip() != dest_bin.strip()) or (current_warehouse.strip() != dest_warehouse.strip())):
logger.info('Roll exists but bins are different. Adding a movement (06) transaction...' + current_warehouse + '-' + current_bin + ' --> ' + dest_warehouse + '-' + dest_bin + ' Ticket: ' + row[TICKET])
i = TXRYBW10()
i.RWAPL = '00'
i.RWSPL = '00'
i.RWSLA = 'SO'
i.RWPLT = '00'
i.RWBNR = '001'
i.RWALA = 'SO'
i.RWBEW = '06'
if(row[ORDER] not in PULL_ORDERS):
i.RWALN = jOrder
i.RWSLN = jOrder
i.RWAFN = jLine
i.RWSFN = jLine
else:
i.RWALN = None
i.RWSLN = None
i.RWAFN = None
i.RWSFN = None
i.RWANR = jItem
i.RWCHA = current_lot
i.RWRNR = row[TICKET]
i.RWPGLG = row[FINISHED_YARDS]
i.RWPLG = row[INVOICED_YARDS]
i.RWLOC = dest_warehouse.strip() #Xfer to warehouse
i.RWLPL = dest_bin.strip() #Xfer to bin
i.RWLO2 = current_warehouse.strip() #Xfer from warehouse
i.RWLP2 = current_bin.strip() #Xfer from bin
i.RWPL2 = '00' #Xfer from plant
i.RWPWG = int(float(row[WEIGHT]))
#i.RWTWG = int(float(row[WEIGHT]))
i.RWTWG = 0
i.RWQAL = '01'
session.add(i)
if( (float(current_inv_yards) == float(row[INVOICED_YARDS])) and (int(current_inv_lbs) == int(float(row[WEIGHT]))) and
(current_bin.strip() == dest_bin.strip()) and (current_warehouse.strip() == dest_warehouse.strip()) and
(float(current_fin_yards) == float(row[FINISHED_YARDS]))):
logger.info('Nothing appears to be different. Ignoring. Ticket: ' + row[TICKET])
'''
End record processing
'''
#If errors were encountered during processing, e-mail the suspect rows to Aaron in a csv
if ERRORS:
#Dump eRows to errors.csv
logger.debug("Dumping error rows to: " + PATH + "ERRORS\\ERRORS_" + jLot + ".csv")
with open(PATH + "ERRORS\\ERRORS_" + jLot + ".csv", "wb") as g:
writer = csv.writer(g)
writer.writerows(eRows)
try:
if not TEST_MODE:
logger.info('E-mailing Aaron list of error rows...')
dnr_account = load_email_credentials()
send_mail('donotreply@example.com', ROLL_ERROR_RECIPIENTS, 'AS400 -> Jomar Inventory Errors', 'See Attachment.',
username=dnr_account[0], password=dnr_account[1], files=[PATH + "ERRORS\\ERRORS_" + jLot + ".csv"])
except:
logger.error('Could not send e-mail. Check internet connection.')
if not TEST_MODE:
logger.debug("Moving " + f + " to " + "PROCESSED\\" + f.split("\\")[2])
os.rename(f, PATH + "PROCESSED\\" + f.split("\\")[2])
'''
End file processing
'''
#When all files have been processed commit new records to interface table.
if(TEST_MODE):
logger.warn('**TEST MODE ENABLED. ROLLING BACK DATABASE TRANSACTIONS**')
session.rollback()
else:
logger.debug('Committing database transactions...')
session.commit()
logger.debug('Closing database connection.')
session.close()