Compare commits

...

2 Commits

Author SHA1 Message Date
c8c7816f01 update exim 2025-02-21 06:08:34 +07:00
89982de5b0 exporting is good 2025-02-21 03:50:45 +07:00
4 changed files with 222 additions and 980815 deletions

3
.gitignore vendored
View File

@ -169,4 +169,5 @@ QBbackup/
.xlsx .xlsx
.pdf .pdf
QBbackup/ QBbackup/
test_folder_source_DeliveryNote/ test_folder_source_DeliveryNote/
Exim/Data

268
exim.py
View File

@ -6,6 +6,8 @@ from QBClass.QBClasses import InvoiceQuery, SalesOrderQuery
import time import time
import json import json
import pandas as pd import pandas as pd
import numpy as np
import datetime
print('succes Loading modules') print('succes Loading modules')
@ -26,7 +28,7 @@ def timer(func):
@timer @timer
def get_all_so_from_invoice(MaxReturned=None, FromTxnDate=None, ToTxnDate=None): def get_all_so_from_invoice( FromTxnDate=None, ToTxnDate=None, MaxReturned=None,):
print(MaxReturned, FromTxnDate, ToTxnDate) print(MaxReturned, FromTxnDate, ToTxnDate)
start = time.time() start = time.time()
print('Get Invoice Query List. Processing..... wait for at minute(1 month=90secs)') print('Get Invoice Query List. Processing..... wait for at minute(1 month=90secs)')
@ -160,6 +162,7 @@ def process():
@timer @timer
def process_data(iq_list, so_dict): def process_data(iq_list, so_dict):
print('process_data')
# iq = InvoiceQuery(MaxReturned= 20, IncludeLinkedTxns='true', IncludeLineItems='true', debug=False) # iq = InvoiceQuery(MaxReturned= 20, IncludeLinkedTxns='true', IncludeLineItems='true', debug=False)
# iq = InvoiceQuery(TxnDateRangeFilter_FromTxnDate='2024-02-01', TxnDateRangeFilter_ToTxnDate='2024-02-29', IncludeLinkedTxns='true', IncludeLineItems='true') # iq = InvoiceQuery(TxnDateRangeFilter_FromTxnDate='2024-02-01', TxnDateRangeFilter_ToTxnDate='2024-02-29', IncludeLinkedTxns='true', IncludeLineItems='true')
# pprint(iq_list, sort_dicts=False) # pprint(iq_list, sort_dicts=False)
@ -171,6 +174,7 @@ def process_data(iq_list, so_dict):
fullyInvoicedSO = [] fullyInvoicedSO = []
so_list = [x for x in so_dict] so_list = [x for x in so_dict]
print(f'{len(so_list) = }') print(f'{len(so_list) = }')
result_iq_list = []
for idx, txn in enumerate(iq_list): for idx, txn in enumerate(iq_list):
# print(f"{idx = } {txn['RefNumber'] = } {txn['TxnDate'] = } {txn['Subtotal'] = } ") # print(f"{idx = } {txn['RefNumber'] = } {txn['TxnDate'] = } {txn['Subtotal'] = } ")
# print(f"{txn['Subtotal'] = }") # print(f"{txn['Subtotal'] = }")
@ -183,8 +187,9 @@ def process_data(iq_list, so_dict):
txn_linkedTxn = txn['LinkedTxn'] txn_linkedTxn = txn['LinkedTxn']
for linkedtxn in txn_linkedTxn: for linkedtxn in txn_linkedTxn:
try: # try:
if linkedtxn['TxnType']=='SalesOrder': if linkedtxn['TxnType']=='SalesOrder':
try:
# so_list.remove(linkedtxn['RefNumber']) # so_list.remove(linkedtxn['RefNumber'])
# so = SalesOrderQuery(RefNumber = linkedtxn['RefNumber'], IncludeLinkedTxns='true', debug=False) # so = SalesOrderQuery(RefNumber = linkedtxn['RefNumber'], IncludeLinkedTxns='true', debug=False)
sodt = so_dict[linkedtxn['RefNumber']] sodt = so_dict[linkedtxn['RefNumber']]
@ -212,68 +217,235 @@ def process_data(iq_list, so_dict):
soNotInOneInv.append(sodt['RefNumber']) soNotInOneInv.append(sodt['RefNumber'])
if float(linkedtxn['Amount'])<0: if float(linkedtxn['Amount'])<0:
if sodt['TotalAmount']!=linkedtxn['Amount'][1:]: # if sodt['TotalAmount']!=linkedtxn['Amount'][1:]:
if is_soLinkedToOneInvoice: #maybe the SO is manually closed, check it item by item, find which item is not in invoice # pass
if sodt['IsManuallyClosed'] == 'true': # if is_soLinkedToOneInvoice: #maybe the SO is manually closed, check it item by item, find which item is not in invoice
pass # if sodt['IsManuallyClosed'] == 'true':
print(f"{sodt['TxnID'] = } {sodt['RefNumber'] = }") # pass
manuallyClosedSO.append(sodt['RefNumber']) # print(f"{sodt['TxnID'] = } {sodt['RefNumber'] = }")
else: # manuallyClosedSO.append(sodt['RefNumber'])
print('SO TotalAmount<>Amount in Invoice. not Manually closed and not fully Invoiced') # else:
pprint(f'{linkedtxn = }', sort_dicts=False) # print('SO TotalAmount<>Amount in Invoice. not Manually closed and not fully Invoiced')
print(f"{sodt['TxnID'] = } {sodt['RefNumber'] = }") # pprint(f'{linkedtxn = }', sort_dicts=False)
print(sodt) # print(f"{sodt['TxnID'] = } {sodt['RefNumber'] = }")
openSO.append(sodt['RefNumber']) # print(sodt)
else: # openSO.append(sodt['RefNumber'])
pass # this SO is fully invoiced, starting process to export the details # else:
pass # this SO is fully invoiced, starting process to export the details
try:
if not isinstance(sodt['SalesOrderLineRet'],list):
sodt['SalesOrderLineRet'] = [sodt['SalesOrderLineRet']]
if not isinstance(txn['InvoiceLineRet'],list):
txn['InvoiceLineRet'] = [txn['InvoiceLineRet']]
for so_line in sodt['SalesOrderLineRet']:
checklist = ['ItemRef', ]
if len([ i for i in checklist if i in so_line])==0:
continue
soitemref_fullname = so_line.get('ItemRef',{}).get('FullName')
soquantity:str = so_line.get('Quantity')
# print(f'{soquantity = }')
sounitofmeasure = so_line.get('UnitOfMeasure')
# sooverrideuomsetref_fullname = so_line['OverrideUOMSetRef']['FullName']
sorate = so_line.get('Rate')
soamount = so_line.get('Amount')
soinvoiced = so_line.get('Invoiced')
soismanuallyclosed = so_line['IsManuallyClosed']
soother2 = so_line.get('Other2')
#check compare to the invoicelineret
for inv_line in txn['InvoiceLineRet']: #loop start from top in order
is_inv_so_line_ok = True
if 'soline' not in inv_line: #this line has no so link yet
if inv_line.get('ItemRef',{}).get('FullName') != soitemref_fullname:
continue
if inv_line.get('UnitOfMeasure') != sounitofmeasure:
print(f"{inv_line['ItemRef']['FullName'] = } {inv_line['UnitOfMeasure']=} != {sounitofmeasure=}")
is_inv_so_line_ok = False
#do convertion????
# continue
if soinvoiced and inv_line.get('Quantity',0)!=soinvoiced: #compre with the invoiced
print(f"{inv_line['ItemRef']['FullName'] = } {inv_line['Quantity']=} != {soquantity=}")
is_inv_so_line_ok = False
if sorate:
if float(inv_line.get('Rate',0))!=float(sorate):
print(f"{inv_line['ItemRef']['FullName'] = } {inv_line['Rate']=} <> {sorate=} ; {float(inv_line.get('Rate',0)) = } {float(sorate) = }")
is_inv_so_line_ok = False
# if soamount:
# if float(inv_line.get('Amount',0))!=float(soamount):
# print(f"{txn['RefNumber']} {inv_line['ItemRef']['FullName'] = } {inv_line['Amount']=} != {soamount=}")
# is_inv_so_line_ok = False
if not is_inv_so_line_ok:
print(f"{inv_line['ItemRef']['FullName'] = } Some detail not equal")
continue
#add to the spesific invoiceline
inv_line['soline']= {'RefNumber':sodt['RefNumber'],
'TxnDate':sodt['TxnDate'],
'ItemRef_FullName':soitemref_fullname,
'Quantity':soquantity,
'UnitOfMeasure':sounitofmeasure,
'Rate':sorate,
'Amount':soamount,
'Invoiced':soinvoiced,
'IsManuallyClosed':soismanuallyclosed,
'Other2':soother2}
break
except Exception as e:
print(f"SO {sodt['RefNumber'] = } {txn['RefNumber'] = }")
print(f'ERROR: {e}')
else: else:
print('Linkedtxn amount is positif(should be negatif') print('Linkedtxn amount is positif(should be negatif')
except Exception as e: except Exception as e:
print('ERROR') # print('ERROR')
pprint(linkedtxn, sort_dicts=False) # pprint(linkedtxn, sort_dicts=False)
print(f"{sodt['TxnID'] = }") # print(f"{sodt['TxnID'] = }")
print(sodt) print(f"{sodt['RefNumber'] = } {txn['RefNumber'] = }")
print(e) print(f'ERROR: {e}')
break break
else: else:
nolinkInv.append(txn) nolinkInv.append(txn)
# c =next(iter(iq_list))
# print('TEST RESULT:')
# pprint(c, sort_dicts=False)
print(f'{len(nolinkInv) = }\n{nolinkInv = }\n{soNotInOneInv = }\n{manuallyClosedSO = }\n{openSO = }\n{len(fullyInvoicedSO) = }') print(f'{len(nolinkInv) = }\n{nolinkInv = }\n{soNotInOneInv = }\n{manuallyClosedSO = }\n{openSO = }\n{len(fullyInvoicedSO) = }')
c = [item for item in so_list if item not in fullyInvoicedSO] c = [item for item in so_list if item not in fullyInvoicedSO]
print(f'not fuuly invoice, leftover SO: {c}') print(f'not fuuly invoice, leftover SO: {c}')
return iq_list
def writeToFile(iq_list, so_dict):
def checking_iqwith_so(iq_list):
if not iq_list:
return False
if not isinstance(iq_list,list):
iq_list = [iq_list]
inv_nolineret = []
inv_line_no_soline = []
inv_line_no_itemref = []
for txn in iq_list:
if 'InvoiceLineRet' not in txn:
print(f"{txn['RefNumber'] = } doesnt have InvoiceLineRet")
inv_nolineret.append(txn['RefNumber'])
continue
if not isinstance(txn['InvoiceLineRet'], list):
txn['InvoiceLineRet'] = [txn['InvoiceLineRet']]
for idx, inv_line in enumerate(txn['InvoiceLineRet']):
if 'soline' in inv_line:
continue
if 'ItemRef' not in inv_line:
inv_line_no_itemref.append({'RefNumber':txn['RefNumber'],
'idx':idx,})
continue
if '400_Sales' not in inv_line['ItemRef']['FullName']:
inv_line_no_soline.append({'RefNumber':txn['RefNumber'],
'idx':idx,
'ItemRef_FullName':inv_line['ItemRef']['FullName'],
'Amount':inv_line['Amount']})
print(f'{inv_line_no_soline = }')
print(f'{inv_line_no_itemref = }')
print(f'{inv_nolineret = }')
if inv_line_no_soline or inv_line_no_itemref or inv_nolineret:
return False
else:
return True
def writeToFile(iq_list=None, so_dict=None, filename = "", suffix="", indent=2):
if not filename:
return False
try: try:
iq_list_json = json.dumps(iq_list, indent=2) if iq_list:
so_dict_json = json.dumps(so_dict, indent=2) if indent==None:
# Writing to sample.json iq_list_json = json.dumps(iq_list)
with open("iq.json", "w") as outfile: else:
outfile.write(iq_list_json) iq_list_json = json.dumps(iq_list, indent=indent)
with open("iq_so_dict.json", "w") as outfile: with open(f"{filename}{suffix}.json", "w") as outfile:
outfile.write(so_dict_json) outfile.write(iq_list_json)
if so_dict:
if indent==None:
so_dict_json = json.dumps(so_dict)
else:
so_dict_json = json.dumps(so_dict, indent=indent)
with open(f"{filename}_so_dict{suffix}.json", "w") as outfile:
outfile.write(so_dict_json)
return True return True
except Exception as e: except Exception as e:
print(e) print(e)
return False return False
def readJsonFromFile(iq_filename, so_filename): def readJsonFromFile(filename):
try: try:
with open(iq_filename, "r") as infile: with open(filename, "r") as infile:
iq_list = json.load(infile) _list = json.load(infile)
with open(so_filename, "r") as infile: return _list
so_dict = json.load(infile)
return iq_list, so_dict
except Exception as e: except Exception as e:
print(e) print(e)
return [],{} return []
# print(timeit.repeat(process, repeat=1))
# process()
# iq_list, so_dict = get_all_so_from_invoice(MaxReturned=20)
iq_list, so_dict = get_all_so_from_invoice(FromTxnDate="2024-08-01", ToTxnDate="2024-08-31")
writeToFile(iq_list, so_dict)
iq_list, so_dict = readJsonFromFile("iq.json", "iq_so_dict.json")
print(f'{len(iq_list) = } {len(so_dict) = }')
process_data(iq_list, so_dict)
def get_last_date_of_month(stryearmonth:str):
# Get Last date of Month
# Using replace() + timedelta()
# initializing date
test_date = datetime.datetime.fromisoformat(stryearmonth)
# test_date = datetime.datetime(2018, 6, 4)
# printing original date
print("The original date is : " + str(test_date))
# getting next month
# using replace to get to last day + offset
# to reach next month
nxt_mnth = test_date.replace(day=28) + datetime.timedelta(days=4)
# subtracting the days from next month date to
# get last date of current Month
res = nxt_mnth - datetime.timedelta(days=nxt_mnth.day)
# printing result
datesplit = stryearmonth.split('-')
print("Last date of month : " + str(res.day), f"{datesplit[0]}-{datesplit[1]}-{str(res.day)}")
return f"{datesplit[0]}-{datesplit[1]}-{str(res.day)}"
def main(fromtxndate, totxndate, maxreturned:int=None):
# print(timeit.repeat(process, repeat=1))
# process()
invqueryfilename = f'exim\Data\{fromtxndate}iq'
# if maxreturned:
# iq_list, so_dict = get_all_so_from_invoice(MaxReturned=maxreturned)
# else:
# try:
# _fromdate = datetime.datetime.fromisoformat(fromtxndate)
# _todate = datetime.datetime.fromisoformat(totxndate)
# except Exception as e:
# print('date format should be yyyy-mm-dd example: "2024-03-09"')
# return False
# ## Reading from QB and write to a file depends on txndate
# iq_list, so_dict = get_all_so_from_invoice(FromTxnDate=fromtxndate, ToTxnDate=totxndate)
# writeToFile(iq_list, so_dict, filename=invqueryfilename)
### reading from existing file the iq_list and so_dict jsonfile
iq_list = readJsonFromFile(f"{invqueryfilename}.json")
so_dict = readJsonFromFile(f"{invqueryfilename}_so_dict.json")
print(f'{len(iq_list) = } {len(so_dict) = }')
iq_list = process_data(iq_list, so_dict)
suffix = '_withsoindent2'
writeToFile(iq_list, filename=f'{invqueryfilename}', suffix=suffix, indent=2)
iq_list = readJsonFromFile(f'{invqueryfilename}{suffix}.json')
print(len(iq_list))
print(f'Seems {invqueryfilename}{suffix}.json is {checking_iqwith_so(iq_list)}') #checking the iq_list. is it good to import to new QB?
if __name__=='__main__':
# print(np.arange('2021-02', '2021-03', dtype='datetime64[D]'))
fromtxndate = '2022-08-01'
totxndate = get_last_date_of_month(fromtxndate)
_fromdate = datetime.datetime.fromisoformat(fromtxndate)
print(fromtxndate)
main(fromtxndate, totxndate)

437061
iq.json

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff