From c8c7816f01918dcbfb8453d5c1724f004d1924f4 Mon Sep 17 00:00:00 2001 From: bcomsugi Date: Fri, 21 Feb 2025 06:08:34 +0700 Subject: [PATCH] update exim --- exim.py | 238 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 143 insertions(+), 95 deletions(-) diff --git a/exim.py b/exim.py index 2b92da5..533b60f 100644 --- a/exim.py +++ b/exim.py @@ -6,6 +6,8 @@ from QBClass.QBClasses import InvoiceQuery, SalesOrderQuery import time import json import pandas as pd +import numpy as np +import datetime print('succes Loading modules') @@ -26,7 +28,7 @@ def timer(func): @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) start = time.time() print('Get Invoice Query List. Processing..... wait for at minute(1 month=90secs)') @@ -160,6 +162,7 @@ def process(): @timer def process_data(iq_list, so_dict): + print('process_data') # 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) @@ -214,81 +217,81 @@ def process_data(iq_list, so_dict): 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 - 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') + # if sodt['TotalAmount']!=linkedtxn['Amount'][1:]: + # pass + # 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 + 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['ItemRef']['FullName'] != soitemref_fullname: - continue - if inv_line.get('UnitOfMeasure') != sounitofmeasure: - print(f"{inv_line['ItemRef']['FullName'] = } {inv_line['UnitOfMeasure']=} != {sounitofmeasure=}") + #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 - #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 inv_line['Amount']!=soamount or float(inv_line['Amount'])!=float(soamount): - if soamount: - if float(inv_line.get('Amount',0))!=float(soamount): - print(f"{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}') + # 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: print('Linkedtxn amount is positif(should be negatif') except Exception as e: @@ -329,9 +332,7 @@ def checking_iqwith_so(iq_list): continue if 'ItemRef' not in inv_line: inv_line_no_itemref.append({'RefNumber':txn['RefNumber'], - 'idx':idx, - 'ItemRef_FullName':inv_line['ItemRef']['FullName'], - 'Amount':inv_line['Amount']}) + 'idx':idx,}) continue if '400_Sales' not in inv_line['ItemRef']['FullName']: inv_line_no_soline.append({'RefNumber':txn['RefNumber'], @@ -364,7 +365,7 @@ def writeToFile(iq_list=None, so_dict=None, filename = "", suffix="", indent=2): so_dict_json = json.dumps(so_dict) else: so_dict_json = json.dumps(so_dict, indent=indent) - with open(f"{filename}{suffix}.json", "w") as outfile: + with open(f"{filename}_so_dict{suffix}.json", "w") as outfile: outfile.write(so_dict_json) return True except Exception as e: @@ -380,24 +381,71 @@ def readJsonFromFile(filename): print(e) return [] -# print(timeit.repeat(process, repeat=1)) -# process() -invqueryfilename = 'exim\Data\iq' -# iq_list, so_dict = get_all_so_from_invoice(MaxReturned=20) -### Reading from QB and write to a file depends on txndate -# iq_list, so_dict = get_all_so_from_invoice(FromTxnDate="2024-08-01", ToTxnDate="2024-08-31") -# writeToFile(iq_list, so_dict) +def get_last_date_of_month(stryearmonth:str): + # Get Last date of Month + # Using replace() + timedelta() -### reading from existing file the iq_list and so_dict jsonfile + # 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)}") -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? + 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) \ No newline at end of file