# 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)



