This commit is contained in:
Sugiarto 2024-02-02 23:32:51 +07:00
commit 39d788c5f0
18 changed files with 1099 additions and 188 deletions

View File

@ -179,7 +179,8 @@ if __name__ == "__main__":
# ini= ItemInventoryQuery(IncludeRetElement=['FullName', 'DataExtRet'] , OwnerID = str(x), MaxReturned=None)
# ini= ItemInventoryQuery( OwnerID = str(x), MaxReturned="10", NameRangeFilter=["TSHT:TS WOODGRAIN:TS-252", "TSHT:TS WOODGRAIN:TS-252"])
# ini= ItemInventoryQuery( OwnerID = str(x), MaxReturned="10", NameRangeFilter=["TSHT:TS LUXURY:TS-L-252", "TSHT:TS LUXURY:TS-L-252"])
ini= ItemInventoryQuery( OwnerID = str(x), MaxReturned="10")
# ini= ItemInventoryQuery( OwnerID = str(x), MaxReturned="10")
ini= ItemInventoryQuery( OwnerID ="0", MaxReturned=None)
# ini= ItemInventoryQuery( OwnerID = str(x))
# ini.to_excel('ItemInventory\ItemInventory_FromQB.xlsx')
itu = ini.connect_to_quickbooks(ini.create_QBXML())
@ -189,7 +190,7 @@ if __name__ == "__main__":
print("YEAH")
df = pd.DataFrame.from_dict(ini.get_data(itu))
print(df)
# df.to_excel('ItemInventory\ItemInventory_FromQB.xlsx', index=False)
df.to_excel('ItemInventory\ItemInventory_FromQB.xlsx', index=False)
modtime = datetime.fromtimestamp(os.path.getmtime('ItemInventory\ItemInventory_FromQB.xlsx'))
print(f"modified Time:{modtime}")
break

View File

@ -6,6 +6,9 @@ import datetime
import pandas as pd
from datetime import date
import timeit
import pythoncom
import os
class PriceLevelQuery:
def __init__(self, **kwargs) -> None:
@ -16,8 +19,13 @@ class PriceLevelQuery:
self.IncludeRetElement = kwargs['IncludeRetElement'] if 'IncludeRetElement' in kwargs else []
self.TxnDateRangeFilter = kwargs['TxnDateRangeFilter'] if 'TxnDateRangeFilter' in kwargs else None
self.FullName = kwargs['FullName'] if 'FullName' in kwargs and isinstance(kwargs['FullName'], list) else []
print(f'FULLNAME:{self.FullName}')
self.NameFilter = kwargs['NameFilter'] if 'NameFilter' in kwargs else None
self.ItemRef = kwargs['ItemRef'] if 'ItemRef' in kwargs else None
self.QBXML = None
self.response_string = None
self.status_ok = False
self.status_msg = None
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
if type(attrib) is not dict:
@ -62,7 +70,8 @@ class PriceLevelQuery:
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
self.QBXML = qbxml_query
return self.QBXML
def create_invoiceadd_QBXML(self):
root = ET.Element("QBXML")
@ -124,7 +133,7 @@ class PriceLevelQuery:
# enumfodnc= win32com.client.Dispatch('QBXMLRP2.RequestProcessor')
# print(enumfodnc)
# print(enumfodnc.qbFileOpenDoNotCare)
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor", pythoncom.CoInitialize())
sessionManager.OpenConnection('', 'DASA2')
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
@ -137,7 +146,8 @@ class PriceLevelQuery:
sessionManager.EndSession(ticket) # Close the company file
sessionManager.CloseConnection() # Close the connection
# print (f'response_string:{response_string}')
return response_string
self.response_string = response_string
return self.response_string
def __str__(self, *args) -> str:
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
@ -148,10 +158,11 @@ class PriceLevelQuery:
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)
def get_status(self, QBXML=None): #for InvoiceAddRS
if not QBXML:
tree = ET.fromstring(self.response_string)
else:
tree = ET.fromstring(QBXML)
GSRQRs = tree.find(".//PriceLevelQueryRs")
# print(f"GSRQRs:{GSRQRs}")
status_code = GSRQRs.attrib #.get('statusCode')
@ -159,10 +170,13 @@ class PriceLevelQuery:
# print(GSRQRs.attrib['statusCode'])
status=GSRQRs.attrib.get('statusMessage')
print(f'status={status}')
print(f'get_status={status}')
self.status_msg = status_code
if 'OK' in status:
self.status_ok = True
return True, status_code
else:
self.status_ok = False
return False, status_code
@ -346,7 +360,7 @@ class PriceLevelQuery:
def get_pricelevel(self, response_string=None):
if not response_string:
response_string = self.connect_to_quickbooks(self.create_QBXML())
statusok, status = self.status_ok(response_string)
statusok, status = self.get_status(response_string)
if statusok:
QBXML = ET.fromstring(response_string)
PriceLevellist = {}
@ -356,7 +370,6 @@ class PriceLevelQuery:
PriceLevelNamelist = []
# PriceLevelName = QBXML.find('.//Name').text
# print(f'PriceLevelPerItemRets count:{len(PriceLevelPerItemRets)}')
for PriceLevelRet in PriceLevelRets:
PriceLevelPerItemRets = PriceLevelRet.findall('.//PriceLevelPerItemRet')
PriceLevelName = PriceLevelRet.find('.//Name').text
@ -375,11 +388,12 @@ class PriceLevelQuery:
PriceLeveldf.sort_values(by=['PriceLevelName', 'FullName'], inplace=True)
PriceLeveldf=PriceLeveldf.reset_index(drop=True)
print(PriceLeveldf)
print(os.getcwd())
PriceLeveldf.to_excel('ItemInventory\PriceLevel.xlsx', sheet_name=PriceLevelName, index=False )
# print(PriceLevellist)
return PriceLevellist
return PriceLevellist, status
else:
return None
return None, status
def create_open_sales_order_qbxml(self, txnid:list, IncludeLineItems=True):
root = ET.Element("QBXML")
@ -421,19 +435,40 @@ class PriceLevelQuery:
return None
return None
def name_list(self):
self.get_status(self.connect_to_quickbooks(self.create_QBXML()))
namelist = []
if self.status_ok:
tree = ET.fromstring(self.response_string)
for _ in tree.iter('Name'):
# print(_.text)
namelist.append(_.text)
if len(namelist)>0:
return namelist
return None
print('### PriceLevelQuery ###')
if __name__ == '__main__':
starttime = timeit.default_timer()
# ini=PriceLevelQuery(FullName= '999 HPL', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'IsManuallyClosed', 'IsFullyInvoiced'])
# ini=PriceLevelQuery(FullName= 'abadi serpong', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
ini = PriceLevelQuery(FullName = ['t 202202', 'M 202202'], )
FullName = ['t 202202', 'M 202202', 'b 202202']
FullName = None
ini = PriceLevelQuery(FullName = FullName, IncludeRetElement=['Name'] )
# ini = PriceLevelQuery( ItemRef="ECO:0:ECO-002")
itu = ini.create_QBXML()
print(f'createQBXML->main:{itu}')
response_string = ini.connect_to_quickbooks(itu)
print(f'response_string:{response_string}')
response_string = None
pricelevel = ini.get_pricelevel()
print(ini.name_list())
# itu = ini.create_QBXML()
# print(f'createQBXML->main:{itu}')
# response_string = ini.connect_to_quickbooks(itu)
# print(f'response_string:{response_string}')
# response_string = None
# pricelevel, status = ini.get_pricelevel()
# if pricelevel:
# print(f'Success Save Price Level : {FullName}')
# else:
# print(f"Saving Not Success. status: {status}")
# print(f'pricelevel:{pricelevel}')
# print(ini.get_open_sales_order(open_sales_orders))

View File

