diff --git a/QBClasses.py b/QBClasses.py index ad97cc5..46db2ed 100644 --- a/QBClasses.py +++ b/QBClasses.py @@ -387,7 +387,7 @@ class SalesOrderQuery(baseQBQuery): self.QBDict[self.__class__.__name__ + "Rq"]={} ### End Required Variable. can put ENUM list below this (eg: self.ENUM_GeneralSummaryReportQuery=[]) - if 'TxnID' in kwargs and kwargs['TxnID']: + if 'TxnID' in kwargs and kwargs['TxnID'] and len(kwargs['TxnID'])>0: self.QBDict[self.__class__.__name__ + "Rq"]["TxnID"]=kwargs['TxnID'] elif 'RefNumber' in kwargs and kwargs['RefNumber']: self.QBDict[self.__class__.__name__ + "Rq"]["RefNumber"]=kwargs['RefNumber'] @@ -467,14 +467,11 @@ class InvoiceQuery(baseQBQuery): if 'TxnID' in kwargs and kwargs['TxnID']: _txnIDs=makeAList(kwargs['TxnID']) - self.QBDict[self.__class__.__name__ + "Rq"]["TxnID"]= _txnIDs + self.QBDict[self.__class__.__name__ + "Rq"]["TxnID"]= makeAList(kwargs['TxnID']) elif 'RefNumber' in kwargs and kwargs['RefNumber']: - _refNumbers=makeAList(kwargs['RefNumber']) - # print(f'{_refNumbers=}') - self.QBDict[self.__class__.__name__ + "Rq"]['RefNumber']=_refNumbers + self.QBDict[self.__class__.__name__ + "Rq"]['RefNumber']=makeAList(kwargs['RefNumber']) elif 'RefNumberCaseSensitive' in kwargs and kwargs['RefNumberCaseSensitive']: - _refNumbersCaseSensitive=makeAList(kwargs['RefNumberCaseSensitive']) - self.QBDict[self.__class__.__name__ + "Rq"]["RefNumberCaseSensitive"]=_refNumbersCaseSensitive + self.QBDict[self.__class__.__name__ + "Rq"]["RefNumberCaseSensitive"]=makeAList(kwargs['RefNumberCaseSensitive']) else: if 'MaxReturned' in kwargs: self.QBDict[self.__class__.__name__ + "Rq"]["MaxReturned"]=kwargs['MaxReturned'] @@ -486,18 +483,20 @@ class InvoiceQuery(baseQBQuery): self.QBDict[self.__class__.__name__ + "Rq"]["TxnDateRangeFilter"]={'DateMacro':kwargs['TxnDateRangeFilter_DateMacro']} if 'EntityFilter_ListID' in kwargs: - self.QBDict[self.__class__.__name__ + "Rq"]["EntityFilter"]={'ListID':kwargs['EntityFilter_ListID']} + # self.QBDict[self.__class__.__name__ + "Rq"]["EntityFilter"]={'ListID':kwargs['EntityFilter_ListID']} + self.QBDict[self.__class__.__name__ + "Rq"]['EntityFilter']={'ListID':makeAList(kwargs['EntityFilter_ListID'])} elif 'EntityFilter_FullName' in kwargs: - self.QBDict[self.__class__.__name__ + "Rq"]["EntityFilter"]={'FullName':kwargs['EntityFilter_FullName']} + # self.QBDict[self.__class__.__name__ + "Rq"]["EntityFilter"]={'FullName':kwargs['EntityFilter_FullName']} + self.QBDict[self.__class__.__name__ + "Rq"]['EntityFilter']={'FullName':makeAList(kwargs['EntityFilter_FullName'])} elif 'EntityFilter_ListIDWithChildren' in kwargs: self.QBDict[self.__class__.__name__ + "Rq"]["EntityFilter"]={'ListIDWithChildren':kwargs['EntityFilter_ListIDWithChildren']} elif 'EntityFilter_FullNameWithChildren' in kwargs: self.QBDict[self.__class__.__name__ + "Rq"]["EntityFilter"]={'FullNameWithChildren':kwargs['EntityFilter_FullNameWithChildren']} if 'AccountFilter_ListID' in kwargs: - self.QBDict[self.__class__.__name__ + "Rq"]["AccountFilter"]={'ListID':kwargs['AccountFilter_ListID']} + self.QBDict[self.__class__.__name__ + "Rq"]["AccountFilter"]={'ListID':makeAList(kwargs['AccountFilter_ListID'])} elif 'AccountFilter_FullName' in kwargs: - self.QBDict[self.__class__.__name__ + "Rq"]["AccountFilter"]={'FullName':kwargs['AccountFilter_FullName']} + self.QBDict[self.__class__.__name__ + "Rq"]["AccountFilter"]={'FullName':makeAList(kwargs['AccountFilter_FullName'])} elif 'AccountFilter_ListIDWithChildren' in kwargs: self.QBDict[self.__class__.__name__ + "Rq"]["AccountFilter"]={'ListIDWithChildren':kwargs['AccountFilter_ListIDWithChildren']} elif 'AccountFilter_FullNameWithChildren' in kwargs: @@ -506,7 +505,8 @@ class InvoiceQuery(baseQBQuery): if 'RefNumberFilter_MatchCriterion' in kwargs and 'RefNumberFilter_RefNumber' in kwargs: self.QBDict[self.__class__.__name__ + "Rq"]["RefNumberFilter"]={'MatchCriterion':kwargs['RefNumberFilter_MatchCriterion', 'RefNumber':kwargs['RefNumberFilter_RefNumber']]} elif 'RefNumberRangeFilter_FromRefNumber' in kwargs or 'RefNumberRangeFilter_ToRefNumber' in kwargs: #if or then use {'key1':kwargs.get('key1',""), 'key2':kwargs.get('key2', "")} - self.QBDict[self.__class__.__name__ + "Rq"]["RefNumberRangeFilter"]={'FromRefNumber':kwargs.get('RefNumberRangeFilter_FromRefNumber', ""), 'ToRefNumber':kwargs.get('RefNumberRangeFilter_ToRefNumber', "")} + ##### CAREFULL with the get() coz "" will get anything + self.QBDict[self.__class__.__name__ + "Rq"]["RefNumberRangeFilter"]={'FromRefNumber':kwargs.get('RefNumberRangeFilter_FromRefNumber', ""), 'ToRefNumber':kwargs.get('RefNumberRangeFilter_ToRefNumber', "")} if 'CurrencyFilter_ListID' in kwargs: self.QBDict[self.__class__.__name__ + "Rq"]["CurrencyFilter"]=kwargs['CurrencyFilter_ListID'] diff --git a/save_data.py b/save_data.py index bed98ee..a49b7c5 100644 --- a/save_data.py +++ b/save_data.py @@ -1,6 +1,6 @@ from QBClasses import SalesOrderQuery, InvoiceQuery, TransactionQuery -from utils import timing, makeAList -from pprint import pprint +from utils import timing, makeAList, makeLinkedTxnList +from pprint import pprint, PrettyPrinter # h1="17b2d5" # h2="17b2c1" # res = int(h2,16)-int(h1,16) @@ -16,14 +16,113 @@ def main(): "IsToBeEmailed", "ExternalGUID", "LinkedTxn", "InvoiceLineRet", "InvoiceLineGroupRet", ] + so_IncludeRetElements = ["TxnID", "TimeCreated", "TimeModified", "EditSequence", "TxnNumber", "CustomerRef", + "TxnDate", "RefNumber", "PONumber,", "SalesRepRef", "SubTotal", "TotalAmount", + "IsManuallyClosed", "IsFullyInvoiced", "Memo", "CustomerMsgRef", "LinkedTxn", "SalesOrderLineRet", "SalesOrderLineGroupRet", + ] refNumbers_listofdict= [{'RefNumber': '0099'}, {'RefNumber': '0100'}, {'RefNumber': '0103'}] refNumbers_list= makeAList(refNumbers_listofdict,dictvalue=True, listofdict=True) print(refNumbers_list) - invQ = InvoiceQuery(**{'debug':False, 'MaxReturned':100, 'RefNumber':refNumbers_list, 'IncludeLineItems':'true', 'IncludeLinkedTxns':'true'})#, 'IncludeRetElements':IncludeRetElements})#, 'OwnerID':0} ) + # invQ = InvoiceQuery(**{'debug':False, 'MaxReturned':1, 'RefNumber':refNumbers_list, 'IncludeLineItems':'true', 'IncludeLinkedTxns':'true', 'IncludeRetElements':IncludeRetElements})#, 'OwnerID':0} ) + invQ = InvoiceQuery(**{'debug':False, 'MaxReturned':1, 'IncludeLineItems':'true', 'IncludeLinkedTxns':'true', 'IncludeRetElement':IncludeRetElements})#, 'OwnerID':0} ) + # print(pprint(invQ), type(invQ), pprint(invQ.all())) # print(invQ.response_string) - print(invQ.filter("RefNumber")) - print(invQ.count()) + # print(invQ.filter(["RefNumber","txnid"])) + # print(invQ.all(), type(invQ.all())) + # PrettyPrinter( sort_dicts=False).pprint(invQ.all()) + # pprint(invQ.all(),sort_dicts=False) + print(f'{invQ.count() =}') + + ## start process ## + invdicts = invQ.all() + if isinstance(invdicts,dict): + invdicts=[invdicts] + + print("SALES ORDER") + + for inv_idx, invdict in enumerate(invdicts): #loop over every Invoice, and get the linkedtxnID + InvoiceLineRet = invQ.filter("InvoiceLineRet").all(invdict) + linkedtxns = invQ.filter("LinkedTxn").all(invdict) + print(f'{InvoiceLineRet=}', type(InvoiceLineRet)) + # print(linkedtxns, type(linkedtxns)) + linkedTxnIDs=makeLinkedTxnList(linkedtxns, 'SalesOrder') #this is the list of linkedTxn.TxnID + print(f'{inv_idx=} of {len(invdicts)}; {linkedTxnIDs = }') + so_init={'IncludeLineItems':'true', 'IncludeLinkedTxns':'true', 'TxnID':linkedTxnIDs, 'IncludeRetElement': so_IncludeRetElements} + SOOrders=SalesOrderQuery(**so_init) + # pprint(SOOrders.all(), sort_dicts=False) + solineret = SOOrders.filter(["TxnID", "RefNumber", "SalesOrderLineRet"]).all() + # print(solineret) + sorefnumbers = SOOrders.filter("RefNumber").getValuesOf("RefNumber") + print(f'{sorefnumbers=}') + is_soRefNumber_duplicated = False + for sorefNumber in sorefnumbers: #check if SO RefNumber is duplicated, not valid RefNumber raise error + + checkduplicateRefNumber = SalesOrderQuery(RefNumber=sorefNumber) + if checkduplicateRefNumber.count()==1: + pass # SOrefnumber no duplicate + print("no duplicate refnumber") + + elif checkduplicateRefNumber.count()>1: + is_soRefNumber_duplicated=True + print(f"there is duplicate in one of there refnumbers:{sorefNumber=}") + raise Exception(f"there is duplicate in one of there refnumbers:{sorefNumber=}") + break + print(f'{checkduplicateRefNumber.count() = }') + if is_soRefNumber_duplicated: + return + + #prepare SOOrders to be a list of dict + soorders = SOOrders.all() + if isinstance(soorders, dict): + soorders=[soorders] + #start comparing inv to so line by line + invLineRets = InvoiceLineRet[0].get('InvoiceLineRet') + for invLineRet in invLineRets: + FullName_inv = invLineRet['ItemRef']['FullName'] + TxnLineID_inv = invLineRet['TxnLineID'] + Quantity_inv = invLineRet['Quantity'] + UnitOfMeasure_inv = invLineRet['UnitOfMeasure'] + OverrideUOMSetRef_inv = invLineRet['OverrideUOMSetRef']['FullName'] + Rate_inv = invLineRet['Rate'] + Amount_inv = invLineRet['Amount'] + + for soorder in soorders: + pass + TxnID_so = soorder['TxnID'] + RefNumber_so = soorder['RefNumber'] + IsFullyInvoiced_so = soorder['IsFullyInvoiced'] + IsManuallyClosed_so = soorder['IsManuallyClosed'] + SalesOrderLineRet_so = soorder['SalesOrderLineRet'] + LinkedTxn_so = soorder['LinkedTxn'] + #prepare SalesOrderLineRet to be a list of dict + if isinstance(SalesOrderLineRet_so, dict): + SalesOrderLineRet_so = [SalesOrderLineRet_so] + # print(f'{SalesOrderLineRet_so = }') + for soLineRet_idx, soLineRet in enumerate(SalesOrderLineRet_so): + # print(soLineRet_idx, soLineRet) + print(f"{soLineRet_idx} {soLineRet.get('checked', False) = }") + if soLineRet.get('checked', False): # if can find checked key and not True, then skip next soLineRet + # print(soLineRet_idx,"continue") + continue + print(f'{soLineRet_idx = }->{soLineRet}') + FullName_so = soLineRet['ItemRef']['FullName'] + TxnLineID_so = soLineRet['TxnLineID'] + Quantity_so = soLineRet['Quantity'] + UnitOfMeasure_so = soLineRet['UnitOfMeasure'] + OverrideUOMSetRef_so = soLineRet['OverrideUOMSetRef']['FullName'] + Rate_so = soLineRet['Rate'] + Amount_so = soLineRet['Amount'] + Invoiced_so = soLineRet.get('Invoiced',0) + IsManuallyClosed_so = soLineRet['IsManuallyClosed'] + SalesOrderLineRet_so[soLineRet_idx]['checked']=True + # break only if match with invlineret==solineret + # pprint(solineret, sort_dicts=False) + # pprint(SOOrders.all(), sort_dicts=False) + # pprint(invQ.all()) + print(f'{SOOrders.count() = }') + + if __name__=='__main__': diff --git a/server.py b/server.py index 641d816..37c64c9 100644 --- a/server.py +++ b/server.py @@ -69,7 +69,7 @@ class baseQBQuery: dataDict["?qbxml"]["QBXML"] = {"QBXMLMsgsRq": { ### Example with multiple FullName Item ### "@onError": self.onError, firstKey: self.QBDict[firstKey]}} - print(f'{dataDict = }') + # print(f'{dataDict = }') # # QBXML = '' + xmltodict.unparse(dataDict, pretty=True) self.QBXML = xmltodict.unparse(dataDict, pretty=True).replace("", "").replace(f'version="{version}"', f'version="{version}"?') print(self.QBXML, type(self.QBXML)) diff --git a/utils.py b/utils.py index 12b5295..a37c890 100644 --- a/utils.py +++ b/utils.py @@ -2,6 +2,25 @@ from time import time from typing import Union + +def makeLinkedTxnList(x:list, key:str=None)->list: + txnIDs=[] + if isinstance(x, dict): + x=[x] + print(f'{type(x)=}') + for y in x: #y = each dict of LinkedTxn, and it has a listofdict or a dict inside + linkedTxns=y.get('LinkedTxn',None) + if linkedTxns is None: + return + if isinstance(linkedTxns, dict): + linkedTxns = [linkedTxns] + for linkedTxn in linkedTxns: + # print(f'{key = }') + if key and linkedTxn.get('TxnType')==key: + txnIDs.append(linkedTxn.get('TxnID',None)) + print(f'{txnIDs = }') + return txnIDs + def makeAList(x:Union[str , dict , list], dictvalue:bool=False, listofdict:bool=False)->list : if isinstance(x,str): x=[x]