FINERACT-1905: Distribution logic fix

This commit is contained in:
Adam Saghy
2023-07-06 15:09:55 +02:00
parent b691ec4b1d
commit 43639bf0a3
6 changed files with 2736 additions and 198 deletions
@@ -410,6 +410,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money penaltyPortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) {
return penaltyPortionOfTransaction;
}
final Money penaltyChargesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(penaltyChargesDue)) {
this.penaltyChargesPaid = getPenaltyChargesPaid(currency).plus(penaltyChargesDue).getAmount();
@@ -432,7 +436,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money feePortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) {
return feePortionOfTransaction;
}
final Money feeChargesDue = getFeeChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feeChargesDue)) {
this.feeChargesPaid = getFeeChargesPaid(currency).plus(feeChargesDue).getAmount();
@@ -455,7 +461,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money interestPortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) {
return interestPortionOfTransaction;
}
final Money interestDue = getInterestOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
this.interestPaid = getInterestPaid(currency).plus(interestDue).getAmount();
@@ -478,7 +486,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money principalPortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) {
return principalPortionOfTransaction;
}
final Money principalDue = getPrincipalOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(principalDue)) {
this.principalCompleted = getPrincipalCompleted(currency).plus(principalDue).getAmount();
@@ -500,7 +510,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
public Money waiveInterestComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedInterestPortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) {
return waivedInterestPortionOfTransaction;
}
final Money interestDue = getInterestOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
this.interestWaived = getInterestWaived(currency).plus(interestDue).getAmount();
@@ -520,7 +532,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
public Money waivePenaltyChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedPenaltyChargesPortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) {
return waivedPenaltyChargesPortionOfTransaction;
}
final Money penanltiesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(penanltiesDue)) {
this.penaltyChargesWaived = getPenaltyChargesWaived(currency).plus(penanltiesDue).getAmount();
@@ -540,7 +554,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
public Money waiveFeeChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedFeeChargesPortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) {
return waivedFeeChargesPortionOfTransaction;
}
final Money feesDue = getFeeChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feesDue)) {
this.feeChargesWaived = getFeeChargesWaived(currency).plus(feesDue).getAmount();
@@ -56,15 +56,6 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
return STRATEGY_NAME;
}
@Override
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
return transactionDate.isBefore(currentInstallment.getDueDate());
}
/**
* For early/'in advance' repayments
*/
@@ -73,8 +64,106 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
charges);
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
Money transactionAmountRemaining = paymentInAdvance;
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
Money penaltyChargesPortion = Money.zero(currency);
if (loanTransaction.isChargesWaiver()) {
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate,
loanTransaction.getPenaltyChargesPortion(currency));
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
feeChargesPortion = currentInstallment.waiveFeeChargesComponent(transactionDate,
loanTransaction.getFeeChargesPortion(currency));
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
} else if (loanTransaction.isInterestWaiver()) {
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else if (loanTransaction.isChargePayment()) {
if (loanTransaction.isPenaltyPayment()) {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
} else {
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
}
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else {
boolean ignoreDueDateCheck = false;
boolean rerun = false;
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
Money calculatedPenaltyCharge = Money.zero(currency);
Money calculatedFeeCharge = Money.zero(currency);
// Calculate the amount of due charges
for (LoanCharge charge : orderedLoanChargesByDueDate) {
if (charge.isPenaltyCharge()) {
calculatedPenaltyCharge = calculatedPenaltyCharge.add(charge.getAmount(currency));
} else {
calculatedFeeCharge = calculatedFeeCharge.add(charge.getAmount(currency));
}
}
do {
Money subPenaltyPortion;
if (!ignoreDueDateCheck) {
if (calculatedPenaltyCharge.isGreaterThan(transactionAmountRemaining)) {
calculatedPenaltyCharge = transactionAmountRemaining;
}
} else {
calculatedPenaltyCharge = transactionAmountRemaining;
}
subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, calculatedPenaltyCharge);
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
Money subFeePortion;
if (!ignoreDueDateCheck) {
if (calculatedFeeCharge.isGreaterThan(transactionAmountRemaining)) {
calculatedFeeCharge = transactionAmountRemaining;
}
} else {
calculatedFeeCharge = transactionAmountRemaining;
}
subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, calculatedFeeCharge);
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
feeChargesPortion = feeChargesPortion.add(subFeePortion);
Money subInterestPortion;
if (ignoreDueDateCheck || !transactionDate.isBefore(currentInstallment.getDueDate())) {
subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
interestPortion = interestPortion.add(subInterestPortion);
}
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
principalPortion = principalPortion.add(subPrincipalPortion);
// If the transactionAmountRemaining is greater than zero, rerun the allocation without due date check
// to distribute the in advance portions
if (transactionAmountRemaining.isGreaterThanZero()) {
ignoreDueDateCheck = true;
}
rerun = !rerun;
} while (ignoreDueDateCheck && rerun);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
}
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
}
return transactionAmountRemaining;
}
/**
@@ -131,66 +220,22 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
}
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else {
boolean ignoreDueDateCheck = false;
boolean rerun = false;
Money subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
Money calculatedPenaltyCharge = Money.zero(currency);
Money calculatedFeeCharge = Money.zero(currency);
// Calculate the amount of due charges
for (LoanCharge charge : orderedLoanChargesByDueDate) {
if (charge.isPenaltyCharge()) {
calculatedPenaltyCharge = calculatedPenaltyCharge.add(charge.getAmount(currency));
} else {
calculatedFeeCharge = calculatedFeeCharge.add(charge.getAmount(currency));
}
}
Money subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
feeChargesPortion = feeChargesPortion.add(subFeePortion);
do {
Money subPenaltyPortion;
if (!ignoreDueDateCheck) {
if (calculatedPenaltyCharge.isGreaterThan(transactionAmountRemaining)) {
calculatedPenaltyCharge = transactionAmountRemaining;
}
} else {
calculatedPenaltyCharge = transactionAmountUnprocessed;
}
subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, calculatedPenaltyCharge);
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
Money subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
interestPortion = interestPortion.add(subInterestPortion);
Money subFeePortion;
if (!ignoreDueDateCheck) {
if (calculatedFeeCharge.isGreaterThan(transactionAmountRemaining)) {
calculatedFeeCharge = transactionAmountRemaining;
}
} else {
calculatedFeeCharge = transactionAmountUnprocessed;
}
subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, calculatedFeeCharge);
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
feeChargesPortion = feeChargesPortion.add(subFeePortion);
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
principalPortion = principalPortion.add(subPrincipalPortion);
Money subInterestPortion;
if (ignoreDueDateCheck || !transactionDate.isBefore(currentInstallment.getDueDate())) {
subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
interestPortion = interestPortion.add(subInterestPortion);
}
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
principalPortion = principalPortion.add(subPrincipalPortion);
// If the transactionAmountRemaining is greater than zero, rerun the allocation without due date check
// to distribute the in advance portions
if (transactionAmountRemaining.isGreaterThanZero()) {
ignoreDueDateCheck = true;
}
rerun = !rerun;
} while (ignoreDueDateCheck && rerun);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
}
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
@@ -56,109 +56,17 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
return STRATEGY_NAME;
}
@Override
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
return transactionDate.isBefore(currentInstallment.getDueDate());
}
/**
* For early/'in advance' repayments
*/
@Override
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money inAdvancePayment,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
Money transactionAmountRemaining = paymentInAdvance;
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
Money penaltyChargesPortion = Money.zero(currency);
if (loanTransaction.isChargesWaiver()) {
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate,
loanTransaction.getPenaltyChargesPortion(currency));
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
feeChargesPortion = currentInstallment.waiveFeeChargesComponent(transactionDate,
loanTransaction.getFeeChargesPortion(currency));
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
} else if (loanTransaction.isInterestWaiver()) {
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else if (loanTransaction.isChargePayment()) {
if (loanTransaction.isPenaltyPayment()) {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
} else {
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
}
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else {
// In advance penalty, interest, principal, fee
Money subPenaltyPortion;
subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
Money subInterestPortion;
subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
interestPortion = interestPortion.add(subInterestPortion);
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
principalPortion = principalPortion.add(subPrincipalPortion);
Money subFeePortion;
subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
feeChargesPortion = feeChargesPortion.add(subFeePortion);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
}
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
}
return transactionAmountRemaining;
}
/**
* For late repayments
*/
@Override
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
Set<LoanCharge> charges) {
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
transactionMappings, charges);
}
/**
* For normal on-time repayments
*/
@Override
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
Money transactionAmountRemaining = transactionAmountUnprocessed;
final MonetaryCurrency currency = inAdvancePayment.getCurrency();
Money transactionAmountRemaining = inAdvancePayment;
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
@@ -214,7 +122,7 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
calculatedPenaltyCharge = transactionAmountRemaining;
}
} else {
calculatedPenaltyCharge = transactionAmountUnprocessed;
calculatedPenaltyCharge = transactionAmountRemaining;
}
subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, calculatedPenaltyCharge);
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
@@ -227,9 +135,11 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
interestPortion = interestPortion.add(subInterestPortion);
}
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
principalPortion = principalPortion.add(subPrincipalPortion);
if (ignoreDueDateCheck || !transactionDate.isBefore(currentInstallment.getDueDate())) {
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
principalPortion = principalPortion.add(subPrincipalPortion);
}
Money subFeePortion;
if (!ignoreDueDateCheck) {
@@ -237,7 +147,7 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
calculatedFeeCharge = transactionAmountRemaining;
}
} else {
calculatedFeeCharge = transactionAmountUnprocessed;
calculatedFeeCharge = transactionAmountRemaining;
}
subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, calculatedFeeCharge);
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
@@ -259,6 +169,84 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
return transactionAmountRemaining;
}
/**
* For late repayments
*/
@Override
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money latePayment,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, latePayment, transactionMappings,
charges);
}
/**
* For normal on-time repayments
*/
@Override
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final LoanTransaction loanTransaction, final Money onTimePayment,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = onTimePayment.getCurrency();
Money transactionAmountRemaining = onTimePayment;
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
Money penaltyChargesPortion = Money.zero(currency);
if (loanTransaction.isChargesWaiver()) {
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate,
loanTransaction.getPenaltyChargesPortion(currency));
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
feeChargesPortion = currentInstallment.waiveFeeChargesComponent(transactionDate,
loanTransaction.getFeeChargesPortion(currency));
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
} else if (loanTransaction.isInterestWaiver()) {
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else if (loanTransaction.isChargePayment()) {
if (loanTransaction.isPenaltyPayment()) {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
} else {
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
}
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else {
Money subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
Money subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
interestPortion = interestPortion.add(subInterestPortion);
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
principalPortion = principalPortion.add(subPrincipalPortion);
Money subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
feeChargesPortion = feeChargesPortion.add(subFeePortion);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
}
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
}
return transactionAmountRemaining;
}
@Override
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
// TODO - KW - dont do anything with loan over-payment for now
@@ -19,6 +19,7 @@
package org.apache.fineract.integrationtests;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -30,8 +31,12 @@ import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import org.apache.fineract.client.models.BusinessDateRequest;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
@@ -49,6 +54,7 @@ import org.apache.fineract.integrationtests.common.loans.LoanRescheduleRequestTe
import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -64,7 +70,7 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
private BusinessDateHelper businessDateHelper;
private LoanTransactionHelper loanTransactionHelper;
private LoanRescheduleRequestHelper loanRescheduleRequestHelper;
private InlineLoanCOBHelper inlineLoanCOBHelper;
private AccountHelper accountHelper;
@BeforeEach
@@ -78,6 +84,7 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
this.loanRescheduleRequestHelper = new LoanRescheduleRequestHelper(this.requestSpec, this.responseSpec);
this.businessDateHelper = new BusinessDateHelper();
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
inlineLoanCOBHelper = new InlineLoanCOBHelper(requestSpec, responseSpec);
}
// Scenario1:
@@ -104,12 +111,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", true));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -216,12 +223,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", false));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -324,12 +331,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", false));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -441,12 +448,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", false));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "3", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "3", "0",
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "3", "1", "3", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "90", "30", "3", "0",
LoanApplicationTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -530,12 +537,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", false));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "3", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "3", "0",
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "3", "1", "3", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "90", "30", "3", "0",
LoanApplicationTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -678,12 +685,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "20", false));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -753,12 +760,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "15", true));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -890,12 +897,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "20", false));
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "15", true));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -909,7 +916,7 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
loanStatusHashMap = loanTransactionHelper.disburseLoanWithTransactionAmount("01 January 2023", loanID, "1000");
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateRescheduleFromDate("1 February 2023")
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateRescheduleFromDate("31 January 2023")
.updateAdjustedDueDate("01 March 2023").updateSubmittedOnDate("25 January 2023").updateGraceOnPrincipal(null)
.updateGraceOnInterest(null).updateExtraTerms(null).build(loanID.toString());
final HashMap<String, String> map = new HashMap<>();
@@ -1114,12 +1121,12 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "20", false));
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "15", true));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
"01 January 2023", "01 January 2023");
@@ -1133,7 +1140,7 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
loanStatusHashMap = loanTransactionHelper.disburseLoanWithTransactionAmount("01 January 2023", loanID, "1000");
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateRescheduleFromDate("1 February 2023")
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateRescheduleFromDate("31 January 2023")
.updateAdjustedDueDate("01 March 2023").updateSubmittedOnDate("25 January 2023").updateGraceOnPrincipal(null)
.updateGraceOnInterest(null).updateExtraTerms(null).build(loanID.toString());
final HashMap<String, String> map = new HashMap<>();
@@ -1397,13 +1404,401 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
}
}
// Scenario10:
// DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY
// 1. Disburse the loan
// 2. Snooze fee
// 3. Merchant issued refund (partial)
// 4. Charge adjustment (same day)
// 5. Merchant issued refund (rest)
@Test
public void scenario10() {
try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("2023.05.14").dateFormat("yyyy.MM.dd").locale("en"));
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(requestSpec, responseSpec, "submitted-date");
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "3.65", false));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY,
"14 May 2023", "14 May 2023");
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
loanStatusHashMap = loanTransactionHelper.approveLoan("14 May 2023", loanID);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
loanStatusHashMap = loanTransactionHelper.disburseLoanWithTransactionAmount("14 May 2023", loanID, "127.95");
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("2023.06.11").dateFormat("yyyy.MM.dd").locale("en"));
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateRescheduleFromDate("13 June 2023")
.updateAdjustedDueDate("13 July 2023").updateSubmittedOnDate("11 June 2023").updateGraceOnPrincipal(null)
.updateGraceOnInterest(null).updateExtraTerms(null).build(loanID.toString());
final HashMap<String, String> map = new HashMap<>();
map.put("locale", "en");
map.put("dateFormat", "dd MMMM yyyy");
map.put("approvedOnDate", "11 June 2023");
final String aproveRequestJSON = new Gson().toJson(map);
Integer loanRescheduleRequestId = this.loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
this.loanRescheduleRequestHelper.approveLoanRescheduleRequest(loanRescheduleRequestId, aproveRequestJSON);
Integer penalty1LoanChargeId = loanTransactionHelper.addChargesForLoan(loanID,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), "13 July 2023", "3.65"));
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("2023.06.12").dateFormat("yyyy.MM.dd").locale("en"));
inlineLoanCOBHelper.executeInlineCOB(List.of(loanID.longValue()));
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails((long) loanID);
assertEquals(131.6, loanDetails.getSummary().getTotalOutstanding());
assertEquals(131.6, loanDetails.getRepaymentSchedule().getTotalOutstanding());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
assertTrue(loanDetails.getStatus().getActive());
assertNull(loanDetails.getTransactions().get(0).getReversedOnDate());
assertTrue(loanDetails.getTransactions().get(0).getTransactionRelations().isEmpty());
assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement());
assertEquals(127.95, loanDetails.getTransactions().get(0).getAmount());
assertEquals(0.0, loanDetails.getTransactions().get(0).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(0).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(0).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(0).getInterestPortion());
assertEquals(0.0, loanDetails.getTransactions().get(0).getFeeChargesPortion());
assertEquals(127.95, loanDetails.getTransactions().get(0).getOutstandingLoanBalance());
assertNull(loanDetails.getTransactions().get(1).getReversedOnDate());
assertTrue(loanDetails.getTransactions().get(1).getTransactionRelations().isEmpty());
assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual());
assertEquals(3.65, loanDetails.getTransactions().get(1).getAmount());
assertEquals(0.0, loanDetails.getTransactions().get(1).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(1).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(1).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(1).getInterestPortion());
assertEquals(3.65, loanDetails.getTransactions().get(1).getFeeChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(1).getOutstandingLoanBalance());
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("2023.06.17").dateFormat("yyyy.MM.dd").locale("en"));
PostLoansLoanIdTransactionsResponse merchantIssuedRefund1 = loanTransactionHelper.makeMerchantIssuedRefund(Long.valueOf(loanID),
new PostLoansLoanIdTransactionsRequest().locale("en").dateFormat("dd MMMM yyyy").transactionDate("17 June 2023")
.transactionAmount(125.0));
loanDetails = loanTransactionHelper.getLoanDetails((long) loanID);
assertEquals(6.6, loanDetails.getSummary().getTotalOutstanding());
assertEquals(6.6, loanDetails.getRepaymentSchedule().getTotalOutstanding());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
assertEquals(125.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
assertEquals(2.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
assertTrue(loanDetails.getStatus().getActive());
assertNull(loanDetails.getTransactions().get(2).getReversedOnDate());
assertTrue(loanDetails.getTransactions().get(2).getTransactionRelations().isEmpty());
assertTrue(loanDetails.getTransactions().get(2).getType().getMerchantIssuedRefund());
assertEquals(125.0, loanDetails.getTransactions().get(2).getAmount());
assertEquals(125.0, loanDetails.getTransactions().get(2).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(2).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(2).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(2).getInterestPortion());
assertEquals(0.0, loanDetails.getTransactions().get(2).getFeeChargesPortion());
assertEquals(2.95, loanDetails.getTransactions().get(2).getOutstandingLoanBalance());
PostLoansLoanIdChargesChargeIdResponse chargeAdjustmentResponse = this.loanTransactionHelper.chargeAdjustment((long) loanID,
(long) penalty1LoanChargeId, new PostLoansLoanIdChargesChargeIdRequest().amount(3.65));
loanDetails = loanTransactionHelper.getLoanDetails((long) loanID);
assertEquals(2.95, loanDetails.getSummary().getTotalOutstanding());
assertEquals(2.95, loanDetails.getRepaymentSchedule().getTotalOutstanding());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
assertEquals(0.70, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
assertEquals(2.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
assertTrue(loanDetails.getStatus().getActive());
assertNull(loanDetails.getTransactions().get(3).getReversedOnDate());
assertFalse(loanDetails.getTransactions().get(3).getTransactionRelations().isEmpty());
assertEquals((long) penalty1LoanChargeId,
loanDetails.getTransactions().get(3).getTransactionRelations().iterator().next().getToLoanCharge());
assertTrue(loanDetails.getTransactions().get(3).getType().getChargeAdjustment());
assertEquals(3.65, loanDetails.getTransactions().get(3).getAmount());
assertEquals(2.95, loanDetails.getTransactions().get(3).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(3).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(3).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(3).getInterestPortion());
assertEquals(0.7, loanDetails.getTransactions().get(3).getFeeChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(3).getOutstandingLoanBalance());
PostLoansLoanIdTransactionsResponse merchantIssuedRefund2 = loanTransactionHelper.makeMerchantIssuedRefund(Long.valueOf(loanID),
new PostLoansLoanIdTransactionsRequest().locale("en").dateFormat("dd MMMM yyyy").transactionDate("17 June 2023")
.transactionAmount(2.95));
loanDetails = loanTransactionHelper.getLoanDetails((long) loanID);
assertEquals(0.0, loanDetails.getSummary().getTotalOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getTotalOutstanding());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
assertNull(loanDetails.getTransactions().get(4).getReversedOnDate());
assertTrue(loanDetails.getTransactions().get(4).getTransactionRelations().isEmpty());
assertTrue(loanDetails.getTransactions().get(4).getType().getMerchantIssuedRefund());
assertEquals(2.95, loanDetails.getTransactions().get(4).getAmount());
assertEquals(0.0, loanDetails.getTransactions().get(4).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(4).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(4).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(4).getInterestPortion());
assertEquals(2.95, loanDetails.getTransactions().get(4).getFeeChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(4).getOutstandingLoanBalance());
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(requestSpec, responseSpec, "due-date");
}
}
// Scenario11:
// DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY
// 1. Disburse the loan
// 2. Snooze fee
// 3. Merchant issued refund (partial)
// 4. Charge adjustment (same day)
// 5. Merchant issued refund (rest)
@Test
public void scenario11() {
try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("2023.05.14").dateFormat("yyyy.MM.dd").locale("en"));
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(requestSpec, responseSpec, "submitted-date");
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "3.65", false));
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "30", "1", "0",
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "30", "30", "1", "0",
LoanApplicationTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
"14 May 2023", "14 May 2023");
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
loanStatusHashMap = loanTransactionHelper.approveLoan("14 May 2023", loanID);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
loanStatusHashMap = loanTransactionHelper.disburseLoanWithTransactionAmount("14 May 2023", loanID, "127.95");
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("2023.06.11").dateFormat("yyyy.MM.dd").locale("en"));
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateRescheduleFromDate("13 June 2023")
.updateAdjustedDueDate("13 July 2023").updateSubmittedOnDate("11 June 2023").updateGraceOnPrincipal(null)
.updateGraceOnInterest(null).updateExtraTerms(null).build(loanID.toString());
final HashMap<String, String> map = new HashMap<>();
map.put("locale", "en");
map.put("dateFormat", "dd MMMM yyyy");
map.put("approvedOnDate", "11 June 2023");
final String aproveRequestJSON = new Gson().toJson(map);
Integer loanRescheduleRequestId = this.loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
this.loanRescheduleRequestHelper.approveLoanRescheduleRequest(loanRescheduleRequestId, aproveRequestJSON);
Integer penalty1LoanChargeId = loanTransactionHelper.addChargesForLoan(loanID,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), "13 July 2023", "3.65"));
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("2023.06.12").dateFormat("yyyy.MM.dd").locale("en"));
inlineLoanCOBHelper.executeInlineCOB(List.of(loanID.longValue()));
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails((long) loanID);
assertEquals(131.6, loanDetails.getSummary().getTotalOutstanding());
assertEquals(131.6, loanDetails.getRepaymentSchedule().getTotalOutstanding());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
assertTrue(loanDetails.getStatus().getActive());
assertNull(loanDetails.getTransactions().get(0).getReversedOnDate());
assertTrue(loanDetails.getTransactions().get(0).getTransactionRelations().isEmpty());
assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement());
assertEquals(127.95, loanDetails.getTransactions().get(0).getAmount());
assertEquals(0.0, loanDetails.getTransactions().get(0).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(0).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(0).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(0).getInterestPortion());
assertEquals(0.0, loanDetails.getTransactions().get(0).getFeeChargesPortion());
assertEquals(127.95, loanDetails.getTransactions().get(0).getOutstandingLoanBalance());
assertNull(loanDetails.getTransactions().get(1).getReversedOnDate());
assertTrue(loanDetails.getTransactions().get(1).getTransactionRelations().isEmpty());
assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual());
assertEquals(3.65, loanDetails.getTransactions().get(1).getAmount());
assertEquals(0.0, loanDetails.getTransactions().get(1).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(1).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(1).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(1).getInterestPortion());
assertEquals(3.65, loanDetails.getTransactions().get(1).getFeeChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(1).getOutstandingLoanBalance());
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("2023.06.17").dateFormat("yyyy.MM.dd").locale("en"));
PostLoansLoanIdTransactionsResponse merchantIssuedRefund1 = loanTransactionHelper.makeMerchantIssuedRefund(Long.valueOf(loanID),
new PostLoansLoanIdTransactionsRequest().locale("en").dateFormat("dd MMMM yyyy").transactionDate("17 June 2023")
.transactionAmount(125.0));
loanDetails = loanTransactionHelper.getLoanDetails((long) loanID);
assertEquals(6.6, loanDetails.getSummary().getTotalOutstanding());
assertEquals(6.6, loanDetails.getRepaymentSchedule().getTotalOutstanding());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
assertEquals(125.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
assertEquals(2.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
assertTrue(loanDetails.getStatus().getActive());
assertNull(loanDetails.getTransactions().get(2).getReversedOnDate());
assertTrue(loanDetails.getTransactions().get(2).getTransactionRelations().isEmpty());
assertTrue(loanDetails.getTransactions().get(2).getType().getMerchantIssuedRefund());
assertEquals(125.0, loanDetails.getTransactions().get(2).getAmount());
assertEquals(125.0, loanDetails.getTransactions().get(2).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(2).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(2).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(2).getInterestPortion());
assertEquals(0.0, loanDetails.getTransactions().get(2).getFeeChargesPortion());
assertEquals(2.95, loanDetails.getTransactions().get(2).getOutstandingLoanBalance());
PostLoansLoanIdChargesChargeIdResponse chargeAdjustmentResponse = this.loanTransactionHelper.chargeAdjustment((long) loanID,
(long) penalty1LoanChargeId, new PostLoansLoanIdChargesChargeIdRequest().amount(3.65));
loanDetails = loanTransactionHelper.getLoanDetails((long) loanID);
assertEquals(2.95, loanDetails.getSummary().getTotalOutstanding());
assertEquals(2.95, loanDetails.getRepaymentSchedule().getTotalOutstanding());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
assertEquals(0.70, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
assertEquals(2.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
assertTrue(loanDetails.getStatus().getActive());
assertNull(loanDetails.getTransactions().get(3).getReversedOnDate());
assertFalse(loanDetails.getTransactions().get(3).getTransactionRelations().isEmpty());
assertEquals((long) penalty1LoanChargeId,
loanDetails.getTransactions().get(3).getTransactionRelations().iterator().next().getToLoanCharge());
assertTrue(loanDetails.getTransactions().get(3).getType().getChargeAdjustment());
assertEquals(3.65, loanDetails.getTransactions().get(3).getAmount());
assertEquals(2.95, loanDetails.getTransactions().get(3).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(3).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(3).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(3).getInterestPortion());
assertEquals(0.7, loanDetails.getTransactions().get(3).getFeeChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(3).getOutstandingLoanBalance());
PostLoansLoanIdTransactionsResponse merchantIssuedRefund2 = loanTransactionHelper.makeMerchantIssuedRefund(Long.valueOf(loanID),
new PostLoansLoanIdTransactionsRequest().locale("en").dateFormat("dd MMMM yyyy").transactionDate("17 June 2023")
.transactionAmount(2.95));
loanDetails = loanTransactionHelper.getLoanDetails((long) loanID);
assertEquals(0.0, loanDetails.getSummary().getTotalOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getTotalOutstanding());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
assertEquals(3.65, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
assertEquals(127.95, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
assertNull(loanDetails.getTransactions().get(4).getReversedOnDate());
assertTrue(loanDetails.getTransactions().get(4).getTransactionRelations().isEmpty());
assertTrue(loanDetails.getTransactions().get(4).getType().getMerchantIssuedRefund());
assertEquals(2.95, loanDetails.getTransactions().get(4).getAmount());
assertEquals(0.0, loanDetails.getTransactions().get(4).getPrincipalPortion());
assertEquals(0.0, loanDetails.getTransactions().get(4).getPenaltyChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(4).getOverpaymentPortion());
assertEquals(0.0, loanDetails.getTransactions().get(4).getInterestPortion());
assertEquals(2.95, loanDetails.getTransactions().get(4).getFeeChargesPortion());
assertEquals(0.0, loanDetails.getTransactions().get(4).getOutstandingLoanBalance());
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(requestSpec, responseSpec, "due-date");
}
}
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID, final String principal,
final String loanTermFrequency, final String repaymentAfterEvery, final String numberOfRepayments, final String interestRate,
final String repaymentStrategy, final String expectedDisbursementDate, final String submittedOnDate) {
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principal)
.withLoanTermFrequency(loanTermFrequency).withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments)
.withRepaymentEveryAfter(repaymentAfterEvery).withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod(interestRate)
.withLoanTermFrequency(loanTermFrequency).withLoanTermFrequencyAsDays().withNumberOfRepayments(numberOfRepayments)
.withRepaymentEveryAfter(repaymentAfterEvery).withRepaymentFrequencyTypeAsDays().withInterestRatePerPeriod(interestRate)
.withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments().withRepaymentStrategy(repaymentStrategy)
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate(expectedDisbursementDate)
.withSubmittedOnDate(submittedOnDate).withLoanType("individual").build(clientID.toString(), loanProductID.toString(), null);
@@ -1413,8 +1808,8 @@ public class DueDateRespectiveLoanRepaymentScheduleTest {
private Integer createLoanProductWithNoAccountingNoInterest(final String principal, final String repaymentAfterEvery,
final String numberOfRepayments, final String interestRate, final String repaymentStrategy, final Account... accounts) {
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(principal).withRepaymentTypeAsMonth()
.withRepaymentAfterEvery(repaymentAfterEvery).withNumberOfRepayments(numberOfRepayments).withRepaymentTypeAsMonth()
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(principal).withRepaymentTypeAsDays()
.withRepaymentAfterEvery(repaymentAfterEvery).withNumberOfRepayments(numberOfRepayments)
.withinterestRatePerPeriod(interestRate).withInterestRateFrequencyTypeAsMonths().withRepaymentStrategy(repaymentStrategy)
.withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRulePeriodicAccrual(accounts)
.withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", "0").build(null);