@ -8,7 +8,10 @@ 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}')
@ -17,20 +20,25 @@ class SalesOrderQuery:
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(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 = self._df_customer.loc[(self._df_customer["FullName"]==self.FullName)].values.tolist()[0]
print(f'Customer:{self.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
@ -52,9 +60,23 @@ class SalesOrderQuery:
# 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 self.ext_doc_no_list
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 = {}
@ -79,20 +101,34 @@ class SalesOrderQuery:
QBXMLMsgsRq.text = "\n "
SalesOrderQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "SalesOrderQueryRq","\n " )
# SalesOrderType = self.create_sub_element(ET, SalesOrderQueryRq, 'SalesOrderType', self.SalesOrderType)
if self.FullName:
EntityFilter = self.create_sub_element(ET, SalesOrderQueryRq, 'EntityFilter', "\n ")
FullName = self.create_sub_element(ET, EntityFilter, "FullName", self.FullName, 6)
if 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)
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'))
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:
@ -153,8 +189,17 @@ class SalesOrderQuery:
return self.get_customer_pricelevel(response_string)
return response_string
def create_invoiceadd_QBXML(self):
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 "
@ -168,35 +213,56 @@ class SalesOrderQuery:
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 )
# 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):
SOTxnId = salesorder['TxnID']
disc_amount+=int(salesorder['Disc_Amount'])
print(f'create_invoiceadd_QBXML->SOTxnId: {SOTxnId}')
for itemline in salesorder['SalesOrderLineRet']:
# if 'DNQuantity' in itemline:
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", str(itemline['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 = 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")
@ -205,8 +271,10 @@ class SalesOrderQuery:
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(' ') != ''])
return qbxml_query
# print(self.pprintXml(qbxml_query))
return self.pprintXml(qbxml_query)
def connect_to_quickbooks(self, qbxml_query):
@ -229,7 +297,7 @@ class SalesOrderQuery:
sessionManager.EndSession(ticket) # Close the company file
sessionManager.CloseConnection() # Close the connection
# print (f'response_string:{response_string}')
return response_string
return self.pprintXml(response_string)
def __str__(self, *args) -> str:
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
@ -258,49 +326,127 @@ class SalesOrderQuery:
return False, status_code
def _get_sales_order_header(self, response_string):
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:{response_string}')
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')
# print(len(SalesOrderLineRet))
# ic(len(SalesOrderLineRet))
disc_amount=0
if len(SalesOrderLineRet) > 0:
disc_amount=0
# disc_amount=0
for SalesOrderLineRet in SalesOrderLineRet:
discPerItem = 0
discPerPcs = 0
convertQTY = 1
TxnLineID = SalesOrderLineRet.find('TxnLineID').text
ItemFullName = SalesOrderLineRet.find('ItemRef/FullName').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
Rate = float(SalesOrderLineRet.find('Rate').text)
Amount = float(SalesOrderLineRet.find('Amount').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:
if self.Customer[2]:
# print(Rate, (Rate - self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.SPPriceLevelName)].values.tolist()[0][2]))
print(Quantity, 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]))
disc_amount += float(Quantity) * (Rate-self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2])
# disc_amount+=float(Quantity)*2000 #testing only
Invoiced = SalesOrderLineRet.find('Invoiced').text
LineIsManuallyClosed = SalesOrderLineRet.find('IsManuallyClosed').text
# print(TxnLineID, ItemFullName)
BackOrdered = float(Quantity) - float(Invoiced)
if BackOrdered:
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,
@ -310,6 +456,9 @@ class SalesOrderQuery:
'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
@ -318,7 +467,8 @@ class SalesOrderQuery:
self.SalesOrderList=_SalesOrderlist
# print(f'_get_sales_order_header->Salesorderlist: {self.SalesOrderList}')
return self.SalesOrderList
def addDiscountToInvoiceList(self, _dict:dict):
print("addDiscountToInvoiceList")
@ -501,11 +651,12 @@ class SalesOrderQuery:
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
# break
# txnid = [x[0] if isinstance(x, list) else x for x in txnid ]
print(txnid)
if txnid:
@ -522,7 +673,9 @@ if __name__ == '__main__':
# 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(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()}')

View File

@ -0,0 +1,30 @@
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load humanize %}
{% block title %}Prepare Data{% endblock title %}
{% block body %}
<div class="container-fluid">
<form action="" method="POST" class="card border-info-subtle p-3 mt-2" autocomplete="off" id="form">
{% csrf_token %}
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" name="subject" value="Item Inventory">Item Inventory</button>
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" name="subject" value="Customer List">Customer List</button>
<div class="container-fluid card border-info p-3 mt-3">
{% for so_no in objects %}
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox{{forloop.count}}" name="selected_items" value="{{so_no}}">{{so_no}}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" name="subject" value="Price Level List">Price Level List</button>
</div>
</form>
</div>
<datalist id='itemname'>
{% for customer in customers %}
<option value="{{customer|first}}" data-pk="{{customer|first}}" data-desc="{{customer|first}}" data-rate="{{customer|first}}">
{% endfor %}
</datalist>
{% endblock body %}

View File

@ -4,18 +4,7 @@
{% block title %}Invoice{% endblock title %}
{% block body %}
{% comment %} {% if messages %}
<div class="alert alert-success" role="alert">
{{message}}
</div>
{% endif %} {% endcomment %}
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="alert alert-{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<div class="container-fluid">
<form action="" method="POST" class="card border-info-subtle p-3 mt-2" autocomplete="off" id="form">
@ -23,7 +12,7 @@
<div class="row">
</div>
<!-- <input name="customer_fullname" type='text' list='itemname' onchange='changeitem(event)' value='{{customer_name}}' class='textinput form-control'> -->
<input name="customerreffullname" type='text' list='itemname' value='{{customer_name}}' id='id_CustomerRefFullName' class='textinput form-control' autofocus>
<input name="customerreffullname" type='text' list='itemname' value='{{customer_name}}' id='id_CustomerRefFullName' class='textinput form-control' placeholder="Choose Customer" autofocus>
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" id="id_btnsave" onclick="checksubmit(event)">Save</button>
<!-- <button type="submit" class="btn btn-primary mt-2 me-2 ms-2" id="id_btnsave" >Save</button> -->

View File

