# from . import QBClasses from pprint import pprint # from QBClass.QBClasses import InvoiceQuery, SalesOrderQuery from QBClass.QBClasses import InvoiceQuery, SalesOrderQuery # import timeit import time import json import pandas as pd print('succes Loading modules') def timer(func): def wrapper(*args, **kwargs): nonlocal total start = time.time() result = func(*args, **kwargs) duration = time.time() - start total += duration print(f"Execution time: {duration} Total: {total}") return result total = 0 return wrapper @timer def get_all_so_from_invoice(MaxReturned=None, FromTxnDate=None, ToTxnDate=None): print(MaxReturned, FromTxnDate, ToTxnDate) start = time.time() print('Get Invoice Query List. Processing..... wait for at minute(1 month=90secs)') if MaxReturned: iq = InvoiceQuery(MaxReturned= MaxReturned, IncludeLinkedTxns='true', IncludeLineItems='true', debug=False) elif FromTxnDate and ToTxnDate: print('here') iq = InvoiceQuery(TxnDateRangeFilter_FromTxnDate=FromTxnDate, TxnDateRangeFilter_ToTxnDate=ToTxnDate, IncludeLinkedTxns='true', IncludeLineItems='true', debug=False) else: iq = InvoiceQuery(TxnDateRangeFilter_FromTxnDate='2021-01-01', TxnDateRangeFilter_ToTxnDate='2021-03-30', IncludeLinkedTxns='true', IncludeLineItems='true', debug=False) # pprint(iq.all(), sort_dicts=False) # print(iq.all()) print(f"Execution time InvoiceQuery: {time.time()-start} {len(iq.all()) = }") so_list = [] iq_list = [] dup_so_list = [] for idx, txn in enumerate(iq.all()): # iq_list.append(txn) # print(f"{idx = } {txn['RefNumber'] = } {txn['TxnDate'] = } {txn['Subtotal'] = } ") if 'LinkedTxn' in txn: # pprint(txn['LinkedTxn'], sort_dicts=False) if not isinstance(txn['LinkedTxn'], list): #if there is no receive payment and only 1 linked traction, need to change to a list. RECORD it txn_linkedTxn = [txn['LinkedTxn']] else: txn_linkedTxn = txn['LinkedTxn'] for linkedtxn in txn_linkedTxn: if linkedtxn['TxnType']=='SalesOrder': if linkedtxn['RefNumber'] not in so_list: so_list.append(linkedtxn['RefNumber']) else: dup_so_list.append(linkedtxn['RefNumber']) print(f'{dup_so_list = }') print() so_dict = {} print(f"Execution time before SO: {time.time()-start}") print('Get Sales Order Query List. Processing..... wait for at minute(1 month=130 secs)') so = SalesOrderQuery(RefNumber = so_list, IncludeLinkedTxns='true', IncludeLineItems='true', debug=False) duplicateSO = [] soWithNoLinkedTxn = [] print(f"Execution time SalesOrderQuery: {time.time()-start}") for idx, txn in enumerate(so.all()): if 'LinkedTxn' in txn: if txn['RefNumber'] not in so_dict: so_dict[txn['RefNumber']] = txn else: duplicateSO.append(txn['RefNumber']) else: soWithNoLinkedTxn.append(txn) # pprint(so.all(), sort_dicts=False) res = next(iter(so_dict)) # print(f'{so_dict[res] = }') # pprint(so_dict[res]) print(f'{soWithNoLinkedTxn = }') print(f'{len(iq.all()) = } {len(so.all()) = } {len(so_list) = } {len(dup_so_list) = } {len(so_dict) = }') print(f'{duplicateSO = }') print() ### start processing like below module ### pass return iq.all(), so_dict @timer def process(): 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') pprint(iq.all(), sort_dicts=False) # print(iq.all()) nolinkInv = [] soNotInOneInv = [] for idx, txn in enumerate(iq.all()): print(f"{idx = } {txn['RefNumber'] = } {txn['TxnDate'] = } {txn['Subtotal'] = } ") # print(f"{txn['Subtotal'] = }") if 'LinkedTxn' in txn: # pprint(txn['LinkedTxn'], sort_dicts=False) if not isinstance(txn['LinkedTxn'], list): #if there is no receive payment and only 1 linked traction, need to change to a list. RECORD it txn_linkedTxn = [txn['LinkedTxn']] else: txn_linkedTxn = txn['LinkedTxn'] for linkedtxn in txn_linkedTxn: try: if linkedtxn['TxnType']=='SalesOrder': so = SalesOrderQuery(RefNumber = linkedtxn['RefNumber'], IncludeLinkedTxns='true', debug=False) is_soLinkedToOneInvoice = False if 'LinkedTxn' in so.all(): if not isinstance(so.all()['LinkedTxn'], list): # print(so.all()) so_linkedTxn = [so.all()['LinkedTxn']] #make a list else: so_linkedTxn = so.all()['LinkedTxn'] #just copy, already list # print(so.all()) for solinkedtxn in so_linkedTxn: # print(len(so_linkedTxn)) if solinkedtxn['TxnType']=='Invoice' and len(so_linkedTxn)==1: # print(so.all()['RefNumber'], 'the only one SO') is_soLinkedToOneInvoice=True # pass else: is_soLinkedToOneInvoice=False print(so.all()['RefNumber'], 'NOT the only One, this SO have other Invoice number') #make append to a list soNotInOneInv.append(so.all()['RefNumber']) if float(linkedtxn['Amount'])<0: if so.all()['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 if so.all()['IsManuallyClosed'] == 'true': pass print(f"{so.all()['TxnID'] = } {so.all()['RefNumber'] = }") else: print('SO TotalAmount<>Amount in Invoice. not Manually closed and not fully Invoiced') pprint(f'{linkedtxn = }', sort_dicts=False) print(f"{so.all()['TxnID'] = } {so.all()['RefNumber'] = }") print(so.all()) else: pass # this SO is fully invoiced, starting process to export the details else: print('Linkedtxn amount is positif(should be negatif') except Exception as e: print('ERROR') pprint(linkedtxn, sort_dicts=False) print(f"{so.all()['TxnID'] = }") print(so.all()) print(e) break else: nolinkInv.append(txn) print(f'{len(nolinkInv) = }') @timer def process_data(iq_list, so_dict): # 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') # pprint(iq_list, sort_dicts=False) # print(iq_list) nolinkInv = [] soNotInOneInv = [] manuallyClosedSO = [] openSO = [] fullyInvoicedSO = [] so_list = [x for x in so_dict] print(f'{len(so_list) = }') for idx, txn in enumerate(iq_list): # print(f"{idx = } {txn['RefNumber'] = } {txn['TxnDate'] = } {txn['Subtotal'] = } ") # print(f"{txn['Subtotal'] = }") if 'LinkedTxn' in txn: # pprint(txn['LinkedTxn'], sort_dicts=False) if not isinstance(txn['LinkedTxn'], list): #if there is no receive payment and only 1 linked traction, need to change to a list. RECORD it txn_linkedTxn = [txn['LinkedTxn']] else: txn_linkedTxn = txn['LinkedTxn'] for linkedtxn in txn_linkedTxn: try: if linkedtxn['TxnType']=='SalesOrder': # so_list.remove(linkedtxn['RefNumber']) # so = SalesOrderQuery(RefNumber = linkedtxn['RefNumber'], IncludeLinkedTxns='true', debug=False) sodt = so_dict[linkedtxn['RefNumber']] is_soLinkedToOneInvoice = False if sodt['IsManuallyClosed']=='true': manuallyClosedSO.append(sodt['RefNumber']) if sodt['IsFullyInvoiced']=='true': fullyInvoicedSO.append(sodt['RefNumber']) if 'LinkedTxn' in sodt: if not isinstance(sodt['LinkedTxn'], list): # print(sodt) so_linkedTxn = [sodt['LinkedTxn']] #make a list else: so_linkedTxn = sodt['LinkedTxn'] #just copy, already list # print(sodt) for solinkedtxn in so_linkedTxn: # print(len(so_linkedTxn)) if solinkedtxn['TxnType']=='Invoice' and len(so_linkedTxn)==1: # print(sodt['RefNumber'], 'the only one SO') is_soLinkedToOneInvoice=True # pass else: is_soLinkedToOneInvoice=False print(sodt['RefNumber'], 'NOT the only One, this SO have other Invoice number') #make append to a list soNotInOneInv.append(sodt['RefNumber']) if float(linkedtxn['Amount'])<0: 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 if sodt['IsManuallyClosed'] == 'true': pass print(f"{sodt['TxnID'] = } {sodt['RefNumber'] = }") manuallyClosedSO.append(sodt['RefNumber']) else: print('SO TotalAmount<>Amount in Invoice. not Manually closed and not fully Invoiced') pprint(f'{linkedtxn = }', sort_dicts=False) print(f"{sodt['TxnID'] = } {sodt['RefNumber'] = }") print(sodt) openSO.append(sodt['RefNumber']) else: pass # this SO is fully invoiced, starting process to export the details else: print('Linkedtxn amount is positif(should be negatif') except Exception as e: print('ERROR') pprint(linkedtxn, sort_dicts=False) print(f"{sodt['TxnID'] = }") print(sodt) print(e) break else: nolinkInv.append(txn) 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] print(f'not fuuly invoice, leftover SO: {c}') def writeToFile(iq_list, so_dict): try: iq_list_json = json.dumps(iq_list, indent=2) so_dict_json = json.dumps(so_dict, indent=2) # Writing to sample.json with open("iq.json", "w") as outfile: outfile.write(iq_list_json) with open("iq_so_dict.json", "w") as outfile: outfile.write(so_dict_json) return True except Exception as e: print(e) return False def readJsonFromFile(iq_filename, so_filename): try: with open(iq_filename, "r") as infile: iq_list = json.load(infile) with open(so_filename, "r") as infile: so_dict = json.load(infile) return iq_list, so_dict except Exception as e: print(e) 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)