Untitled

mail@pastecode.io avatar
unknown
apex
a year ago
31 kB
45
Indexable
Never
List<String> contractNamesList = new List<String>{'LAI-00069730'};

List<String> hds = new List<String>();

hds.add('Name');
hds.add('loan__Loan_Product_Name__r.Name');
hds.add('loan__Loan_Status__c');
hds.add('_calculatedLoanAmount');
hds.add('_calculatedTotalMargin');
hds.add('_calculatedPayoffAsOfToday');
hds.add('_disbursalVariance');
hds.add('_marginVariance');
hds.add('_payOffVariance');
hds.add('_calculatedVariance');
hds.add('totalPaybackAmount');
hds.add('TotalOutstandingAmount__c');
hds.add('loan__Pay_Off_Amount_As_Of_Today__c');
hds.add('_contractBalanceCalculated');
hds.add('feeTotal');
hds.add('_chargedApplied');
hds.add('_chargeWaived');
hds.add('_chargeWaivedUnwaivedCountOtherTxn');
hds.add('_subChargeApplied');
hds.add('_subChargeWaived');
hds.add('feePaidTotal');
hds.add('loan__Fees_Paid__c');
hds.add('_feesPaidVariance');
hds.add('loan__Fees_Remaining__c');
hds.add('_computedFeesRemaining');
hds.add('principalTotal');
hds.add('loan__Principal_Paid__c');
hds.add('_principalPaidVariance');
hds.add('disbursementTotal');
hds.add('loan__Principal_Remaining__c');
hds.add('computedPrincipalRemaining');
hds.add('loan__Loan_Amount__c');
hds.add('pendingPaymentTotal');
hds.add('_interestIncurred');
hds.add('interestTotal');
hds.add('loan__Interest_Paid__c');
hds.add('_interestPaidVariance');
hds.add('_interestWaivedOtherTxn');
hds.add('totalLocDeferralInterestPaid');
hds.add('totalLocDeferralInterestAccrued');
hds.add('totalLocDeferralInterestRemaining');
hds.add('loan__Interest_Remaining__c');
hds.add('_computedInterestRemaining');
hds.add('loan__Interest_Waived__c');
hds.add('loan__Interest_estimated__c');
hds.add('interestFeesTotal');
hds.add('Additional_Interest_Charged__c');
hds.add('contractPaymentTotal');
hds.add('otherTxnTotal');
hds.add('excessTotal');
hds.add('_totalExcess');
hds.add('loan__Excess__c');
hds.add('_excessApplied');
hds.add('computedExcess');
hds.add('totalCredit');
hds.add('totalDebit');
hds.add('totalPaymentsReceived');
hds.add('totalPrincipalWaived');
hds.add('Principal_Waived__c');
hds.add('totalExcessFlushed');
hds.add('Loc_Interest_Accrued__c');
hds.add('Deferral_Period_Start_Date__c');
hds.add('Deferral_Period_End_Date__c');
hds.add('_contractRestructureOtherTxn');
hds.add('_refundOtherTxn');
hds.add('_balanceWaivedOtherTxn');

System.debug(LoggingLevel.INFO,String.join(hds,';'));
Id extentionRecordTypeId = Schema.SObjectType.loan__Other_Transaction__c.getRecordTypeInfosByName().get('Extension Record Type').getRecordTypeId();
List<String> avoidCheckStatusList = new List<String>{'Public Holiday'};
Map<String,List<loan__Loan_Account__History>> loanHistoryMap = new Map<String,List<loan__Loan_Account__History>>();
Map<String,loan__Loan_Payment_Transaction__c> loanToLPTMap = new Map<String,loan__Loan_Payment_Transaction__c>();
Map<String,loan__Loan_account_Due_Details__c> loanToBillMap = new Map<String,loan__Loan_account_Due_Details__c>();
Map<String,loan__Other_Transaction__c> loanToOtherTxnMap = new Map<String,loan__Other_Transaction__c>();
Map<String,loan__Loan_Disbursal_Transaction__c> disbursalTxnMap = new Map<String,loan__Loan_Disbursal_Transaction__c>();
Map<String,loan__Charge__c> chargeMap = new Map<String,loan__Charge__c>();
Map<String,Subcharge__c> subChargeMap = new Map<String,Subcharge__c>();

Boolean shouldPrintTransaction = contractNamesList.size()==1;

for(loan__Loan_Account__History loanHistory : [
	SELECT Id, IsDeleted, ParentId, Parent.Name, CreatedById, CreatedBy.Name, CreatedDate, Field, DataType, OldValue, NewValue
		FROM loan__Loan_Account__History
		WHERE Parent.Name IN :contractNamesList
		AND DataType='Double'
		AND Field NOT IN ('DPD_Age_YTD__c','Days_Past_Paid__c')
		ORDER by Parent.Name, CreatedDate]) {

	String key = loanHistory.Parent.Name + String.valueOf(loanHistory.CreatedDate.Date());

	if(loanHistoryMap.containsKey(key)) {
		loanHistoryMap.get(key).add(loanHistory);
	} else {
		loanHistoryMap.put(key, new List<loan__Loan_Account__History>{loanHistory});
	}
}