@ -0,0 +1,110 @@
{% extends 'base.html' %}
{% block body %}
<!-- <form method="POST" action="{{ request.path }}"> -->
<div class="container">
<form method="POST" action="{% url 'Invoice:save_inv' %}" class="modal-content">
{% csrf_token %}
<div class="modal-header">
<!-- <h1>Choose Items</h1> -->
<h2>{{ customer_fullname }}</h2>
<div>
<h3> <label class="form-check-label">Ref Number</label> <input type="text" name="ref_number" value="">
</h3>
</div>
<div>
<h3> <label class="form-check-label">Date</label> <input type="date" name="date" required>
</h3>
</div>
</div>
<div class="modal-body">
<div class="mb-3 mt-3">
<input type="hidden" name="customer_fullname" value="{{ customer_fullname }}">
<!-- <input name="customerreffullname" type='text' list='itemname' value='{{customer_name}}' id='id_CustomerRefFullName' class='textinput form-control' autofocus> -->
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Item</th>
<th scope="col">Date</th>
<th scope="col">SO Number</th>
<th scope="col">Ordered</th>
<th scope="col">Prev. Inv</th>
<th scope="col">To Invoice</th>
<th scope="col">UOM</th>
<th scope="col">Rate</th>
</tr>
</thead>
<tbody>
{% for so_dict in objects %}
<!-- {{so_dict}} -->
{% for so_line in so_dict.SalesOrderLineRet %}
<tr>
<td>
<input type="hidden" name="RefNumber" value="{{ so_dict.RefNumber }}">
<input type="hidden" name="CustomerFullName" value="{{ so_dict.CustomerFullName }}">
<input type="hidden" name="TxnID" value="{{ so_dict.TxnID }}">
<input type="hidden" name="TxnDate" value="{{ so_dict.TxnDate }}">
<input type="hidden" name="TotalAmount" value="{{ so_dict.TotalAmount }}">
{% for k, v in so_line.items %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endfor %}
<div class="form-check">
<input type="checkbox" name="so_field" value="{{ so_line.TxnLineID }}" id="id_so_field_{{forloop.counter}}" class="form-check-input" checked>
<label for="id_so_field_{{ forloop.counter }}" class="form-check-label">{{so_line.ItemFullName}}
</label>
</div>
</td>
<td>{{ so_dict.TxnDate }}</td>
<td>{{ so_dict.RefNumber }}</td>
<td>{% widthratio so_line.Quantity so_line.convertQTY 1 %}</td>
<td>{% widthratio so_line.Invoiced so_line.convertQTY 1 %}</td>
<td><input type="number" name="backordered" required value="{% widthratio so_line.BackOrdered so_line.convertQTY 1 %}" min="0" max="{% widthratio so_line.BackOrdered so_line.convertQTY 1 %}"> </td>
<td>{{ so_line.UOM }}</td>
<td><input type="number" name="rate" required value="{% widthratio so_line.Rate 1 so_line.convertQTY %}" min="0" > </td>
</tr>
{% endfor %}
{% endfor %}
<tr>
<!-- bisa jadikan for loop jika banyak item lagi -->
<td>
<input type="hidden" name="other_items" value="peti">
<div class="form-check">
<input type="checkbox" name="selected_items" value="peti" class="form-check-input peti">
<label class="form-check-label">PETI</label>
</div>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><input type="number" name="other_qty" required value="1" min="0" max="100"> </td>
<td>PETI</td>
<td><input type="number" name="other_rate" required value="40000" min="0" max="40000000"> </td>
</tr>
<tr>
<td>
<input type="hidden" name="other_items" value="expedition">
<div class="form-check">
<input type="checkbox" name="selected_items" value="expedition" class="form-check-input expedition">
<label class="form-check-label">Ekspedisi</label>
</div>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><input type="number" name="other_qty" required value="1" min="0" max="1"> </td>
<td></td>
<td><input type="number" name="other_rate" required value="0" min="0" max="10000000"> </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" id="id_btnsave" >Save to Invoice</button>
</div>
</form>
</div>
{% endblock %}

View File

@ -2,10 +2,10 @@
{% block body %}
<!-- <form method="POST" action="{{ request.path }}"> -->
<div class="container">
<form method="POST" action="{% url 'Invoice:show_inv' %}" class="modal-content">
<form method="POST" action="{% url 'Invoice:choose_inv' %}" class="modal-content">
{% csrf_token %}
<div class="modal-header">
<h1>Choose SO</h1>
<h2>Select SO Customer: {{ customer_fullname }}</h2>
</div>
<div class="modal-body">
<div class="mb-3 mt-3">
@ -40,7 +40,7 @@
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" id="id_btnsave" >Save</button>
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" id="id_btnsave" >Choose</button>
</div>
</form>
</div>

View File

