dasaproject/SO_to_Inv/readSO.py
2024-07-24 07:35:25 +07:00

701 lines
38 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
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.IncludeLinkedTxns = kwargs['IncludeLinkedTxns'] if 'IncludeLinkedTxns' 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 self.IncludeLinkedTxns:
IncludeLinkedTxns = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeLinkedTxns", self.IncludeLinkedTxns, 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, 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 = """<?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 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 #skip this orderline
else:
ItemFullName=ItemFullName.text
# print(ItemFullName)
if 'Sales' in ItemFullName and 'Disc' in ItemFullName:
continue #skip this sales discount line
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 = """<?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)