for (loan__Loan_Payment_Transaction__c lpt: [
	SELECT  Name, loan__Loan_Account__r.Name, loan__Transaction_Date__c, loan__Transaction_Amount__c, loan__Clearing_Date__c, Transaction_Status__c , LastModifiedDate,
			loan__Interest__c, loan__Fees__c, loan__Principal__c, loan__Payment_Mode__r.Name, loan__Excess__c,
		(SELECT CreatedDate, NewValue FROM Histories where Field IN ('loan__Rejected__c', 'loan__Reversed__c') order by createdDate desc limit 1)
		FROM loan__Loan_Payment_Transaction__c
		WHERE loan__Loan_Account__r.Name IN :contractNamesList
		AND Transaction_Status__c IN ('Cleared','Rejected','Reversed')
		order by loan__Transaction_Date__c, loan__Clearing_Date__c]) {
	loanToLPTMap.put(lpt.Name, lpt);
}

for (loan__Loan_account_Due_Details__c bill : [
	Select Name, CreatedDate, LastModifiedDate, Deferral_Interest__c, loan__Transaction_Date__c, loan__Due_Amt__c
		FROM loan__Loan_account_Due_Details__c
		WHERE loan__Loan_Account__r.Name IN :contractNamesList]) {
	loanToBillMap.put(bill.Name, bill);
}

for (loan__Other_Transaction__c otherTxn : [
	Select Name, CreatedDate, LastModifiedDate, loan__Waived_Interest__c, Waived_Principal__c, loan__Txn_Amt__c, loan__Transaction_Type__c,
			loan__Description__c, loan__Txn_Date__c, RecordTypeId, Deferral_Period_Start_Date__c, Deferral_Period_End_Date__c, Loc_Deferral_Interest_Payoff_Date__c
		FROM loan__Other_Transaction__c
		WHERE loan__Loan_Account__r.Name IN :contractNamesList
		ORDER BY loan__Txn_Date__c]) {
	loanToOtherTxnMap.put(otherTxn.Name, otherTxn);
}

for (loan__Loan_Disbursal_Transaction__c disbursalTxn : [
	Select Name, CreatedDate, LastModifiedDate
		FROM loan__Loan_Disbursal_Transaction__c
		WHERE loan__Loan_Account__r.Name IN :contractNamesList]) {
	disbursalTxnMap.put(disbursalTxn.Name, disbursalTxn);
}

for (loan__Charge__c charge : [
	Select Name, CreatedDate, LastModifiedDate
		FROM loan__Charge__c
		WHERE loan__Loan_Account__r.Name IN :contractNamesList]) {
	chargeMap.put(charge.Name, charge);
}

for (Subcharge__c subCharge : [
	Select Name, CreatedDate, LastModifiedDate
		FROM Subcharge__c
		WHERE Loan_Account__r.Name IN :contractNamesList]) {
	subChargeMap.put(subCharge.Name, subCharge);
}