@ -1,13 +1,18 @@
from django.urls import path
from . import views
# from icecream import install
# install()
# from icecream import ic
# ic.configureOutput(includeContext=True, )
app_name = "Invoice"
urlpatterns = [
# path('', views.home_view ),
path('', views.show_customer, name="show_customer" ),
path('selectso', views.select_so, name="select_so" ),
path('showinv', views.show_inv, name="show_inv" ),
path('showinv', views.choose_inv, name="choose_inv" ),
path('saveinv', views.save_inv, name="save_inv" ),
path('preparedata', views.prepare_data, name="prepare_data" ),
path('', views.index, name="index"),

View File

@ -1,6 +1,6 @@
from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from django.http import HttpResponse, JsonResponse
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from django.forms import modelformset_factory, inlineformset_factory
from django.db import transaction
from django.core import serializers
@ -14,8 +14,14 @@ from django.contrib import messages
import os
import pandas as pd
from django.conf import settings
# from icecream import ic
import timeit
from decimal import Decimal
from datetime import datetime
def get_SalesOrderQuery(customer_name):
# ic.configureOutput(includeContext= True)
def get_SalesOrderQuery(customer_name, txnid=None):
try:
print("try")
from SO_to_Inv.readSO import SalesOrderQuery
@ -23,10 +29,11 @@ def get_SalesOrderQuery(customer_name):
except:
import sys
sys.path.append('.')
sys.path.append('..')
print("except")
print(sys.path)
from SO_to_Inv.readSO import SalesOrderQuery
print("salesorderquery imported")
# print(os.getcwd())
# parentdir = os.path.dirname(os.getcwd())
# print(parentdir)
@ -34,13 +41,40 @@ def get_SalesOrderQuery(customer_name):
basedir = settings.BASE_DIR
parentdir = os.path.dirname(basedir)
ini=SalesOrderQuery(FullName= customer_name, IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'], cwd=parentdir)
if txnid:
ini=SalesOrderQuery(FullName= customer_name, TxnID=txnid, IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'], cwd=parentdir)
pass
else:
ini=SalesOrderQuery(FullName= customer_name, IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'], cwd=parentdir)
return ini
return None
def get_TransactionQuery(txnid=None, refnumber=None, customername=None, txntypefilter=None, includeret=[ 'TxnID', 'EntityRef', 'TxnDate', 'RefNumber', 'Memo'] ):
try:
print("try")
from qbtransactionquery import TransactionQuery
except:
import sys
sys.path.append('.')
sys.path.append('..')
print("except")
print(sys.path)
from qbtransactionquery import TransactionQuery
print("transactionquery imported")
basedir = settings.BASE_DIR
parentdir = os.path.dirname(basedir)
if txnid:
ini=TransactionQuery(FullName= customername, RefNumber = refnumber, TxnID=txnid, IncludeRetElement = includeret, cwd=parentdir)
else:
ini=TransactionQuery(FullName= customername, RefNumber = refnumber, IncludeRetElement =includeret, cwd=parentdir)
return ini
def show_customer(request):
pass
print("show_customer")
starttime = timeit.default_timer()
# thispath = os.getcwd()
# print(thispath)
basedir = settings.BASE_DIR
@ -54,15 +88,110 @@ def show_customer(request):
if request.method =="POST":
customer_name = request.POST.get("customerreffullname")
print(f'customer_name: {customer_name}')
context['objects'] = [['abc', 'def', 'ghi', 'jkl']]
# context['objects'] = [['abc', 'def', 'ghi', 'jkl']]
context['objects'] = []
print("The time difference is bef show customer :", timeit.default_timer() - starttime)
if customer_name:
ini = get_SalesOrderQuery(customer_name)
print("The time difference is mid show customer :", timeit.default_timer() - starttime)
if ini:
print("after ini show customer")
qbxml = ini.create_QBXML()
# print(qbxml)
print("The time difference is :", timeit.default_timer() - starttime)
print("timeeint")
response_string = ini.connect_to_quickbooks(qbxml)
# print(response_string)
open_sales_orders = ini.get_open_so()
print(f'open sales orders showcustomer:{open_sales_orders}')
ini=None
if not open_sales_orders:
messages.info(request, f"There is No open SO for {customer_name}")
return redirect("Invoice:show_customer")
# if open_sales_orders:
context['objects'] = open_sales_orders
context['customer_fullname'] = customer_name
print("The time difference is :", timeit.default_timer() - starttime)
return render(request, "Invoice/so_list_form.html", context)
# print(df.values.tolist())
print("The time difference is :", timeit.default_timer() - starttime)
return render(request, "Invoice/show-customers.html", context)
def select_so(request):
pass
def choose_inv(request):
print("choose_inv")
starttime = timeit.default_timer()
context={}
if request.method == "POST":
print(request.POST)
data = dict(request.POST)
del data['csrfmiddlewaretoken']
del data['customer_fullname']
# if 'other_items' in data:
# del data['other_items']
# del data['other_qty']
# del data['other_rate']
# del data['selected_items']
print(f"datadict:{data}")
try:
df = pd.DataFrame(data)
print(df)
except:
print(Exception)
return HttpResponse("DataFrame Error")
print(f'json:{df.to_json(orient="records")}')
print(df.to_dict("records"))
if ('so_field' in request.POST) and ('customer_fullname' in request.POST):
print(request.POST.getlist('so_field'))
open_sales_orders_TxnID = request.POST.getlist('so_field')
customer_fullname = request.POST.get('customer_fullname')
print(f'Customer_fullname:{customer_fullname} -> request values:{open_sales_orders_TxnID}')
### get the SO detail
ini=get_SalesOrderQuery(customer_fullname, open_sales_orders_TxnID)
# ini=get_SalesOrderQuery(customer_fullname)
print("")
print("The time difference chooseinv is :", timeit.default_timer() - starttime)
print(f'ini::{ini}')
print("The time difference chooseinv is :", timeit.default_timer() - starttime)
print("before ITU")
itu = ini.get_open_sales_order(open_sales_orders_TxnID)
# print(itu)
print("The time difference after itu is :", timeit.default_timer() - starttime)
print(f'get_open_sales_order:{itu}')
if itu:
context['objects'] = itu
context['customer_fullname'] = customer_fullname
# print("Invoiceaddqbxml:")
# invoiceaddQBXML=ini.create_invoiceadd_QBXML()
# print(invoiceaddQBXML)
# print("")
# result=None
# print("")
# result = ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML())
# print("RESULT:")
# print(result)
print("The time difference finish choose inv is :", timeit.default_timer() - starttime)
return render(request, "Invoice/so_details_form.html", context)
else:
messages.warning(request, "Customer PriceLevel or PriceLevelList not found. please prepare data first and check Customer 'Special Customer Custom Field'")
elif 'customer_fullname' in request.POST:
customer_name = request.POST.get('customer_fullname')
ini = get_SalesOrderQuery(customer_name)
if ini:
print("after ini")
qbxml = ini.create_QBXML()
print(qbxml)
# print(qbxml)
response_string = ini.connect_to_quickbooks(qbxml)
print(response_string)
open_sales_orders = ini.get_open_so()
@ -73,39 +202,334 @@ def show_customer(request):
context['customer_fullname'] = customer_name
return render(request, "Invoice/so_list_form.html", context)
# print(df.values.tolist())
print("The time difference is :", timeit.default_timer() - starttime)
return render(request, "Invoice/show-customers.html", context)
return render( request, "Invoice/so_list_form.html", context)
# return HttpResponse('')
def select_so(request):
pass
def unique(list1):
# insert the list to the set
list_set = set(list1)
# convert the set to the list
unique_list = (list(list_set))
return unique_list
def show_inv(request):
print("show_inv")
def check_duplicate_refnumber(refnumber):
ini = get_TransactionQuery(refnumber=refnumber, includeret=['TxnID', 'RefNumber'])
status, statuscode = ini.status_ok(ini.connect_to_quickbooks(ini.create_QBXML()))
print(f'check duplicate refnumber:{status}; {statuscode}')
return status ### status=True -> duplicate refnumber
def save_inv(request):
print("save_inv")
starttime = timeit.default_timer()
context={}
if request.method == "POST":
print(request.POST)
# print(request.POST)
data = dict(request.POST)
del data['csrfmiddlewaretoken']
del data['customer_fullname']
### get others items into list of dict ###
others=[]
otherFullName = {'peti': 'PETI', 'expedition':'Sales_Ekspedisi'}
ref_number = request.POST.get('ref_number', None)
txn_date = request.POST.get('date', None)
print(ref_number, txn_date)
if len(ref_number)>20:
return HttpResponse("RefNumber Invalid. ")
elif ref_number.strip()=="":
ref_number = None
if ref_number and check_duplicate_refnumber(ref_number):
messages.warning(request, 'RefNumber already Exist. please choose other RefNumber')
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
return HttpResponse('RefNumber already Exist. please choose other RefNumber')
if len(txn_date)<6:
return HttpResponse("Date is Invalid")
del data['ref_number']
del data['date']
if 'selected_items' in data:
for _ in data['selected_items']:
if _ in data['other_items']:
data_idx = data['other_items'].index(_)
print(f'selected item:{_} is in index:{data_idx}')
other_qty = int(data['other_qty'][data_idx])
other_rate = float(data['other_rate'][data_idx])
if other_qty<=0:
other_qty = 1
if other_rate <= 0:
continue
temp_ = {'other_item':data['other_items'][data_idx], 'other_itemFullName': otherFullName[data['other_items'][data_idx]], 'other_qty':other_qty, 'other_rate': other_rate}
others.append(temp_)
del data['selected_items']
if 'other_items' in data:
del data['other_items']
if 'other_qty' in data:
del data['other_qty']
if 'other_rate' in data:
del data['other_rate']
print(f'others:{others}')
print(f"datadict:{data}")
required_data = ['RefNumber', 'CustomerFullName', 'TxnID', 'TxnDate', 'TotalAmount', 'TxnLineID', 'ItemFullName',
'Quantity', 'UOM', 'Rate', 'Amount', 'Invoiced', 'LineIsManuallyClosed', 'backordered', 'rate', 'convertQTY']
if 'so_field' not in data:
return HttpResponse("no Selected Items. Please select at least 1 item to be invoiced.")
for _ in required_data:
if _ not in data:
return HttpResponse(f"Required data not returned from the form: {_}")
### retrieve only the selected so_field ###
so_field = data['so_field']
del data['so_field']
selectedItems = []
for _ in so_field:
if _ not in data['TxnLineID']:
return HttpResponse(f"One of the TxnLineID is not valid: {_}")
data_idx = data['TxnLineID'].index(_)
try:
df = pd.DataFrame(data)
print(df)
df = df[df['TxnLineID'].isin(so_field)].reset_index()
print(df)
except:
print(Exception)
return HttpResponse("DataFrame Error")
# print(f'json:{df.to_json(orient="records")}')
web_dict = df.to_dict("records")
# print(f'web_dict:{web_dict}' )
web_dict = sorted(web_dict, key=lambda x: x['TxnID'])
# print(f'Sorted web_dict:{web_dict}' )
if ('so_field' in request.POST) and ('customer_fullname' in request.POST):
# print(request.POST.getlist('so_field'))
open_sales_orders = request.POST.getlist('so_field')
print(request.POST.getlist('so_field'))
# open_sales_orders_TxnID = unique(request.POST.getlist('so_field'))
open_sales_orders_TxnID = unique(df['TxnID'].to_list())
customer_fullname = request.POST.get('customer_fullname')
print(f'Customer_fullname:{customer_fullname} -> request values:{open_sales_orders}')
print(f'Customer_fullname:{customer_fullname} -> request values:{open_sales_orders_TxnID}')
### get the SO detail
ini=get_SalesOrderQuery(customer_fullname)
itu = ini.get_open_sales_order(open_sales_orders)
print(f'ini::{ini}')
print("before ITU")
itu = ini.get_open_sales_order(open_sales_orders_TxnID)
# print(itu)
itu = sorted(itu, key=lambda x: x['TxnID'])
print(f'get_open_sales_order:{itu}')
if itu:
invoiceaddQBXML=ini.create_invoiceadd_QBXML()
### do the checking web_dict and openSO ###
data_to_save = []
dict_ = {}
txnids = []
disc_amount = 0
for web in web_dict:
for tu_ in itu:
# print(tu_)
if web['TxnID'] == tu_['TxnID'] and web['TxnDate'] == tu_['TxnDate'] and web['RefNumber'] == tu_['RefNumber']:
if web['TxnID'] not in txnids: # new txn
if dict_:
dict_['Disc_Amount']=disc_amount
data_to_save.append(dict_)
dict_ = {'RefNumber': web['RefNumber'], 'CustomerFullName': tu_['CustomerFullName'], 'TxnID': web['TxnID'],
'TxnDate': web['TxnDate'], 'TxnNumber': tu_['TxnNumber'],
'TotalAmount': '0.00', 'IsFullyInvoiced': tu_['IsFullyInvoiced'], 'IsManuallyClosed': tu_['IsManuallyClosed'],
'SalesOrderLineRet': [] }
disc_amount = 0
txnids.append(web['TxnID'])
txnids = unique(txnids) ### not usefull
for tu_line_ret in tu_['SalesOrderLineRet']:
# print(tu_line_ret)
if web['TxnLineID']==tu_line_ret['TxnLineID'] and web['ItemFullName']==tu_line_ret['ItemFullName']:
### modified back the rate and backordered using convertQTY ###
convertQTY = int(tu_line_ret['convertQTY'])
webbackordered = Decimal(web['backordered']) * convertQTY
webrate = Decimal(web['rate']) / convertQTY
###
if 0 < webbackordered <= Decimal(tu_line_ret['BackOrdered']):
# print('put in list')
discPerPcs=0
discPerItem=0
if webrate == Decimal(tu_line_ret['Rate']):
discPerPcs = ini.get_discperpcs(web['ItemFullName'], webrate)
elif webrate < Decimal(tu_line_ret['Rate']):
discPerPcs = Decimal(tu_line_ret['Rate']) - webrate
discPerItem = webbackordered * discPerPcs
disc_amount += discPerItem
SalesOrderLinedict = {'TxnLineID':web['TxnLineID'],
'ItemFullName':tu_line_ret['ItemFullName'],
'Quantity':tu_line_ret['Quantity'],
'UOM':tu_line_ret['UOM'],
'Rate':webrate,
'Amount':Decimal(tu_line_ret['Amount']),
'BackOrdered':webbackordered,
'Invoiced':tu_line_ret['Invoiced'],
'LineIsManuallyClosed':tu_line_ret['LineIsManuallyClosed'],
'discPerItem':discPerItem, # backorder qty * disc per pcs
'discPerPcs':discPerPcs,
'convertQTY':tu_line_ret['convertQTY']
}
# print(f'salesorderlineddict:{SalesOrderLinedict}')
_salesorderlineret = dict_['SalesOrderLineRet']
_salesorderlineret.append(SalesOrderLinedict)
dict_['SalesOrderLineRet'] = _salesorderlineret
break
break
print(f'last:{dict_}')
print(f'datatosaveonly:{data_to_save}')
if not data_to_save:
dict_['Disc_Amount']=disc_amount
data_to_save.append(dict_)
elif dict_ != data_to_save[-1]: #append the last dict_
dict_['Disc_Amount']=disc_amount
data_to_save.append(dict_)
print('save the last dict')
else:
print('last data to save == dict')
for _ in others:
data_to_save.append(_)
dict_to_save = {'customer_fullname': customer_fullname, 'ref_number':ref_number, 'txn_date':txn_date, 'data':data_to_save }
print(f'Final List:{dict_to_save}')
print("")
# print(itu)
context['objects'] = itu
context['customer_fullname'] = customer_fullname
# print(context['objects'])
print("Invoiceaddqbxml:")
invoiceaddQBXML=ini.create_invoiceadd_QBXML(dict_to_save)
print(invoiceaddQBXML)
result=None
result = ini.connect_to_quickbooks(invoiceaddQBXML)
print("RESULT:")
print(result)
rst, status_msg = ini.status_ok(result)
if rst:
saved_inv = ini.get_saved_refnumber(result)
print(saved_inv)
# context['messages']=[{"alert":"info", "message": "invoice Is Good"}]
balance_remaining = "{:,}".format(float(saved_inv['BalanceRemaining']))
date_format = '%Y-%m-%d'
date_obj = datetime.strptime(saved_inv['TxnDate'], date_format).strftime('%d %b %Y')
messages.success(request, f"Customer : {saved_inv['Customer_FullName']}<br>Invoice No : {saved_inv['RefNumber']} &emsp; Date : {date_obj}<br>Invoice Amount : Rp. {balance_remaining}<br> Is SAVED with link to SO No. : {unique(df['RefNumber'].to_list())}<br>it takes {round(timeit.default_timer() - starttime, 4)} secs")
# messages.info(request, 'Invoice Has Been SAVED2')
else:
messages.warning(request, f"Error saving SO No. {unique(df['RefNumber'].to_list())}<br>Status: {status_msg}")
print("The time difference finish Save Inv is :", timeit.default_timer() - starttime)
return redirect('Invoice:show_customer')
return render(request, "Invoice/show-customers.html", context)
return render(request, "Invoice/so_details_form.html", context)
else:
return HttpResponse(f"You cannot Save, because There Is No Open Sales Order for Customer: {customer_fullname}")
elif 'customer_fullname' in request.POST:
customer_name = request.POST.get('customer_fullname')
ini = get_SalesOrderQuery(customer_name)
if ini:
print("after ini")
qbxml = ini.create_QBXML()
# print(qbxml)
response_string = ini.connect_to_quickbooks(qbxml)
print(response_string)
open_sales_orders = ini.get_open_so()
print(f'open sales orders:{open_sales_orders}')
ini=None
if open_sales_orders:
context['objects'] = open_sales_orders
context['customer_fullname'] = customer_name
return render( request, "Invoice/so_list_form.html", context)
# return HttpResponse('')
def prepare_data(request):
starttime = timeit.default_timer()
import sys
sys.path.append('.')
sys.path.append('..')
context = {}
FullName = ""
if request.method=='POST':
print(request.POST)
subject = request.POST.get("subject", None)
print(subject, type(subject))
if subject and isinstance(subject, str):
ret=False
# import sys
# sys.path.append('.')
# sys.path.append('..')
if subject.upper() == "ITEM INVENTORY":
# try:
print("try iteminventory import")
from ItemInventoryQuery import ItemInventoryQuery
ini= ItemInventoryQuery( OwnerID ="0")
itu = ini.connect_to_quickbooks(ini.create_QBXML())
ret, msg = ini.status_ok(itu)
if ret:
print("YEAH")
df = pd.DataFrame.from_dict(ini.get_data(itu))
print(df)
df.to_excel('ItemInventory\ItemInventory_FromQB.xlsx', index=False)
modtime = datetime.fromtimestamp(os.path.getmtime('ItemInventory\ItemInventory_FromQB.xlsx'))
print(f"modified Time:{modtime}")
# print(ini.status_ok(itu))
else:
messages.warning(request, f"Saving {subject} NOT success: {msg}")
elif subject.upper() == "CUSTOMER LIST":
# try:
print("try customerlist import")
from SO_to_Inv.CustomerQuery import CustomerQuery
ini=CustomerQuery()
itu = ini.create_customerquery_QBXML()
# print(f'itu:{itu}')
print(f'get customer pricelevel list:{ini.get_customer_pricelevel_list(itu)}')
ret=True
elif subject.upper() == "PRICE LEVEL LIST":
# try:
print("try pricelevelquery import")
from SO_to_Inv.PriceLevelQuery import PriceLevelQuery
FullName = ['t 202202', 'M 202202', 'b 202202', 'sp 202202']
FullName = request.POST.getlist("selected_items", FullName)
# print(f'fullname:{FullName}')
ini = PriceLevelQuery(FullName = FullName, )
itu = ini.connect_to_quickbooks(ini.create_QBXML())
ret, msg = ini.get_pricelevel()
ini= None
itu = None
if ret:
print(f'Success Save Price Level : {FullName}')
else:
messages.warning(request, f"Saving Not Success. status: {msg}")
print(f"Saving Not Success. status: {msg}")
if ret:
# messages.info(request, "<p> You are <em>pretty</em><br> smart</p>")
messages.info(request, f"{subject} has been updated Successfully in {round(timeit.default_timer() - starttime, 4)} seconds. {FullName}")
else:
messages.error(request, "not valid choice. Please choose again")
# else:
from SO_to_Inv.PriceLevelQuery import PriceLevelQuery
ini = PriceLevelQuery(IncludeRetElement = ['Name'])
price_level_list = ini.name_list()
# print(price_level_list)
context['objects']=price_level_list
print("The time difference is :", timeit.default_timer() - starttime)
return render(request, "Invoice/prepare_data.html", context=context)
def index(request):
print("index Inv")
context = {}

View File

@ -2,6 +2,9 @@
"""Django's command-line utility for administrative tasks."""
import os
import sys
# from icecream import install
# install()
def main():

View File

@ -1,15 +1,16 @@
<!DOCTYPE html>
{% load static %}
{% load tz %}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% comment %} <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script> {% endcomment %}
<link href= {% static 'bootstrap.min.css' %} rel="stylesheet" crossorigin="anonymous">
<script src= {% static 'bootstrap.bundle.min.js' %} ></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
<!-- <link href= {% static 'bootstrap.min.css' %} rel="stylesheet" crossorigin="anonymous">
<script src= {% static 'bootstrap.bundle.min.js' %} ></script> -->
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
<style type="text/css">
@ -47,9 +48,9 @@
{% block head %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-expand-lg bg-light">
<nav class="navbar navbar-expand-lg border-bottom bg-body-tertiary" data-bs-theme='light' >
<div class="container-fluid">
<a class="navbar-brand" href="#">DBW</a>
<a class="navbar-brand" href="{% url 'Invoice:show_customer' %}">DBW</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarScroll" aria-controls="navbarScroll" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
@ -71,20 +72,44 @@
<li><a class="dropdown-item" href="{% url 'Invoice:index' %}">Invoice List</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
<li><a class="dropdown-item" href="{% url 'Invoice:prepare_data' %}">Prepare Data</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled">Link</a>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
SO to Inv
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'Invoice:show_customer' %}">SO to Inv</a></li>
<li><a class="dropdown-item" href="{% url 'Invoice:prepare_data' %}">Prepare Data</a></li>
</ul>
</li>
<!-- <li class="nav-item">
<a class="nav-link disabled">Link</a>
</li> -->
</ul>
<div>{% block search_nav %}{% endblock search_nav %}
<div>{% block search_nav %}<form class="d-flex" role="search">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
{% endblock search_nav %}
</div>
</div>
</div>
</nav>
{% block body %}{% endblock body %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{message | safe}}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<div>
{% block body %}{% endblock body %}
</div>
<!-- Modal -->

12
main.py
View File

@ -1,7 +1,11 @@
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi import FastAPI, Request, UploadFile, Body
# from icecream import install
# import icecream
# install()
# from icecream import ic
# ic.configureOutput(includeContext=True, )
import json
from iteminventorydasa import QBStock
import datetime
@ -13,7 +17,7 @@ import pdfexcel4DNwithxlrd
from ItemInventoryQuery import ItemInventoryQuery
from SO_to_Inv import readSO
import os
from icecream import ic
import pprint
# app = FastAPI()
@ -56,6 +60,7 @@ async def renew_iteminventory():
@app.post("/upload-file/")
async def create_upload_file(uploaded_file: UploadFile):
base_file_location = f"DN_Excel_files"
status = "ERROR"
if uploaded_file.filename.endswith(".xls") and uploaded_file.filename.startswith("TCO-DN"):
folder_yearmonth = uploaded_file.filename.split('-')
if len(folder_yearmonth)>3:
@ -123,7 +128,7 @@ async def create_upload_file(uploaded_file: UploadFile):
return [{"info": f"file '{uploaded_file.filename}' saved at '{file_location}'", "status": status, "msg": msg}]
return {"info": f"file '{uploaded_file.filename}' is not Excel(.xls) file or it is not Delivery Note(TCO-DN)file. please upload the correct file", "status": status}
return {"info": f"file '{uploaded_file.filename}' is not Excel(.xls) file or Filename is not start with 'TCO-DN'. Please upload the correct file", "status": status}
@app.post('/getopenso')
async def getopenso(request: Request, CustomerName: str = Body(...)):
@ -134,6 +139,7 @@ async def getopenso(request: Request, CustomerName: str = Body(...)):
FullName = CustomerName['CustomerName']
print(FullName)
ini=readSO.SalesOrderQuery(FullName= FullName, IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
print("selesai salesorder")
open_sales_orders = ini.get_open_so()
print(f'return opensalesorder:{open_sales_orders}')
return open_sales_orders

View File

@ -84,9 +84,25 @@ def read_DN_excel(filename):
# DNRefNum = rawdata[1][3].strip().split("-")[1][-1] + "".join(rawdata[1][3].strip().split("-")[-2:])
# TxnDate = rawdata[2][3].strip().split(" ")[-1]
DNRefNum = wb.sheets()[0].cell(1, 3).value.strip().split("-")[1][-1] + "".join(wb.sheets()[0].cell(1, 3).value.strip().split("-")[-2:])
_y=1
_x=0
for col in range(1,wb.sheets()[0].ncols):
col_value = wb.sheets()[0].col_values(col)
print("col=",col, col_value[1])
if "TCO-DN" in col_value[1]:
_x=col
if _x == 0 :
return False, "Cannot find TCO-DN cell"
# if "TCO-DN" in wb.sheets()[0].cell(_y, _x).value.strip():
print(wb.sheets()[0].cell(_y, _x).value.strip())
DNRefNum = wb.sheets()[0].cell(_y, _x).value.strip().split("-")[1][-1] + "".join(wb.sheets()[0].cell(_y, _x).value.strip().split("-")[-2:])
# print(DNRefNum)
TxnDate = wb.sheets()[0].cell(2, 3).value.strip().split(" ")[-1]
TxnDate = wb.sheets()[0].cell(_y+1, _x).value.strip().split(" ")[-1]
Memo = DNRefNum
print(f'pdfexcel4DNwithxlrd.py->DNRefNum:{DNRefNum}, TxnDate:{TxnDate}')
DeliveryNotedict={'DNRefNum':DNRefNum, 'TxnDate':TxnDate, 'Memo': Memo}
@ -108,7 +124,19 @@ def read_DN_excel(filename):
if not None and row[0]=="Item No" :
if firstpage:
# print('firstpage')
data.append(row)
if len(row)==5:
data.append(row)
elif len(row)>5:
temp_ =[]
for _ in row:
if _ != None:
temp_.append(_)
if len(row)==5:
data.append(temp_)
else:
print("HEADER ERROR!!!")
data.append(['Item No', 'Description', 'QuantityUOM', 'No.SO', 'LPN No.'])
boldataline=True
firstpage=False
# continue
@ -130,7 +158,8 @@ def read_DN_excel(filename):
movetonextpage=True
continue
elif row[3] is not None:
if row[3].startswith('Hormat'):
# if row[3].startswith('Hormat'):
if 'Hormat Kami,' in row:
# print('hormatkami found')
boldataline=False
movetonextpage=True
@ -140,7 +169,8 @@ def read_DN_excel(filename):
else:
data.append(row)
elif row[3] is not None:
if row[3].startswith('Hormat'):
# if row[3].startswith('Hormat'):
if 'Hormat Kami,' in row:
boldataline=False
movetonextpage=True
# continue
@ -149,28 +179,67 @@ def read_DN_excel(filename):
else:
data.append(row)
# print (f'data: {data}')
print (f'data: {data}')
for idx, x in enumerate(data):
if x[0].upper() == "ET-06/A1.": ### Change the source from "ET-06/A1." to "ET-06/A1.BOX_100" and replace the UOM from "BOX_100" to "BOX"
x[0] = "ET-06/A1.BOX_100"
x[2] = x[2].replace("BOX_100", "BOX")
for colidx, col in enumerate(x): ### change the empty cell into None
if col == "":
data[idx][colidx]=None
# for idx, x in enumerate(data):
# print(idx, x)
coly=0
lenList = 0
xylist=[]
for idx, x in enumerate(data):
print(idx, x)
xylist=[]
if len(x)>5 and (idx % 2)==1 :
print(idx,x)
lenList = len(x)
for idy, y in enumerate(x):
if y == None:
coly=idy
else:
xylist.append(y)
if len(xylist)==5:
data[idx]=xylist
elif len(x)>5 and (idx % 2)==0 and coly==0:
pass
print("Different page, seconde line have different column width (6)")
boloddcolumn=True
for _ in x[:len(x)-2]:
print(_)
if _ != None:
boloddcolumn=False
print(boloddcolumn)
if boloddcolumn:
del x[0]
print(x)
elif coly != 0 and lenList == len(x):
for idy, y in enumerate(x):
if idy!=coly:
xylist.append(y)
data[idx]=xylist
coly=0
lenList = 0
print(idx, data[idx])
print(f'len data={len(data)}')
for idx, x in enumerate(data):
# print(idx, x)
if x[0] !=None and x[0].upper() == "ET-06/A1.": ### Change the source from "ET-06/A1." to "ET-06/A1.BOX_100" and replace the UOM from "BOX_100" to "BOX"
print(idx, x)
x[0] = "ET-06/A1.BOX_100"
x[2] = x[2].replace("BOX_100", "BOX")
print(idx, x)
newdata=[]
templist=[]
for idx, dt in enumerate(data):
if dt[0] != 'Item No':
# print(idx,'not item')
# print(idx,'not item', dt)
if dt[0] is None:
if dt[1]:
templist[1]+=" " + dt[1]
@ -191,33 +260,35 @@ def read_DN_excel(filename):
templist=dt
# print(templist)
# for idx, x in enumerate(newdata):
# print(idx, x)
# print(f'len newdata={len(newdata)}')
for idx, x in enumerate(newdata):
pass
print(idx, x)
print(f'len newdata={len(newdata)}')
df=pd.DataFrame(newdata, columns=['Item No', 'Description', 'Quantity', 'No.SO/Ext.Doc.No.', 'LPN No.', 'UOM', 'Ext.Doc.No.'])#, columns=data[0]+"UOM")
df=pd.DataFrame(newdata, columns=['Item No', 'Description', 'Quantity', 'No.SO', 'LPN No.', 'UOM', 'Ext.Doc.No'])#, columns=data[0]+"UOM")
# print(df)
# df=df.groupby(['No.SO/Ext.Doc.No.','Item No', 'UOM'])['Quantity'].sum().reset_index().sort_values(by=['Item No', 'No.SO/Ext.Doc.No.'])#.sort_values(by=['No.SO/Ext.Doc.No.'])
df=df.groupby(['Ext.Doc.No.', 'No.SO/Ext.Doc.No.','Item No', 'UOM'])['Quantity'].sum().reset_index().sort_values(by=['Ext.Doc.No.','No.SO/Ext.Doc.No.', 'Item No'])
# df=df.groupby(['No.SO','Item No', 'UOM'])['Quantity'].sum().reset_index().sort_values(by=['Item No', 'No.SO'])#.sort_values(by=['No.SO'])
df=df.groupby(['Ext.Doc.No', 'No.SO','Item No', 'UOM'])['Quantity'].sum().reset_index().sort_values(by=['Ext.Doc.No','No.SO', 'Item No'])
df['NameFromTaco']=df['Item No']
# print(df)
# df['FullName'] = inteminvdf.loc[df['Item No'],'FullName']
df=df.merge(inteminvdf, how="left")
print(df['FullName'].isnull())
print(df['FullName'])
# print(df)
if df['FullName'].isnull().sum() > 0:
print("Cannot Find Item FullName")
listitemNoFullName = df.loc[df['FullName'].isnull()].values.tolist()
df=df.reindex(columns=['Ext.Doc.No.', 'No.SO/Ext.Doc.No.', 'Item No', 'FullName', 'Quantity', 'UOM'])
print(df)
print(listitemNoFullName)
df=df.reindex(columns=['Ext.Doc.No', 'No.SO', 'Item No', 'FullName', 'Quantity', 'UOM'])
# print(df)
# print(listitemNoFullName)
# print(listitemNoFullName)
return False, listitemNoFullName
else:
df=df.groupby(['Ext.Doc.No.', 'No.SO/Ext.Doc.No.','Item No', 'UOM', 'FullName'])['Quantity'].sum().reset_index().sort_values(by=['Ext.Doc.No.','No.SO/Ext.Doc.No.', 'Item No'])
df=df.reindex(columns=['Ext.Doc.No.', 'No.SO/Ext.Doc.No.', 'Item No', 'FullName', 'Quantity', 'UOM'])
df=df.groupby(['Ext.Doc.No', 'No.SO','Item No', 'UOM', 'FullName'])['Quantity'].sum().reset_index().sort_values(by=['Ext.Doc.No','No.SO', 'Item No'])
df=df.reindex(columns=['Ext.Doc.No', 'No.SO', 'Item No', 'FullName', 'Quantity', 'UOM'])
# print(df)
lst = df.to_dict('records')
# print(lst)
@ -232,5 +303,8 @@ if __name__=="__main__":
# read_DN_excel(filename)
# filename = "DN_Excel_files\TCO-DNPR-2305-00305.xls"
# read_DN_excel(filename)
filename = "DN_Excel_files\TCO-DNPG-2307-01407.xls"
read_DN_excel(filename)
# filename = "DN_Excel_files\TCO-DNPG-2307-01407.xls"
filename = "DN_Excel_files\TCO-DNPG-2310-00337.xls"
status, retdict = read_DN_excel(filename)
print(status)
print(retdict)

File diff suppressed because one or more lines are too long

View File

@ -58,7 +58,7 @@ class SalesOrderQuery:
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(qbxml_query)
return qbxml_query
def connect_to_quickbooks(self, qbxml_query):
@ -83,7 +83,7 @@ class SalesOrderQuery:
def __str__(self, *args) -> str:
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
# print("__str__")
print("__str__")
return str(self.get_datarow())
# return "hello"

View File

@ -6,13 +6,18 @@ import datetime
import pandas as pd
import xml.dom.minidom
from lxml import etree
from tools import pprintXml
import timeit
class TransactionQuery:
def __init__(self, **kwargs) -> None:
# print(f'kwargs:{kwargs}')
# print(args)
# self.TransactionTypeFilter = kwargs['TransactionTypeFilter'] if 'TransactionTypeFilter' in kwargs else 'SalesOrder'
self.TxnTypeFilter = kwargs['TxnTypeFilter'] if 'TxnTypeFilter' in kwargs else 'SalesOrder'
self.EntityTypeFilter = kwargs['EntityTypeFilter'] if 'EntityTypeFilter' in kwargs else None
self.RefNumber = kwargs['RefNumber'] if 'RefNumber' in kwargs else None
self.FullName = kwargs['FullName'] if 'FullName' in kwargs else None
self.TxnTypeFilter = kwargs['TxnTypeFilter'] if 'TxnTypeFilter' in kwargs else None
# self.TransactionPaidStatusFilter = kwargs['TransactionPaidStatusFilter'] if 'TransactionPaidStatusFilter' in kwargs else 'Open'
self.TransactionPaidStatusFilter = kwargs['TransactionPaidStatusFilter'] if 'TransactionPaidStatusFilter' in kwargs else 'Open'
self.TransactionDetailLevelFilter = kwargs['TransactionDetailLevelFilter'] if 'TransactionDetailLevelFilter' in kwargs else None
@ -26,7 +31,7 @@ class TransactionQuery:
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
self.FullName = kwargs['FullName'] if 'FullName' in kwargs else None
# print(self.DateMacro, self.ReportPeriod, self.FromTxnDate, self.ToTxnDate)
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
@ -53,6 +58,13 @@ class TransactionQuery:
QBXMLMsgsRq.text = "\n "
# TransactionQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "TransactionQueryRq","\n", attrib={"metaData":"MetaDataAndResponseData", "iteratorID":"UUIDTYPE" })
TransactionQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "TransactionQueryRq","\n")
if self.RefNumber:
if isinstance(self.RefNumber, list):
for _ in self.RefNumber:
RefNumber = self.create_sub_element(ET, TransactionQueryRq, "RefNumber", _)
else:
RefNumber = self.create_sub_element(ET, TransactionQueryRq, "RefNumber", self.RefNumber)
if self.DateMacro:
TransactionDateRangeFilter = self.create_sub_element(ET, TransactionQueryRq, "TransactionDateRangeFilter","\n")
# ReportPeriod = self.create_sub_element(ET, TransactionQueryRq, "TransactionDateRangeFilter", "\n ",)
@ -65,7 +77,12 @@ class TransactionQuery:
if type(self.ToTxnDate) is datetime.date:
ToTxnDate = self.create_sub_element(ET, TransactionDateRangeFilter, "ToTxnDate", self.ToTxnDate.strftime('%Y-%m-%d'))
if self.EntityTypeFilter or self.FullName:
TransactionEntityFilter = self.create_sub_element(ET, TransactionQueryRq, "TransactionEntityFilter","\n")
if self.EntityTypeFilter:
EntityTypeFilter = self.create_sub_element(ET, TransactionEntityFilter, "EntityTypeFilter", self.EntityTypeFilter, )
if self.FullName:
FullName = self.create_sub_element(ET, TransactionEntityFilter, "FullName", self.FullName,)
# TransactionQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "TransactionQueryRq","\n " )
if self.TxnTypeFilter:
TransactionTypeFilter = self.create_sub_element(ET, TransactionQueryRq, 'TransactionTypeFilter', "\n")
@ -78,13 +95,13 @@ class TransactionQuery:
IncludeRetElement = self.create_sub_element(ET, TransactionQueryRq, 'IncludeRetElement', ire)
mydata = ET.tostring(root, encoding="unicode")
# mydata = ET.tostring(root, encoding = "unicode")
print(mydata)
# print(mydata)
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
qbxml_query = qbxml_query + "\n" + mydata
# def to_string():
# qbxml_query=str(qbxml_query)
return qbxml_query
return pprintXml(qbxml_query)
def connect_to_quickbooks(self, qbxml_query):
# Connect to Quickbooks
@ -95,7 +112,8 @@ class TransactionQuery:
# print(enumfodnc.qbFileOpenDoNotCare)
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
sessionManager.OpenConnection('', 'DASA2')
ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
ticket = sessionManager.BeginSession("", 2)
# Send query and receive response
response_string = sessionManager.ProcessRequest(ticket, qbxml_query)
@ -103,8 +121,9 @@ class TransactionQuery:
# Disconnect from Quickbooks
sessionManager.EndSession(ticket) # Close the company file
sessionManager.CloseConnection() # Close the connection
# print (response_string)
return response_string
# print(f"response: {response_string}")
# print (pprintXml(response_string))
return pprintXml(response_string)
def __str__(self, *args) -> str:
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
@ -123,7 +142,12 @@ class TransactionQuery:
return self._get_total(self.connect_to_quickbooks(self.create_QBXML()))
def status_ok(self, QBXML):
GSRQRs=QBXML.find('.//TransactionQueryRs')
print(QBXML)
tree = ET.fromstring(QBXML)
GSRQRs=tree.find('.//TransactionQueryRs')
print(f"GSRQRs:{GSRQRs}")
status_code = GSRQRs.attrib #.get('statusCode')
# print(GSRQRs.attrib)
# print(GSRQRs.attrib['statusCode'])
@ -252,14 +276,26 @@ class TransactionQuery:
# raise ValueError("Incorrect data format, should be YYYY-MM-DD")
print('### Transaction ###')
if __name__ == '__main__':
starttime = timeit.default_timer()
# ini=TransactionQuery(DateMacro='ThisMonth')
# ini=TransactionQuery()
# temp = xml.dom.minidom.parseString(ini.create_QBXML())
# print(temp.toprettyxml())
print("hello")
# ini=TransactionQuery(FromTxnDate='2023-01-11', ToTxnDate='2023-01-12', TransactionPaidStatusFilter='Closed', TransactionDetailLevelFilter='All')
ini=TransactionQuery(FromTxnDate='2023-01-11', ToTxnDate='2023-01-12', TransactionPaidStatusFilter='Closed', TransactionDetailLevelFilter=None,
IncludeRetElement=['EntityRef', 'TxnDate', 'Amount', 'Memo'])
# ini=TransactionQuery(FromTxnDate='2023-01-11', ToTxnDate='2023-01-12', TransactionPaidStatusFilter='Closed', TransactionDetailLevelFilter=None,
# IncludeRetElement=['EntityRef', 'TxnDate', 'Amount', 'Memo'])
ini=TransactionQuery( TransactionPaidStatusFilter='Open', TransactionDetailLevelFilter=None,
# FullName='Abadi Serpong',
# EntityTypeFilter='Customer',
# RefNumber = ['24010005', '24010001', '24010002'],
RefNumber = ['24010002'],
TxnTypeFilter = None,
# IncludeRetElement=['EntityRef', 'TxnDate', 'Amount', 'Memo', 'RefNumber', 'TxnID', 'TimeCreated', 'TimeModified'])
IncludeRetElement=[ 'TxnID', 'EntityRef', 'TxnDate', 'RefNumber', 'Memo'])
# ini=TransactionQuery(TransactionTypeFilter='SalesByItemSummary')
# ini=TransactionQuery(TransactionTypeFilter='SalesByRepSummary')
# ini=TransactionQuery(TransactionTypeFilter='PurchaseByVendorSummary')
@ -268,7 +304,8 @@ if __name__ == '__main__':
# ini=TransactionQuery(TransactionTypeFilter='InventoryStockStatusByItem')
print(type(ini.create_QBXML()))
print(ini.create_QBXML())
print(f'print ini:{ini}')
print(f"ini result connect:{ini.connect_to_quickbooks(ini.create_QBXML())}")
# print(f'print ini:{ini}')
# print(type(ini.get_datarow()))
# print(ini.get_total())
# print(f'ini.getdatarow:{ini.get_datarow()}')
@ -283,4 +320,6 @@ if __name__ == '__main__':
# print(df.loc[df['TotalSales']>0])
# print(df.tail(10))
# print(headers)
# print(list(df.keys().values))
# print(list(df.keys().values))
print("The time difference is :", timeit.default_timer() - starttime)

1
records Normal file
View File

@ -0,0 +1 @@
{"itemfullname":{"0":"TACO:G_D:TH-017D","1":"TEDG:S122:EDG-009-1\/22","2":"TIERO:AF:TI-Q-9002-AF"},"txnlineid":{"0":"1786D7-1689047665","1":"178970-1689047665","2":"1796BD-1689144572"},"so_field":{"0":"1786D5-1689047665, 1786D7-1689047665","1":"1786D5-1689047665, 178970-1689047665","2":"1796BB-1689144572, 1796BD-1689144572"},"backordered":{"0":"11","1":"1.0","2":"8.0"},"rate":{"0":"179500.0","1":"380000.0","2":"1080000.0"}}

View File

@ -7,5 +7,18 @@ def get_alpha_numeric(text:str):
result += x
return result
def pprintXml(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
if __name__ == "__main__":
print(get_alpha_numeric('TH-201/88. aa, BH'))