dasaproject/SO_to_Inv/readSO.py
2023-12-29 04:41:31 +07:00

639 lines
35 KiB
Python

import xml.etree.ElementTree as ET
import win32com.client
import xmltodict
import pprint
import datetime
import pandas as pd
from datetime import date
import timeit
import os
import pythoncom
# from icecream import ic
# ic.configureOutput(includeContext=True, )
class SalesOrderQuery:
def __init__(self, **kwargs) -> None:
# print(f'kwargs:{kwargs}')
# print(args)
# self.SalesOrderList=[]
self.PriceLevelName = None
self.SPPriceLevelName = None
self.cwd = kwargs['cwd'] if 'cwd' in kwargs else os.getcwd()
self.item_inventory_path = "ItemInventory"
self.price_level_filename = "PriceLevel.xlsx"
self._df_price_level = pd.read_excel(os.path.join(self.cwd, self.item_inventory_path, self.price_level_filename), usecols=['FullName', 'PriceLevelName', 'CustomPrice'],)
print(self._df_price_level)
print(type(self._df_price_level.loc[(self._df_price_level['FullName']=="ECO:0:ECO-002") & (self._df_price_level['PriceLevelName']=="T 202202")].values.tolist()[0][2]))
print(self._df_price_level.loc[(self._df_price_level['FullName']=="TEDG:S122:EDG-009-1/22") & (self._df_price_level['PriceLevelName']=="T 202202")].values.tolist()[0][2])
self.FullName = kwargs['FullName'] if 'FullName' in kwargs else None
self.CustomerPriceLevelName_filename = "CustomerList.xlsx"
self._df_customer = pd.read_excel(os.path.join(self.cwd, self.item_inventory_path, self.CustomerPriceLevelName_filename), usecols=['FullName', 'PriceLevelName', 'SPName'],)
self._df_customer = self._df_customer.fillna('')
print(self._df_customer)
self.Customer = None
if self.FullName:
if self.FullName in self._df_customer['FullName'].values:
self.Customer = self._df_customer.loc[(self._df_customer["FullName"]==self.FullName)].values.tolist()[0]
print(f'Customer:{self.Customer}')
self.DN = kwargs['DN'] if 'DN' in kwargs else {}
self.Reuse = kwargs['Reuse'] if 'Reuse' in kwargs else None
self.SalesOrderList= kwargs['SalesOrderList'] if 'SalesOrderList' in kwargs else []
self.InvoiceList = None
self.SalesOrderType = kwargs['SalesOrderType'] if 'SalesOrderType' in kwargs else 'SalesByCustomerSummary'
self.IncludeLineItems = kwargs['IncludeLineItems'] if 'IncludeLineItems' in kwargs else 'true'
self.IncludeRetElement = kwargs['IncludeRetElement'] if 'IncludeRetElement' in kwargs else []
self.TxnDateRangeFilter = kwargs['TxnDateRangeFilter'] if 'TxnDateRangeFilter' in kwargs else None
self.DateMacro = None
if 'DateMacro' in kwargs:
if kwargs['DateMacro'] in ['All', 'Today', 'ThisWeek', 'ThisWeekToDate', 'ThisMonth', 'ThisMonthToDate', 'ThisQuarter', 'ThisQuarterToDate', 'ThisYear', 'ThisYearToDate', 'Yesterday', 'LastWeek', 'LastWeekToDate', 'LastMonth', 'LastMonthToDate', 'LastQuarter', 'LastQuarterToDate', 'LastYear', 'LastYearToDate', 'NextWeek', 'NextFourWeeks', 'NextMonth', 'NextQuarter', 'NextYear']:
self.DateMacro = kwargs['DateMacro']
self.FromTxnDate = self.validate_date(kwargs['FromTxnDate']) if 'FromTxnDate' in kwargs else None
self.ToTxnDate = self.validate_date(kwargs['ToTxnDate']) if 'ToTxnDate' in kwargs else None
self.ReportEntityFilter = kwargs['ReportEntityFilter'] if 'ReportEntityFilter' in kwargs else None
# print(self.DateMacro, self.TxnDateRangeFilter, self.FromTxnDate, self.ToTxnDate)
# print(self.Reuse)
# if not self.Reuse:
# self.dfDN, self.ext_doc_no_list = self.get_ext_doc_no_list(self.DN)
self.RefNumber = kwargs['RefNumber'] if 'RefNumber' in kwargs else None
# self.get_sales_order_header()
def pprintXml(self, qbxml_query):
import xml.dom.minidom
from xml.sax.saxutils import escape
from lxml import etree
# dom = xml.dom.minidom.parse(xml_fname) # or
if isinstance(qbxml_query, str):
dom = xml.dom.minidom.parseString(qbxml_query)
pretty_xml_as_string = dom.toprettyxml(" ").split('\n')
pretty_xml_as_string = '\n'.join([s for s in pretty_xml_as_string if s.strip() ])
# print(f'pprintxml:\n{pretty_xml_as_string}')
return pretty_xml_as_string
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
if type(attrib) is not dict:
attrib = {}
ele = ET.SubElement(parentNode, thisNode)
for x in attrib:
ele.set(x, attrib[x])
ele.text = text
tail = "\n"
for x in range(whiteSpace):
tail = tail + " "
ele.tail = tail
return ele
def create_QBXML(self):
root = ET.Element("QBXML")
root.tail = "\n"
root.text = "\n "
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
# QBXMLMsgsRq.set("onError", "continueOnError")
QBXMLMsgsRq.set("onError", "stopOnError")
QBXMLMsgsRq.tail = "\n"
QBXMLMsgsRq.text = "\n "
SalesOrderQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "SalesOrderQueryRq","\n " )
# SalesOrderType = self.create_sub_element(ET, SalesOrderQueryRq, 'SalesOrderType', self.SalesOrderType)
if self.RefNumber:
pass
RefNumberFilter = self.create_sub_element(ET, SalesOrderQueryRq, 'RefNumberFilter', "\n ",)
MatchCriterion = self.create_sub_element(ET,RefNumberFilter, 'MatchCriterion', "Contains",)
RefNumber = self.create_sub_element(ET, RefNumberFilter, 'RefNumber', self.RefNumber, )
elif self.FullName:
EntityFilter = self.create_sub_element(ET, SalesOrderQueryRq, 'EntityFilter', "\n ")
FullName = self.create_sub_element(ET, EntityFilter, "FullName", self.FullName, 6)
elif self.DateMacro:
TxnDateRangeFilter = self.create_sub_element(ET, SalesOrderQueryRq, "TxnDateRangeFilter", "\n ",)
SalesOrderType = self.create_sub_element(ET, TxnDateRangeFilter, "DateMacro", self.DateMacro)
# SalesOrderType = self.create_sub_element(ET, SalesOrderQueryRq, "DateMacro", self.DateMacro)
elif type(self.FromTxnDate) is datetime.date or type(self.ToTxnDate) is datetime.date:
TxnDateRangeFilter = self.create_sub_element(ET, SalesOrderQueryRq, "TxnDateRangeFilter", "\n ",)
if type(self.FromTxnDate) is datetime.date:
FromTxnDate = self.create_sub_element(ET, TxnDateRangeFilter, "FromTxnDate", self.FromTxnDate.strftime('%Y-%m-%d'),4)
if type(self.ToTxnDate) is datetime.date:
ToTxnDate = self.create_sub_element(ET, TxnDateRangeFilter, "ToTxnDate", self.ToTxnDate.strftime('%Y-%m-%d'))
if self.IncludeLineItems:
IncludeLineItems = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeLineItems", self.IncludeLineItems, 4)
if len(self.IncludeRetElement)>0:
for x in self.IncludeRetElement:
IncludeRetElement = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeRetElement", x, 4)
mydata = ET.tostring(root, encoding = "unicode")
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
qbxml_query = qbxml_query + "\n" + mydata
# print(f'create_QBXML->qbxml_query: {qbxml_query}')
return qbxml_query
def get_customer_pricelevel(self, response_string):
QBXML = ET.fromstring(response_string)
PriceLevelName = QBXML.find('.//PriceLevelRef')
if PriceLevelName:
PriceLevelName = PriceLevelName.find("FullName").text
print(f'PriceLevelName:{PriceLevelName}')
DataExtRets = QBXML.findall('.//DataExtRet')
SP_PriceLevelName = None
if len(DataExtRets)>0:
for DataExtRet in DataExtRets:
DataExtName = DataExtRet.find('DataExtName').text
DataExtValue = DataExtRet.find('DataExtValue').text
if DataExtName.lower() == 'special cust'.lower():
SP_PriceLevelName = DataExtValue.upper()
break
self.PriceLevelName = PriceLevelName
self.SPPriceLevelName = SP_PriceLevelName
return PriceLevelName, SP_PriceLevelName
def create_customerquery_QBXML(self ):
root = ET.Element("QBXML")
root.tail = "\n"
root.text = "\n "
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
# QBXMLMsgsRq.set("onError", "continueOnError")
QBXMLMsgsRq.set("onError", "stopOnError")
QBXMLMsgsRq.tail = "\n"
QBXMLMsgsRq.text = "\n "
CustomerQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "CustomerQueryRq","\n " )
# SalesOrderType = self.create_sub_element(ET, CustomerQueryRq, 'SalesOrderType', self.SalesOrderType)
FullName = self.create_sub_element(ET, CustomerQueryRq, "FullName", self.FullName, 6)
IncludeRetElement = ['FullName', 'PriceLevelRef', 'DataExtRet']
for x in IncludeRetElement:
IncludeRetElement = self.create_sub_element(ET, CustomerQueryRq, "IncludeRetElement", x, 4)
OwnerID = self.create_sub_element(ET, CustomerQueryRq, "OwnerID", "0", 6)
mydata = ET.tostring(root, encoding = "unicode")
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
qbxml_query = qbxml_query + "\n" + mydata
print(f'create_customer_QBXML->qbxml_query: {qbxml_query}')
response_string=self.connect_to_quickbooks(qbxml_query)
return self.get_customer_pricelevel(response_string)
return response_string
def create_invoiceadd_QBXML(self, soList=None):
print('create_ionvoiceadd_QBXML')
if soList:
self.SalesOrderList = soList
root = ET.Element("QBXML")
root.tail = "\n"
root.text = "\n "
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
QBXMLMsgsRq.set("onError", "stopOnError")
QBXMLMsgsRq.tail = "\n"
QBXMLMsgsRq.text = "\n "
InvoiceAddRq = self.create_sub_element(ET, QBXMLMsgsRq, "InvoiceAddRq", "\n ",2 )
InvoiceAdd = self.create_sub_element(ET, InvoiceAddRq, "InvoiceAdd", "\n ",4 )
CustomerRef = self.create_sub_element(ET, InvoiceAdd, "CustomerRef", "\n ",8 )
FullName = self.create_sub_element(ET, CustomerRef, "FullName", self.SalesOrderList[0]['CustomerFullName'], 8 )
TemplateRef = self.create_sub_element(ET, InvoiceAdd, "TemplateRef", "\n ", 8 )
TemplateFullName = self.create_sub_element(ET, TemplateRef, "FullName", "DBW Invoice (11%)", 8 )
today = str(date.today())
TxnDate = self.create_sub_element(ET, InvoiceAdd, "TxnDate", today,8) # self.DN['TxnDate'], 8 )
RefNumber = self.create_sub_element(ET, InvoiceAdd, "RefNumber", today,8 ) # self.DN['DNRefNum'], 8 )
ShipDate = self.create_sub_element(ET, InvoiceAdd, "ShipDate", today,8) # self.DN['TxnDate'], 8 )
# Memo = self.create_sub_element(ET, InvoiceAdd, "Memo", self.DN['Memo'], 10 )
disc_amount = 0
for soidx, salesorder in enumerate(self.SalesOrderList):
SOTxnId = salesorder['TxnID']
# disc_amount+=int(salesorder['Disc_Amount'])
print(f'create_invoiceadd_QBXML->SOTxnId: {SOTxnId}')
for itemline in salesorder['SalesOrderLineRet']:
backOrdered = str(itemline['BackOrdered'])
# backOrdered = '1' #testing purpose
if float(backOrdered) > 0:
discPerPcs = float(itemline['discPerPcs'])
discPerItem = float(backOrdered) * discPerPcs
disc_amount += discPerItem
InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
# Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", str(itemline['DNQuantity'] ), 12 )
Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", backOrdered, 12 )
# UnitOfMeasure = self.create_sub_element(ET, InvoiceLineAdd, "UnitOfMeasure", str(itemline['UOM']), 12 )
LinkToTxn = self.create_sub_element(ET, InvoiceLineAdd, "LinkToTxn", "\n ", 10 )
TxnID = self.create_sub_element(ET, LinkToTxn, "TxnID", SOTxnId,14 )
TxnLineID = self.create_sub_element(ET, LinkToTxn, "TxnLineID", itemline['TxnLineID'], 12 )
# if soidx == len(self.SalesOrderList)-1: #last list then set the'400_Sales_discount'
# print(f'disc_amount:{format(disc_amount, ".2f")}')
# if disc_amount != 0:
# # disc_amount=format(disc_amount, ".2f")
# InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
# ItemRef = self.create_sub_element(ET, InvoiceLineAdd, "ItemRef", "\n ", 12)
# ItemFullName = self.create_sub_element(ET, ItemRef, "FullName", "400_Sales Discount", 12)
# ItemRate = self.create_sub_element(ET, InvoiceLineAdd, "Rate", str(disc_amount), 10)
if salesorder['Disc_Amount']!=0: # disc_amount != 0:
# disc_amount=format(disc_amount, ".2f")
InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
ItemRef = self.create_sub_element(ET, InvoiceLineAdd, "ItemRef", "\n ", 12)
ItemFullName = self.create_sub_element(ET, ItemRef, "FullName", "400_Sales Discount", 12)
ItemRate = self.create_sub_element(ET, InvoiceLineAdd, "Rate", str(disc_amount), 10)
mydata = ET.tostring(root, encoding = "unicode")
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
qbxml_query = qbxml_query + "\n" + mydata
# print(f'create_invoiceadd_QBXML->Create_Invoiceadd_QBXML: {qbxml_query}')
# print(f"replyfrom qbxml:{self.connect_to_quickbooks(qbxml_query)}")
# print([s for s in qbxml_query.split('\n') if s.strip(' ') != ''])
print(self.pprintXml(qbxml_query))
return qbxml_query
def connect_to_quickbooks(self, qbxml_query):
# Connect to Quickbooks
# sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
# sessionManager.OpenConnection('', 'DASA')
# enumfodnc= win32com.client.Dispatch('QBXMLRP2.RequestProcessor')
# print(enumfodnc)
# print(enumfodnc.qbFileOpenDoNotCare)
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor", pythoncom.CoInitialize())
sessionManager.OpenConnection('', 'DASA2')
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
ticket = sessionManager.BeginSession("", 2)
# Send query and receive response
response_string = sessionManager.ProcessRequest(ticket, qbxml_query)
# Disconnect from Quickbooks
sessionManager.EndSession(ticket) # Close the company file
sessionManager.CloseConnection() # Close the connection
# print (f'response_string:{response_string}')
return response_string
def __str__(self, *args) -> str:
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
# print("__str__")
return str(self.get_sales_order_header())
# return "hello"
def get_sales_order_header(self, *args):
return self._get_sales_order_header(self.connect_to_quickbooks(self.create_QBXML()))
def status_ok(self, QBXML): #for InvoiceAddRS
tree = ET.fromstring(QBXML)
GSRQRs = tree.find(".//InvoiceAddRs")
# print(f"GSRQRs:{GSRQRs}")
status_code = GSRQRs.attrib #.get('statusCode')
# print(GSRQRs.attrib)
# print(GSRQRs.attrib['statusCode'])
status=GSRQRs.attrib.get('statusMessage')
print(f'status={status}')
if 'OK' in status:
return True, status_code
else:
return False, status_code
def get_discperpcs(self, ItemFullName, Rate:float):
discPerPcs = 0
if self.Customer:
if self.Customer[2]:
pricelist = self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2]
discPerPcs = Rate-pricelist
# ic(Rate, disc, (Rate - self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.SPPriceLevelName)].values.tolist()[0][2]))
if discPerPcs < 0 :
discPerPcs = 0
print(f"WARNING: Rate is Lower than Pricelist Cust:{self.Customer} ItemName:{ItemFullName} Rate:{Rate} < {pricelist}")
return discPerPcs
def _get_sales_order_header(self, response_string, includefullInvoiced=False):
print('_get_sales_order_header')
# print(f'responsestring:{self.pprintXml(response_string)}')
QBXML = ET.fromstring(response_string)
datadict = {}
SalesOrderdict = {}
_SalesOrderlist = []
SalesOrderRets = QBXML.findall('.//SalesOrderRet')
if self.Customer == None:
custtemp = QBXML.find('.//SalesOrderRet/CustomerRef/FullName')
if custtemp != None:
self.Customer = custtemp.text
self.Customer = self._df_customer.loc[(self._df_customer["FullName"]==self.Customer)].values.tolist()[0]
print(f'Customer:{self.Customer}')
# print(SalesOrderRets)
for SalesOrderRet in SalesOrderRets:
RefNumber = SalesOrderRet.find('RefNumber').text
# Memo = SalesOrderRet.find('Memo').text
TxnDate = SalesOrderRet.find('TxnDate').text
TxnNumber = SalesOrderRet.find('TxnNumber').text
CustomerFullName = SalesOrderRet.find('CustomerRef/FullName').text
TxnID = SalesOrderRet.find('TxnID').text
TotalAmount = SalesOrderRet.find('TotalAmount').text
IsFullyInvoiced = SalesOrderRet.find('IsFullyInvoiced').text
IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
if includefullInvoiced==False:
if IsFullyInvoiced.lower()=='true':
# print(IsFullyInvoiced)
continue
# print(CustomerFullName, TxnID, TotalAmount)
SalesOrderdict = {'RefNumber':RefNumber, 'CustomerFullName':CustomerFullName, 'TxnID':TxnID,
'TxnDate':TxnDate, 'TxnNumber':TxnNumber,
'TotalAmount':TotalAmount, 'IsFullyInvoiced':IsFullyInvoiced, 'IsManuallyClosed':IsManuallyClosed, 'SalesOrderLineRet':[]}
SalesOrderLineRet = SalesOrderRet.findall('SalesOrderLineRet')
# ic(len(SalesOrderLineRet))
disc_amount=0
if len(SalesOrderLineRet) > 0:
# disc_amount=0
for SalesOrderLineRet in SalesOrderLineRet:
discPerItem = 0
discPerPcs = 0
TxnLineID = SalesOrderLineRet.find('TxnLineID').text
ItemFullName = SalesOrderLineRet.find('ItemRef/FullName')
if ItemFullName is None:
print("no itemfullname")
break
else:
ItemFullName=ItemFullName.text
# print(ItemFullName)
Quantity = SalesOrderLineRet.find('Quantity').text
UnitOfMeasure = SalesOrderLineRet.find('UnitOfMeasure').text
OverrideUOMSetRef = SalesOrderLineRet.find('OverrideUOMSetRef/FullName')
if OverrideUOMSetRef != None:
OverrideUOMSetRef = OverrideUOMSetRef.text
Rate = float(SalesOrderLineRet.find('Rate').text)
Amount = float(SalesOrderLineRet.find('Amount').text)
# if self.SPPriceLevelName:
Invoiced = SalesOrderLineRet.find('Invoiced').text
LineIsManuallyClosed = SalesOrderLineRet.find('IsManuallyClosed').text
# print(TxnLineID, ItemFullName)
BackOrdered = float(Quantity) - float(Invoiced)
if BackOrdered > 0 and LineIsManuallyClosed.lower() == 'false' :
# ic(self.Customer)
discPerPcs = self.get_discperpcs(ItemFullName, Rate)
discPerItem = BackOrdered * discPerPcs
disc_amount += discPerItem
# if self.Customer:
# if self.Customer[2]:
# discPerPcs = Rate-self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2]
# # ic(Rate, disc, (Rate - self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.SPPriceLevelName)].values.tolist()[0][2]))
# if discPerPcs > 0:
# print(Quantity, BackOrdered, Rate, (Rate - self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2]))
# discPerItem = BackOrdered * discPerPcs
# disc_amount += discPerItem
# # disc_amount += BackOrdered * discPerPcs # (Rate-self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2])
# else:
# discPerPcs = 0
SalesOrderLinedict = {'TxnLineID':TxnLineID,
'ItemFullName':ItemFullName,
'Quantity':Quantity,
'UOM':UnitOfMeasure,
'Rate':Rate,
'Amount':Amount,
'BackOrdered':BackOrdered,
'Invoiced':Invoiced,
'LineIsManuallyClosed':LineIsManuallyClosed,
'discPerItem':discPerItem, # backorder qty * disc per pcs
'discPerPcs':discPerPcs,
}
SalesOrderdict['SalesOrderLineRet'].append(SalesOrderLinedict)
SalesOrderdict['Disc_Amount']=disc_amount
_SalesOrderlist.append(SalesOrderdict)
# print(_SalesOrderlist)
self.SalesOrderList=_SalesOrderlist
# print(f'_get_sales_order_header->Salesorderlist: {self.SalesOrderList}')
return self.SalesOrderList
def addDiscountToInvoiceList(self, _dict:dict):
print("addDiscountToInvoiceList")
def addDNQtyToSalesOrderList(self, _dict:dict):
_bolfoundrefnum=False
_bol_dictisadded=False
Error_msg = None
for poidx, _po in enumerate(self.SalesOrderList):
if _po['RefNumber']==_dict['RefNum']:
_bolfoundrefnum=True
if len(_po['SalesOrderLineRet'])>0:
for polineidx, _poline in enumerate(_po['SalesOrderLineRet']):
pass
if _poline['ItemFullName']==_dict['FullName']:
if _poline['UOM'].upper()==_dict['UOM'].upper():
# first do UOM in _dict convert treatment
QuantityIn_dict = _dict['Quantity']
if _dict['UOM'].upper().startswith('ROLL_'):
print(f"addDNQtyToSalesOrderList->DNqty:{_dict['Quantity']}, Roll_:{_dict['UOM'].split('_')[1]}")
QuantityIn_dict = _dict['Quantity'] * int(_dict['UOM'].split("_")[1])
pass
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "CUTTER":
print("addDNQtyToSalesOrderList->cutter")
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("EB-") or _dict['Item No'].upper().startswith("TA-")):
print("addDNQtyToSalesOrderList->LEM")
if _dict['Item No'].split("-")[1].endswith("1006"):
QuantityIn_dict = QuantityIn_dict * 12
elif _dict['Item No'].split("-")[1].endswith("1025"):
QuantityIn_dict = QuantityIn_dict * 6
elif _dict['Item No'].split("-")[1].endswith("1100"):
pass
print("1100 lem")
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("TL-") or _dict['Item No'].upper().startswith("TFL-")):
print("addDNQtyToSalesOrderList->Lock")
QuantityIn_dict = QuantityIn_dict * 20
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "EDG-TRIMMER":
QuantityIn_dict = QuantityIn_dict * 12 #coz box of 12
if _poline['BackOrdered']>=QuantityIn_dict:
self.SalesOrderList[poidx]['SalesOrderLineRet'][polineidx]['DNQuantity']=float(QuantityIn_dict)
_bol_dictisadded = True
else:
print(f"{_poline['ItemFullName']} BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}")
Error_msg = f"BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}"
else:
# print(f"UOM different {_poline['UOM']} <> {_dict['UOM']}")
Error_msg = f"UOM different {_poline['UOM']} <> {_dict['UOM']}"
else:
# print("errorpoline <>DN")
Error_msg = f"poline[ItemFullName] <> DN[FullName]; {_poline['ItemFullName']} <> {_dict['FullName']}, maybe there are 2 same namefromtaco in QB"
else:
pass
# print(f"this refnum {_dict['RefNum']} have no QB PO Return Line")
Error_msg = f"this refnum {_dict['RefNum']} have no QB PO Return Line"
# print (_bol_dictisadded, Error_msg)
return _bol_dictisadded, Error_msg
def prepareInvoice(self, df:pd.DataFrame = None):
# print(df)
_bolAllDNareOk = True
_notindflist=[]
_yescounter = 0
_nocounter = 0
if df is not None:
_dflist = df.to_dict('records')
# print(_dflist)
# else:
# _dflist = self.dfDN.to_dict('records')
# print(self.dfDN)
# print(f'_dflist:{_dflist}')
for idx, xdf in enumerate(_dflist):
_boladdDN, _Errormsg = self.addDNQtyToSalesOrderList(xdf)
# print(f'prepareInvoice->_Errormsg:{_Errormsg}')
if _boladdDN:
_dflist[idx]['ADDED']=True
elif _Errormsg:
_dflist[idx]['ERROR']=_Errormsg
for xdf in (_dflist):
if 'ADDED' not in xdf:
# print (f"prepareInvoice->not added: {xdf['Item No']}")
print (f"prepareInvoice->not added: {xdf}")
_notindflist.append(xdf)
_nocounter+=1
_bolAllDNareOk = False
else:
print (f"ADDED: {xdf['Item No']}")
print(f'{len(_dflist) - _nocounter} of {len(_dflist)} are added')
return _bolAllDNareOk, _notindflist
def validate_date(self, date_text):
if date_text is None:
return None
try:
return datetime.datetime.strptime(date_text, '%Y-%m-%d').date()
except ValueError:
return None
# raise ValueError("Incorrect data format, should be YYYY-MM-DD")
def get_ext_doc_no_list(self, dndict=None):
if dndict:
dnlist = dndict['lines']
else:
dnlist = self.DN
df = pd.DataFrame(dnlist)
# print(df)
df['RefNum']= df['No.SO/Ext.Doc.No.'].apply(lambda x: "L"+ x.split("L-")[1])
# print(df)
ext_doc_no=df['RefNum'].unique().tolist()
if len(ext_doc_no)>0:
return df, ext_doc_no
else:
return df, None
def get_open_so(self, response_string=None):
if not response_string:
response_string = self.connect_to_quickbooks(self.create_QBXML())
QBXML = ET.fromstring(response_string)
_OpenSalesOrderlist = []
SalesOrderRets = QBXML.findall('.//SalesOrderRet')
# print(f'SalesOrderRets count:{len(SalesOrderRets)}')
for SalesOrderRet in SalesOrderRets:
IsFullyInvoiced = SalesOrderRet.find('IsFullyInvoiced').text
IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
if IsFullyInvoiced=='false' and IsManuallyClosed=='false':
txndate = SalesOrderRet.find('TxnDate').text
totalamount = SalesOrderRet.find('TotalAmount').text
refnumber = SalesOrderRet.find('RefNumber').text
_OpenSalesOrderlist.append([SalesOrderRet.find('TxnID').text, txndate, totalamount, refnumber, ])
# _OpenSalesOrderlist.append(SalesOrderRet.find('TxnID').text)
# RefNumber = SalesOrderRet.find('RefNumber').text
# Memo = SalesOrderRet.find('Memo').text
# CustomerFullName = SalesOrderRet.find('CustomerRef/FullName').text
# TxnID = SalesOrderRet.find('TxnID').text
# TotalAmount = SalesOrderRet.find('TotalAmount').text
# IsFullyReceived = SalesOrderRet.find('IsFullyReceived').text
# IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
# print(_OpenSalesOrderlist)
return _OpenSalesOrderlist
def create_open_sales_order_qbxml(self, txnid:list, IncludeLineItems=True):
root = ET.Element("QBXML")
root.tail = "\n"
root.text = "\n "
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
# QBXMLMsgsRq.set("onError", "continueOnError")
QBXMLMsgsRq.set("onError", "stopOnError")
QBXMLMsgsRq.tail = "\n"
QBXMLMsgsRq.text = "\n "
SalesOrderQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "SalesOrderQueryRq","\n " )
if len(txnid)>0:
for x in txnid:
TxnID = self.create_sub_element(ET, SalesOrderQueryRq, "TxnID", x, 6 )
else:
return None
if IncludeLineItems:
IncludeLineItems = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeLineItems", 'true', 4)
# if len(self.IncludeRetElement)>0:
# for x in self.IncludeRetElement:
# IncludeRetElement = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeRetElement", x, 4)
mydata = ET.tostring(root, encoding = "unicode")
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
qbxml_query = qbxml_query + "\n" + mydata
print(f'create_open_sale_order_qbxml->qbxml_query: {qbxml_query}')
return qbxml_query
def get_open_sales_order(self, txnlist:list):
print(f'txnid: {txnlist}', type(txnlist))
txnid = []
for x in txnlist:
# print(x, type(x))
if isinstance(x, list):
txnid.append(x[0])
else:
txnid.append(x)
# break
# txnid = [x[0] if isinstance(x, list) else x for x in txnid ]
print(txnid)
if txnid:
# print(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
return self._get_sales_order_header(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
else:
print("There is No Open Sales Order")
return None
return None
print('### SalesOrder ###')
if __name__ == '__main__':
starttime = timeit.default_timer()
# ini=SalesOrderQuery(FullName= '999 HPL', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'IsManuallyClosed', 'IsFullyInvoiced'])
# ini=SalesOrderQuery(FullName= 'YSM Interior', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
# ini=SalesOrderQuery(FullName= 'Abadi Serpong', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
# ini=SalesOrderQuery(RefNumber = 'B23070685', FullName= 'Abadi Serpong', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
ini=SalesOrderQuery(RefNumber = 'B23070685', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
# iya = ini.create_customerquery_QBXML() #pakai excel saja lebih cepat
# print(iya)
print(f'createQBXML:{ini.create_QBXML()}')
response_string = ini.connect_to_quickbooks(ini.create_QBXML())
# print(f'response_string:{response_string}')
response_string = None
open_sales_orders = ini.get_open_so()
print(f'open sales orders:{open_sales_orders}')
if open_sales_orders:
itu = ini.get_open_sales_order(open_sales_orders)
# print(itu)
print(f'get_open_sales_order:{itu}')
if itu:
print(ini.create_invoiceadd_QBXML())
# ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML())
print("The time difference is :", timeit.default_timer() - starttime)