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 from decimal import Decimal # 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.TxnID = kwargs['TxnID'] if 'TxnID' in kwargs else None 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.TxnID: pass print(self.TxnID) if isinstance(self.TxnID, list): for _ in self.TxnID: TxnID = self.create_sub_element(ET, SalesOrderQueryRq, "TxnID", _, ) else: TxnID = self.create_sub_element(ET, SalesOrderQueryRq, "TxnID", self.TxnID, ) else: 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 = """\n""" qbxml_query = qbxml_query + """""" 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 = """\n""" qbxml_query = qbxml_query + """""" 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, soDict=None): print('create_ionvoiceadd_QBXML') txn_date = str(date.today()) ref_number = None if soDict: self.SalesOrderList = soDict['data'] txn_date = soDict.get('txn_date', str(date.today())) ref_number = soDict.get('ref_number', None) # txn_date = str(date.today()) 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()) # print(txn_date) TxnDate = self.create_sub_element(ET, InvoiceAdd, "TxnDate", txn_date, 8) # self.DN['TxnDate'], 8 ) if ref_number: RefNumber = self.create_sub_element(ET, InvoiceAdd, "RefNumber", ref_number, 8 ) # self.DN['DNRefNum'], 8 ) # ShipDate = self.create_sub_element(ET, InvoiceAdd, "ShipDate", txn_date,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): disc_amount = 0 if 'TxnID' in salesorder: 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) elif 'other_itemFullName' in salesorder and 'other_qty' in salesorder and 'other_rate' in salesorder: pass 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", salesorder['other_itemFullName'], 12) Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", str(salesorder['other_qty']), 12 ) ItemRate = self.create_sub_element(ET, InvoiceLineAdd, "Rate", str(salesorder['other_rate']), 10) mydata = ET.tostring(root, encoding = "unicode") qbxml_query = """\n""" qbxml_query = qbxml_query + """""" 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 self.pprintXml(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 self.pprintXml(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_saved_refnumber(self, QBXML): #for InvoiceAddRS tree = ET.fromstring(QBXML) objects = {} GSRQRs = tree.find(".//InvoiceAddRs") refnumber = tree.find(".//RefNumber").text print(f"saved refnumber:{refnumber}") objects['RefNumber'] = refnumber objects['Customer_FullName']= tree.find(".//CustomerRef/FullName").text objects['TxnDate'] = tree.find(".//TxnDate").text objects['BalanceRemaining'] = tree.find(".//BalanceRemaining").text return objects def get_discperpcs(self, ItemFullName, Rate): discPerPcs = 0 if self.Customer: if self.Customer[2]: try: 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}") except: print('Pricelevelname not found') return None return discPerPcs def _get_sales_order_header(self, response_string, includefullInvoiced=False): print('_get_sales_order_header') print(f'responsestring sales order header:{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 convertQTY = 1 TxnLineID = SalesOrderLineRet.find('TxnLineID').text ItemFullName = SalesOrderLineRet.find('ItemRef/FullName') if ItemFullName is None: print("no itemfullname") continue else: ItemFullName=ItemFullName.text # print(ItemFullName) Quantity = SalesOrderLineRet.find('Quantity').text UnitOfMeasure = SalesOrderLineRet.find('UnitOfMeasure').text ### modified if UOM has ConvertQTY: '_' or ' of '-> in OverrideUOMSetRef if '_' in UnitOfMeasure: convertQTY = int(UnitOfMeasure.split('_')[1]) OverrideUOMSetRef = SalesOrderLineRet.find('OverrideUOMSetRef/FullName') if OverrideUOMSetRef != None: OverrideUOMSetRef = OverrideUOMSetRef.text if 'of' in OverrideUOMSetRef and UnitOfMeasure.upper() == 'BOX': convertQTY = int(OverrideUOMSetRef.split('of')[1]) print(f'OverrideUOMSetRef:{OverrideUOMSetRef}') ### Rate = Decimal(SalesOrderLineRet.find('Rate').text) Amount = Decimal(SalesOrderLineRet.find('Amount').text) # if self.SPPriceLevelName: Invoiced = SalesOrderLineRet.find('Invoiced').text LineIsManuallyClosed = SalesOrderLineRet.find('IsManuallyClosed').text # print(TxnLineID, ItemFullName) BackOrdered = Decimal(Quantity) - Decimal(Invoiced) if BackOrdered > 0 and LineIsManuallyClosed.lower() == 'false' : # ic(self.Customer) discPerPcs = self.get_discperpcs(ItemFullName, Rate) if discPerPcs == None: return False 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, 'convertQTY':convertQTY, } 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 = """\n""" qbxml_query = qbxml_query + """""" 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)