for (loan__Loan_Account__c loan : [
	Select Id, Name, loan__Loan_Product_Name__r.Name, loan__Previous_Installment_Date__c, loan__Interest_Rate__c, loan__Disbursal_Date__c,
		(Select Id, Deferral_Period_Start_Date__c, Deferral_Period_End_Date__c from loan__Other_Loan_Transactions__r
		where RecordTypeId = :extentionRecordTypeId
		AND (NOT Deferral_Reason__c IN :avoidCheckStatusList)
		ORDER BY Deferral_Period_Start_Date__c)
	from loan__Loan_Account__c
	where Name IN :contractNamesList
]) {
	String errorPrefix = loan.Name;

	List<TaxStatementResponse> taxStatementResponseList = TaxStatementUtil.getAllTaxStatementsForLoan(loan.Id, new Set<String>(), true, true);

	TaxStatementResponse taxRes = taxStatementResponseList[0];

	List<TaxStatementResponse.TaxStatement> taxStmtList = taxStatementResponseList[0].taxStatementsList;

	Decimal interestWaivedOtherTxn = 0.00;
	Decimal feesWaivedOtherTxn = 0.00;
	Decimal contractRestructureOtherTxn = 0.00;
	Decimal chargeWaivedUnwaivedCountOtherTxn = 0.00;
	Decimal refundOtherTxn = 0.00;
	Decimal balanceWaivedOtherTxn = 0.00;
	Decimal chargedApplied = 0.00;
	Decimal chargeWaived = 0.00;
	Decimal subChargeApplied = 0.00;
	Decimal subChargeWaived = 0.00;
	Integer descPad = 0;
	Integer annoPad = 0;
	Decimal interestIncurred = 0;
	Set<String> keySet = new Set<String>();
	Decimal interestAccrued = 0.00;

	Map<String, TaxStatementResponse.TaxStatement> taxStMap = new Map<String, TaxStatementResponse.TaxStatement>();
	Integer sortHelper = 1000;
	DateTime txnDateTime = DateTime.now();

	for(TaxStatementResponse.TaxStatement taxSt : taxStmtList) {
		descPad = Math.max(descPad,taxSt.description.length());
		annoPad = Math.max(annoPad,taxSt.annotation.length());

		if(taxSt.description.containsIgnoreCase('Interest Incurred')) { interestIncurred += taxSt.debitAmount; }

		txnDateTime = DateTime.now();
		Integer openIndex = taxSt.description.indexOf('(')+1;
		Integer closeIndex = taxSt.description.indexOf(')');
		String key = '';
		if(openIndex>0 && closeIndex>0) {
			key = taxSt.description.substring(openIndex,closeIndex);
			if(taxSt.description.contains('Refinancing')) { key = key.split(':')[0]; }
			keySet.add(key);
		}

		if(taxSt.transactionDate==null) {
			txnDateTime = DateTime.now();
			taxSt.transactionDate = Date.today();
		} else {
			txnDateTime = DateTime.newInstance(taxSt.transactionDate.year(), taxSt.transactionDate.month(), taxSt.transactionDate.day());
		}

		if(loanToBillMap.containsKey(key) && loanToBillMap.get(key).CreatedDate.Date()==taxSt.transactionDate) {
			txnDateTime = loanToBillMap.get(key).CreatedDate;
		} else if(loanToOtherTxnMap.containsKey(key) && loanToOtherTxnMap.get(key).CreatedDate.Date()==taxSt.transactionDate) {
			txnDateTime = loanToOtherTxnMap.get(key).CreatedDate;
		} else if(disbursalTxnMap.containsKey(key) && disbursalTxnMap.get(key).CreatedDate.Date()==taxSt.transactionDate) {
			txnDateTime = disbursalTxnMap.get(key).CreatedDate;
		} else if(chargeMap.containsKey(key)) {
			if(taxSt.description.containsIgnoreCase('Waived')) { txnDateTime = chargeMap.get(key).LastModifiedDate; }
			else if(chargeMap.get(key).CreatedDate.Date()==taxSt.transactionDate) {txnDateTime = chargeMap.get(key).CreatedDate; }
		} else if(subChargeMap.containsKey(key)) {
			if(taxSt.description.containsIgnoreCase('Waived')) { txnDateTime = subChargeMap.get(key).LastModifiedDate; }
			else if(subChargeMap.get(key).CreatedDate.Date()==taxSt.transactionDate) {txnDateTime = subChargeMap.get(key).CreatedDate; }
		} else if(loanToLPTMap.containsKey(key)) {
			loan__Loan_Payment_Transaction__c lpt = loanToLPTMap.get(key);
			lpt.loan__Interest__c = lpt.loan__Interest__c==null ? 0.00 : lpt.loan__Interest__c;
			if(lpt.Transaction_Status__c=='Cleared') {
				txnDateTime = lpt.loan__Clearing_Date__c;
			} else if(lpt.Transaction_Status__c=='Rejected' || lpt.Transaction_Status__c=='Reversed' ) {
				txnDateTime = lpt.Histories?.CreatedDate;
				taxSt.annotation += lpt;
			}
			if(txnDateTime==null) {
				txnDateTime = lpt.LastModifiedDate;
			}
		}
		taxSt.transactionDate = txnDateTime.Date();

		taxStMap.put(String.valueOf(txnDateTime)+';'+key+';'+String.valueOf(sortHelper), taxSt);
		sortHelper += 10;
	}

	Decimal disbursalVariance = 0.00;
	Decimal computedLoanAmount = 0.00;
	Decimal runInterestRemaining = 0.00;
	Decimal runFeesRemaining = 0;
	Decimal runPrincipalRemaining = 0;
	Decimal runExcessRemaing = 0;
	Decimal excessApplied = 0;
	Decimal totalExcess = 0;
	Decimal intVariance = 0;
	Decimal marginVariance = 0.00;
	
	if(taxRes.loanAccount.loan__Loan_Amount__c==null) { taxRes.loanAccount.loan__Loan_Amount__c = 0.00; }
	if(taxRes.loanAccount.loan__Interest_estimated__c==null) { taxRes.loanAccount.loan__Interest_estimated__c = 0.00; }
	if(taxRes.loanAccount.Additional_Interest_Charged__c==null) { taxRes.loanAccount.Additional_Interest_Charged__c = 0.00; }
	taxRes.loanAccount.Loc_Interest_Accrued__c = (taxRes.loanAccount.Loc_Interest_Accrued__c==null) ? 0.00 : taxRes.loanAccount.Loc_Interest_Accrued__c.setScale(2);

	Decimal calculatedLoanAmount = (taxRes.loanAccount.loan__Principal_Paid__c==null) ? 0.00 : taxRes.loanAccount.loan__Principal_Paid__c.setScale(2);

	calculatedLoanAmount += (taxRes.loanAccount.loan__Principal_Remaining__c==null) ? 0.00 : taxRes.loanAccount.loan__Principal_Remaining__c.setScale(2);
	calculatedLoanAmount += (taxRes.loanAccount.Principal_Waived__c==null) ? 0.00 : taxRes.loanAccount.Principal_Waived__c.setScale(2);
	
	Decimal calculatedTotalMargin = (taxRes.loanAccount.loan__Interest_Paid__c==null) ? 0.00 : taxRes.loanAccount.loan__Interest_Paid__c.setScale(2);

	calculatedTotalMargin += (taxRes.loanAccount.loan__Interest_Remaining__c==null) ? 0.00 : taxRes.loanAccount.loan__Interest_Remaining__c.setScale(2);
	calculatedTotalMargin += (taxRes.loanAccount.loan__Interest_Waived__c==null) ? 0.00 : taxRes.loanAccount.loan__Interest_Waived__c.setScale(2);

	if(taxRes.isLineOfCredit) {
		disbursalVariance = taxRes.disbursementTotal.setScale(2) - calculatedLoanAmount;
		computedLoanAmount = taxRes.disbursementTotal.setScale(2);
		interestAccrued = taxRes.loanAccount.Loc_Interest_Accrued__c;
	} else {
		disbursalVariance = taxRes.loanAccount.loan__Loan_Amount__c.setScale(2) - calculatedLoanAmount;
		computedLoanAmount = taxRes.loanAccount.loan__Loan_Amount__c.setScale(2);
		runInterestRemaining = taxRes.loanAccount.loan__Interest_estimated__c;
		runPrincipalRemaining = computedLoanAmount;
		marginVariance = taxRes.loanAccount.loan__Interest_estimated__c.setScale(2) + taxRes.loanAccount.Additional_Interest_Charged__c.setScale(2) - calculatedTotalMargin;
	}

	contractRestructureOtherTxn = contractRestructureOtherTxn - taxRes.totalExcessFlushed;

	List<String> cols = new List<String>();



	Decimal calculatedPayoffAsOfToday = (taxRes.loanAccount.loan__Principal_Remaining__c==null) ? 0.00 : taxRes.loanAccount.loan__Principal_Remaining__c.setScale(2);

	calculatedPayoffAsOfToday += (taxRes.loanAccount.loan__Interest_Remaining__c==null) ? 0.00 : taxRes.loanAccount.loan__Interest_Remaining__c.setScale(2);
	calculatedPayoffAsOfToday += (taxRes.loanAccount.loan__Fees_Remaining__c==null) ? 0.00 : taxRes.loanAccount.loan__Fees_Remaining__c.setScale(2);
	calculatedPayoffAsOfToday -= (taxRes.loanAccount.loan__Excess__c==null) ? 0.00 : taxRes.loanAccount.loan__Excess__c.setScale(2);

	Decimal payOffVariance = taxRes.loanAccount.loan__Pay_Off_Amount_As_Of_Today__c.setScale(2) - (calculatedPayoffAsOfToday +  interestAccrued);

	Decimal totalOutstandingAmount = (taxRes.loanAccount.TotalOutstandingAmount__c==null) ? 0.00 : taxRes.loanAccount.TotalOutstandingAmount__c.setScale(2);

	Decimal contractBalanceCalculated = taxStmtList.get(taxStmtList.size() - 2).contractBalance + taxRes.totalExcessFlushed;

	Decimal calculatedVariance = (totalOutstandingAmount + taxRes.totalLocDeferralInterestRemaining) - contractBalanceCalculated;

	Decimal principalPaidVariance = taxRes.loanAccount.loan__Principal_Paid__c - taxRes.principalTotal;
	Decimal interestPaidVariance = taxRes.loanAccount.loan__Interest_Paid__c - taxRes.interestTotal;
	Decimal feesPaidVariance = taxRes.loanAccount.loan__Fees_Paid__c - taxRes.feePaidTotal;

	Decimal computedFeesRemaining = taxRes.feeTotal - taxRes.feePaidTotal;
	Decimal computedPrincipalRemaining = computedLoanAmount - taxRes.principalTotal;
	Decimal computedInterestRemaining = taxRes.loanAccount.loan__Interest_estimated__c.setScale(2) - taxRes.interestTotal;

	for (loan__Other_Transaction__c otherTxn : loanToOtherTxnMap.values()) {
		if(!keySet.contains(otherTxn.Name)) {
			TaxStatementResponse.TaxStatement taxSt = new TaxStatementResponse.TaxStatement();
			taxSt.transactionDate = otherTxn.loan__Txn_Date__c;
			taxSt.description = '(' + otherTxn.Name + ') '+ (otherTxn.loan__Transaction_Type__c==null?otherTxn.loan__Description__c:otherTxn.loan__Transaction_Type__c);
			taxSt.annotation = String.valueof(otherTxn);
			descPad = Math.max(descPad,taxSt.description.length());
			taxStmtList.add(taxSt);
			txnDateTime = DateTime.newInstance(taxSt.transactionDate, otherTxn.CreatedDate.Time());
			taxStMap.put(String.valueOf(txnDateTime)+';'+otherTxn.Name+';'+String.valueOf(sortHelper), taxSt);
			sortHelper += 10;
			if(otherTxn.RecordTypeId==extentionRecordTypeId) {
				TaxStatementResponse.TaxStatement defStart = new TaxStatementResponse.TaxStatement();
				defStart.transactionDate = otherTxn.Deferral_Period_Start_Date__c;
				defStart.transactionType = 'DeferralTransaction';
				defStart.description = '(Deferral Start Date)';
				taxStmtList.add(defStart);
				txnDateTime = DateTime.newInstance(defStart.transactionDate, otherTxn.CreatedDate.Time());
				taxStMap.put(String.valueOf(txnDateTime)+';'+otherTxn.Name+';'+String.valueOf(sortHelper), defStart);
				sortHelper += 10;

				TaxStatementResponse.TaxStatement defEnd = new TaxStatementResponse.TaxStatement();
				defEnd.transactionDate = otherTxn.Deferral_Period_End_Date__c?.addDays(1);
				defEnd.transactionType = 'DeferralTransaction';
				defEnd.description = '(Deferral End Date Computation)';
				defEnd.annotation = 'Deferral end Date: '+otherTxn.Deferral_Period_End_Date__c+' Pay Off Date: '+String.valueof(otherTxn.Loc_Deferral_Interest_Payoff_Date__c);
				taxStmtList.add(defEnd);
				txnDateTime = DateTime.newInstance(defEnd.transactionDate, otherTxn.CreatedDate.Time());
				taxStMap.put(String.valueOf(txnDateTime)+';'+otherTxn.Name+';'+String.valueOf(sortHelper), defEnd);
				sortHelper += 10;
			}
		}
	}

	for (loan__Loan_account_Due_Details__c bill : loanToBillMap.values()) {
		if(!keySet.contains(bill.Name)) {
			TaxStatementResponse.TaxStatement taxSt = new TaxStatementResponse.TaxStatement();
			taxSt.transactionDate = bill.loan__Transaction_Date__c;
			taxSt.description = '(' + bill.Name + ') For Debugging';
			taxSt.annotation = String.valueof(bill);
			descPad = Math.max(descPad,taxSt.description.length());
			taxStmtList.add(taxSt);
			txnDateTime = DateTime.newInstance(taxSt.transactionDate, bill.CreatedDate.Time());
			taxStMap.put(String.valueOf(txnDateTime)+':'+bill.Name+';'+String.valueOf(sortHelper), taxSt);
			sortHelper += 10;
		}
	}

	Decimal mContractBalance = taxRes.loanAccount.loan__Loan_Amount__c + taxRes.loanAccount.loan__Interest_estimated__c;
	Decimal runInterestPaid = 0;
	Decimal runFeesPaid = 0;
	Decimal runPrincipalPaid = 0;

	List<String> txnHd = new List<String>();
	txnHd.add('   Transaction Date');
	txnHd.add('T');
	txnHd.add(('Transaction Description').leftPad(Math.max(descPad,50)));
	txnHd.add(('Debit Amount').leftPad(20));
	txnHd.add(('Credit Amount').leftPad(20));
	txnHd.add(('Balance').leftPad(20));
	txnHd.add(' Annotation');

	Integer hd2=txnHd[2].length();
	Integer hd3=txnHd[3].length();
	Integer hd4=txnHd[4].length();
	Integer hd5=txnHd[5].length();

	List<String> histHd = new List<String>();
	histHd.add(txnHd[0]);
	histHd.add('H');
	histHd.add(('History Field').leftpad(hd2));
	histHd.add(('Old Value').leftpad(hd3));
	histHd.add(('New Value').leftpad(hd4));
	histHd.add(('Difference').leftpad(hd5));
	histHd.add(' Created By / Annotation');

	String singeLine = '-';
	for(Integer i=0;i<8;i++) {
		singeLine=singeLine+singeLine;
	}

	if(shouldPrintTransaction) {
		System.debug(String.join(txnHd,';'));
		System.debug(singeLine);
	}

	Decimal computedInterest = 0.00;
	Decimal runComputedInterest = 0.00;
	Decimal runDeferralInterest = 0.00;
	Decimal dailyInterest = 0.00;

	List<String> mapKeyList = new List<String>(taxStMap.keySet());
	List<DateTime> datetimeList = new List<DateTime>();
	mapKeyList.sort();
	taxStmtList.clear();
	for (String mapKey : mapKeyList) {
		taxStmtList.add(taxStMap.get(mapKey));
		datetimeList.add(Datetime.valueOf(mapKey.split(';')[0]));
	}

	for (Integer index=0; index < taxStmtList.size() ; index++) {
		Boolean isLastIdx = index==taxStmtList.size()-1;
		TaxStatementResponse.TaxStatement taxSt = taxStmtList[index];

		if(taxSt.addAmount) { mContractBalance += taxSt.debitAmount; }
		else if(taxSt.subtractAmount) { mContractBalance -= taxSt.creditAmount; }

		taxSt.contractBalance = mContractBalance;

		Integer openIndex = taxSt.description.indexOf('(')+1;
		Integer closeIndex = taxSt.description.indexOf(')');
		String key = '';

		if(openIndex>0 && closeIndex>0) {
			key = taxSt.description.substring(openIndex,closeIndex);
			if(taxSt.description.contains('Refinancing')) { key = key.split(':')[0]; }
		}

		if(taxSt.description.containsIgnoreCase('Interest Incurred')) {
			taxSt.annotation += ' computedInterest: '+ String.valueof(computedInterest);
			intVariance += (computedInterest - taxSt.debitAmount);
			computedInterest = 0;
			runInterestRemaining += taxSt.debitAmount;
			taxSt.annotation += ' intVariance: '+String.valueof(intVariance);
			if(loanToBillMap.containsKey(key)) {
				taxSt.annotation += ' Deferral_Interest__c: '+ String.valueof(loanToBillMap.get(key).Deferral_Interest__c?.setScale(2));
			}
		} else if(taxSt.transactionType.equalsIgnoreCase('Charge Applied')) {
			if( taxSt.subtractAmount ) { runFeesRemaining -= taxSt.creditAmount;}
			else if(taxSt.addAmount) { runFeesRemaining += taxSt.debitAmount; }

			if(taxSt.description.containsIgnoreCase('CHG-')) {
				chargedApplied += taxSt.debitAmount;
				if(taxSt.description.containsIgnoreCase('Waived')) {
					chargeWaived += taxSt.creditAmount;
				}
			} else if(taxSt.description.containsIgnoreCase('SCH-')) {
				subChargeApplied += taxSt.debitAmount;
				if(taxSt.description.containsIgnoreCase('Waived')) {
					subChargeWaived += taxSt.creditAmount;
				}
			}
		} else if(taxSt.transactionType.equalsIgnoreCase('Charge Waived')) {
			chargeWaivedUnwaivedCountOtherTxn++;
		}

		if(loanToOtherTxnMap.containsKey(key)) {
			if(taxSt.transactionType.equalsIgnoreCase('Interest Waived') || taxSt.transactionType.equalsIgnoreCase('Refinance Interest Waived')) {
				runInterestRemaining -= taxSt.creditAmount;
				interestWaivedOtherTxn += taxSt.creditAmount; //added to totalCredit
			} else if(taxSt.transactionType.equalsIgnoreCase('Contract Restructure')) {
				runInterestRemaining += taxSt.debitAmount;
				contractRestructureOtherTxn += taxSt.debitAmount; //added to debitAmount
			} else if(taxSt.transactionType=='Loan Balances Waived') {
				runInterestRemaining -= loanToOtherTxnMap.get(key).loan__Waived_Interest__c;
				runPrincipalRemaining -= loanToOtherTxnMap.get(key).Waived_Principal__c;
				balanceWaivedOtherTxn += taxSt.creditAmount;
			} else if(taxSt.transactionType.equalsIgnoreCase('Refinance Fees Waived') || taxSt.transactionType.equalsIgnoreCase('Refund Reversal')) {
				runFeesRemaining -= taxSt.creditAmount;
				feesWaivedOtherTxn += taxSt.creditAmount; //added to totalCredit
			} else if(taxSt.transactionType.equalsIgnoreCase('Refund') || taxSt.transactionType.equalsIgnoreCase('Fund Transfer')) {
				refundOtherTxn += taxSt.debitAmount;
			} else if (taxSt.transactionType.equalsIgnoreCase('Charge Reversal')) {
				totalExcess += taxSt.creditAmount;
			}
		} else if(disbursalTxnMap.containsKey(key)) {
			runPrincipalRemaining += taxSt.debitAmount;
		} else if(loanToLPTMap.containsKey(key)) {
			loan__Loan_Payment_Transaction__c lpt = loanToLPTMap.get(key);
			lpt.loan__Interest__c = lpt.loan__Interest__c==null ? 0.00 : lpt.loan__Interest__c;
			lpt.loan__Excess__c = lpt.loan__Excess__c==null ? 0.00 : lpt.loan__Excess__c.setScale(2);
			if(lpt.Transaction_Status__c=='Cleared') {
				runInterestPaid += lpt.loan__Interest__c;
				runFeesPaid += lpt.loan__Fees__c;
				runPrincipalPaid += lpt.loan__Principal__c;
				runPrincipalRemaining -= lpt.loan__Principal__c;
				runInterestRemaining -= lpt.loan__Interest__c;
				runFeesRemaining -= lpt.loan__Fees__c;
				if (lpt.loan__Payment_Mode__r.Name=='Excess' || lpt.loan__Payment_Mode__r.Name=='Excess Application' || lpt.loan__Payment_Mode__r.Name=='Excess Flush') {
					excessApplied += lpt.loan__Interest__c + lpt.loan__Fees__c + lpt.loan__Principal__c;
				} else if (lpt.loan__Payment_Mode__r.Name=='Refinancing') {
					excessApplied += lpt.loan__Interest__c + lpt.loan__Fees__c;
				} else {
					totalExcess += lpt.loan__Excess__c;
				}
			}
		}

		if(isLastIdx) {taxSt.annotation += ' computedInterest: '+ String.valueof(computedInterest);}

		List<String> txnLogCol = new List<String>();
		txnLogCol.add(String.valueof(datetimeList[index]));
		txnLogCol.add('T');
		txnLogCol.add(taxSt.description.leftPad(hd2));
		txnLogCol.add(String.valueOf(taxSt.debitAmount.setScale(4)).leftPad(hd3));
		txnLogCol.add(String.valueOf(taxSt.creditAmount.setScale(4)).leftPad(hd4));
		txnLogCol.add(String.valueOf(taxSt.contractBalance.setScale(4)).leftPad(hd5));
		String otherTxt = (String.valueof(datetimeList[index])+';').rightpad(String.join(txnLogCol,';').length());
		txnLogCol.add(' '+taxSt.annotation);

		if(shouldPrintTransaction) { System.debug(String.join(txnLogCol,';')); }
		
		runExcessRemaing = totalExcess - excessApplied - refundOtherTxn;

		Decimal payOff = runPrincipalRemaining + runInterestRemaining + runFeesRemaining - runExcessRemaing + computedInterest;

		otherTxt += '  PayOff: '+String.valueof(payOff);
		otherTxt += ', P Paid: '+ String.valueof( isLastIdx ? taxRes.loanAccount.loan__Principal_Paid__c : runPrincipalPaid);
		otherTxt += ', I Paid: '+String.valueof( isLastIdx ? taxRes.loanAccount.loan__Interest_Paid__c : runInterestPaid);
		otherTxt += ', F Paid: '+String.valueof( isLastIdx ? taxRes.loanAccount.loan__Fees_Paid__c : runFeesPaid);
		otherTxt += ', P Remain: '+String.valueof( isLastIdx ? taxRes.loanAccount.loan__Principal_Remaining__c : runPrincipalRemaining);
		otherTxt += ', I Remain: '+String.valueof( isLastIdx ? taxRes.loanAccount.loan__Interest_Remaining__c : runInterestRemaining);
		otherTxt += ', F Remain: '+String.valueof( isLastIdx ? taxRes.loanAccount.loan__Fees_Remaining__c : runFeesRemaining);
		otherTxt += ', E Remain: '+String.valueof( isLastIdx ? taxRes.loanAccount.loan__Excess__c : runExcessRemaing);
		otherTxt += ', runComputedInterest: '+String.valueof(runComputedInterest);
		otherTxt += ', dailyInterest: '+String.valueof(dailyInterest);

		if(shouldPrintTransaction) { System.debug(otherTxt); }

		Decimal txnAmount = (taxSt.debitAmount==0) ? taxSt.creditAmount : taxSt.debitAmount;

		if(isLastIdx || taxStmtList[index].transactionDate!=taxStmtList[index+1].transactionDate) {
			Date txnDate = taxSt.transactionDate;
			Date nextTxnDate = isLastIdx ? txnDate?.addDays(1) : taxStmtList[index+1].transactionDate;

			if(shouldPrintTransaction) {
				System.debug(singeLine);
				System.debug(String.join(histHd,';'));
				System.debug(singeLine);
			}

			while (txnDate < nextTxnDate) {
				String loanHistorykey = loan.Name + String.valueOf(txnDate);
				if(loanHistoryMap.containsKey(loanHistorykey)) {
					for(loan__Loan_Account__History loanHist : loanHistoryMap.get(loanHistorykey)) {
						Decimal dOldVal = (Decimal)loanHist.OldValue;
						Decimal dNewVal = (Decimal)loanHist.NewValue;
						dOldVal = (dOldVal==null ) ? 0.00 : dOldVal;
						String sOldVal = String.valueof(dOldVal.setScale(4));
						String sNewVal = String.valueof(dNewVal.setScale(4));
						Decimal dDiff = dNewVal - dOldVal;
						String sDiff = String.valueOf(dDiff.setScale(4));

						List<String> histCol = new List<String>();
						histCol.add(String.valueof(loanHist.CreatedDate));
						histCol.add('H');
						histCol.add(loanHist.Field?.leftPad(hd2));
						histCol.add(sOldVal?.leftPad(hd3));
						histCol.add(sNewVal?.leftPad(hd4));
						histCol.add(sDiff?.leftPad(hd5));

						String annotation = loanHist.CreatedBy.Name;
						if(loanHist.CreatedDate.time().hour() < 1 && loanHist.Field == 'loan__Pay_Off_Amount_As_Of_Today__c') {
							annotation += ', PayOff: '+String.valueof(payOff);
							annotation += ', dailyInterest: '+String.valueof(dailyInterest);
							annotation += ', payoffVariance: '+String.valueof(dNewVal-payOff);
						}
						histCol.add(' '+annotation);

						if(shouldPrintTransaction) { System.debug(String.join(histCol,';')); }
					}
				}
				dailyInterest = ((((runPrincipalRemaining-runExcessRemaing)*loan.loan__Interest_Rate__c) * 1.0 / 100) / 365).setScale(2);
				computedInterest += dailyInterest;
				runComputedInterest += dailyInterest;
				payOff += dailyInterest;
				txnDate = txnDate.addDays(1);
			}
			if(shouldPrintTransaction && !isLastIdx) {
				System.debug(singeLine.replace('-','='));
				System.debug(String.join(txnHd,';'));
				System.debug(singeLine);
			}
		}
	}

	cols.add(taxRes.loanAccount.Name);
	cols.add(loan.loan__Loan_Product_Name__r.Name);
	cols.add(taxRes.loanAccount.loan__Loan_Status__c);

	List<Decimal> tmp = new List<Decimal>();
	tmp.add(calculatedLoanAmount);
	tmp.add(calculatedTotalMargin);
	tmp.add(calculatedPayoffAsOfToday);
	tmp.add(disbursalVariance);
	tmp.add(marginVariance);
	tmp.add(payOffVariance);
	tmp.add(calculatedVariance);
	tmp.add(taxRes.totalPaybackAmount);
	tmp.add(taxRes.loanAccount.TotalOutstandingAmount__c);
	tmp.add(taxRes.loanAccount.loan__Pay_Off_Amount_As_Of_Today__c);
	tmp.add(contractBalanceCalculated);
	tmp.add(taxRes.feeTotal);
	tmp.add(chargedApplied);
	tmp.add(chargeWaived);
	tmp.add(chargeWaivedUnwaivedCountOtherTxn);
	tmp.add(subChargeApplied);
	tmp.add(subChargeWaived);
	tmp.add(taxRes.feePaidTotal);
	tmp.add(taxRes.loanAccount.loan__Fees_Paid__c);
	tmp.add(feesPaidVariance);
	tmp.add(taxRes.loanAccount.loan__Fees_Remaining__c);
	tmp.add(computedFeesRemaining);
	tmp.add(taxRes.principalTotal);
	tmp.add(taxRes.loanAccount.loan__Principal_Paid__c);
	tmp.add(principalPaidVariance);
	tmp.add(taxRes.disbursementTotal);
	tmp.add(taxRes.loanAccount.loan__Principal_Remaining__c);
	tmp.add(computedPrincipalRemaining);
	tmp.add(taxRes.loanAccount.loan__Loan_Amount__c);
	tmp.add(taxRes.pendingPaymentTotal);
	tmp.add(interestIncurred);
	tmp.add(taxRes.interestTotal);
	tmp.add(taxRes.loanAccount.loan__Interest_Paid__c);
	tmp.add(interestPaidVariance);
	tmp.add(interestWaivedOtherTxn);
	tmp.add(taxRes.totalLocDeferralInterestPaid);
	tmp.add(taxRes.totalLocDeferralInterestAccrued);
	tmp.add(taxRes.totalLocDeferralInterestRemaining);
	tmp.add(taxRes.loanAccount.loan__Interest_Remaining__c);
	tmp.add(computedInterestRemaining);
	tmp.add(taxRes.loanAccount.loan__Interest_Waived__c);
	tmp.add(taxRes.loanAccount.loan__Interest_estimated__c);
	tmp.add(taxRes.interestFeesTotal);
	tmp.add(taxRes.loanAccount.Additional_Interest_Charged__c);
	tmp.add(taxRes.contractPaymentTotal);
	tmp.add(taxRes.otherTxnTotal);
	tmp.add(taxRes.excessTotal);
	tmp.add(totalExcess);
	tmp.add(taxRes.loanAccount.loan__Excess__c);
	tmp.add(excessApplied);
	tmp.add(taxRes.excessTotal - excessApplied);
	tmp.add(taxRes.totalCredit);
	tmp.add(taxRes.totalDebit);
	tmp.add(taxRes.totalPaymentsReceived);
	tmp.add(taxRes.totalPrincipalWaived);
	tmp.add(taxRes.loanAccount.Principal_Waived__c);
	tmp.add(taxRes.totalExcessFlushed);
	tmp.add(taxRes.loanAccount.Loc_Interest_Accrued__c);
	tmp.add(contractRestructureOtherTxn);
	tmp.add(refundOtherTxn);
	tmp.add(balanceWaivedOtherTxn);

	for(Decimal val : tmp) {
		cols.add(String.valueOf(val));
	}

	if(shouldPrintTransaction) {
		for(Integer i=0 ; i < cols.size() ; i++) {
			System.debug(hds[i]+': '+cols[i]);
		}
	} else {
		System.debug(LoggingLevel.INFO, String.join(cols,';'));
	}
}