Revert "FINERACT-1905: Same day repayment + charge handling improvement"

This reverts commit 15838e5406

Except LoanCharge and liquibase changes. Those are unrelated. not causing any issues, and better to leave them for compatibility
This commit is contained in:
Adam Saghy
2023-08-07 21:17:21 +02:00
parent e29714a728
commit d8882ac471
9 changed files with 1140 additions and 1199 deletions
@@ -5741,7 +5741,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final List<LoanTransaction> copyTransactions = new ArrayList<>();
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
copyTransactions.add(LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction));
copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction));
}
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), copyTransactions, getCurrency(),
getRepaymentScheduleInstallments(), getActiveCharges());
@@ -269,13 +269,11 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
return new LoanTransaction(null, office, LoanTransactionType.REFUND, paymentDetail, amount.getAmount(), paymentDate, externalId);
}
public static LoanTransaction copyTransactionPropertiesForReprocessing(final LoanTransaction loanTransaction) {
LoanTransaction loanTransactionCopy = new LoanTransaction(loanTransaction.loan, loanTransaction.office, loanTransaction.typeOf,
loanTransaction.dateOf, loanTransaction.amount, loanTransaction.principalPortion, loanTransaction.interestPortion,
public static LoanTransaction copyTransactionProperties(final LoanTransaction loanTransaction) {
return new LoanTransaction(loanTransaction.loan, loanTransaction.office, loanTransaction.typeOf, loanTransaction.dateOf,
loanTransaction.amount, loanTransaction.principalPortion, loanTransaction.interestPortion,
loanTransaction.feeChargesPortion, loanTransaction.penaltyChargesPortion, loanTransaction.overPaymentPortion,
loanTransaction.reversed, loanTransaction.paymentDetail, loanTransaction.externalId);
loanTransactionCopy.setCreatedDate(loanTransaction.getCreatedDateTime());
return loanTransactionCopy;
}
public static LoanTransaction accrueLoanCharge(final Loan loan, final Office office, final Money amount, final LocalDate applyDate,
@@ -175,7 +175,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
* For existing transactions, check if the re-payment breakup (principal, interest, fees, penalties)
* has changed.<br>
**/
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction);
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
// Reset derived component of new loan transaction and
// re-process transaction
@@ -214,7 +214,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction,
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments) {
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction);
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
newLoanTransaction.resetDerivedComponents();
// determine how much is outstanding total and breakdown for principal, interest and charges
Money principalPortion = Money.zero(currency);
@@ -280,7 +280,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
if (loanTransaction.getId() == null) {
return;
}
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction);
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
List<LoanTransaction> mergedList = getMergedTransactionList(transactionsToBeProcessed, changedTransactionDetail);
Money overpaidAmount = calculateOverpaidAmount(loanTransaction, mergedList, installments, currency);
@@ -61,81 +61,9 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
*/
@Override
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
Set<LoanCharge> charges) {
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
charges);
}
/**
* 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 doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
charges);
}
/**
* For normal on-time repayments
*/
@Override
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
charges);
}
@Override
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
// TODO - KW - dont do anything with loan over-payment for now
}
@Override
protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
Money transactionAmountRemaining = transactionAmountUnprocessed;
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
Money penaltyChargesPortion = Money.zero(currency);
principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
if (transactionAmountRemaining.isGreaterThanZero()) {
interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
}
if (transactionAmountRemaining.isGreaterThanZero()) {
feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
}
if (transactionAmountRemaining.isGreaterThanZero()) {
penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
}
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;
}
private static Money doTransactionAmountAllocation(LoanRepaymentScheduleInstallment currentInstallment, LoanTransaction loanTransaction,
Money paymentInAdvance, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
@@ -175,8 +103,6 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
.filter(loanCharge -> loanCharge.getCreatedDate().isEmpty() || loanTransaction.getCreatedDate().isEmpty()
|| !loanCharge.getCreatedDate().get().isAfter(loanTransaction.getCreatedDate().get()))
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
Money calculatedPenaltyCharge = Money.zero(currency);
Money calculatedFeeCharge = Money.zero(currency);
@@ -239,4 +165,126 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
}
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;
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 subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
feeChargesPortion = feeChargesPortion.add(subFeePortion);
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);
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
}
@Override
protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
Money transactionAmountRemaining = transactionAmountUnprocessed;
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
Money penaltyChargesPortion = Money.zero(currency);
principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
if (transactionAmountRemaining.isGreaterThanZero()) {
interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
}
if (transactionAmountRemaining.isGreaterThanZero()) {
feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
}
if (transactionAmountRemaining.isGreaterThanZero()) {
penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
}
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;
}
}
@@ -61,81 +61,8 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
*/
@Override
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
Set<LoanCharge> charges) {
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
charges);
}
/**
* 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 doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
charges);
}
/**
* For normal on-time repayments
*/
@Override
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money inAdvancePayment,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
charges);
}
@Override
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
// TODO - KW - dont do anything with loan over-payment for now
}
@Override
protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
Money transactionAmountRemaining = transactionAmountUnprocessed;
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
Money penaltyChargesPortion = Money.zero(currency);
principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
if (transactionAmountRemaining.isGreaterThanZero()) {
interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
}
if (transactionAmountRemaining.isGreaterThanZero()) {
feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
}
if (transactionAmountRemaining.isGreaterThanZero()) {
penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
}
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;
}
private static Money doTransactionAmountAllocation(LoanRepaymentScheduleInstallment currentInstallment, LoanTransaction loanTransaction,
Money inAdvancePayment, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = inAdvancePayment.getCurrency();
@@ -176,8 +103,6 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
.filter(loanCharge -> loanCharge.getCreatedDate().isEmpty() || loanTransaction.getCreatedDate().isEmpty()
|| loanCharge.getCreatedDate().get().isBefore(loanTransaction.getCreatedDate().get()))
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
Money calculatedPenaltyCharge = Money.zero(currency);
Money calculatedFeeCharge = Money.zero(currency);
@@ -243,4 +168,125 @@ 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
}
@Override
protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
Money transactionAmountRemaining = transactionAmountUnprocessed;
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
Money penaltyChargesPortion = Money.zero(currency);
principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
if (transactionAmountRemaining.isGreaterThanZero()) {
interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
}
if (transactionAmountRemaining.isGreaterThanZero()) {
feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
}
if (transactionAmountRemaining.isGreaterThanZero()) {
penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
}
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;
}
}
@@ -2098,7 +2098,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
for (LoanTransaction loanTransaction : transactions) {
if (loanTransaction.isPaymentTransaction()) {
recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(),
LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction)));
LoanTransaction.copyTransactionProperties(loanTransaction)));
}
}
final boolean applyInterestRecalculation = loanApplicationTerms.isInterestRecalculationEnabled();
@@ -6802,8 +6802,7 @@ public class ClientLoanIntegrationTest {
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY);
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(new PostChargesRequest().penalty(true).amount(10.0)
@@ -6863,337 +6862,6 @@ public class ClientLoanIntegrationTest {
}
}
@Test
public void testLoanTransactionOrderAndRepaymentAllocationWithSameDayChargeBeforeRepayment() {
try {
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
repaymentStrategyCode);
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
new PostChargesRequest().penalty(true).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
.chargeAppliesTo(1).locale("en").active(true));
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
.approvedOnDate("01 January 2023").locale("en"));
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
String loanDisbursementUUID = UUID.randomUUID().toString();
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
Long loanId = disbursedLoanResult.getResourceId();
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("07 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
.locale("en").amount(10.0).dueDate("10 January 2023"));
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
String firstRepaymentUUID = UUID.randomUUID().toString();
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("07 January 2023")
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 0.0, 0.0, 0.0, 9.0, 0.0);
}
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
}
}
@Test
public void testLoanTransactionOrderAndRepaymentAllocationWithSameDayChargeAfterRepayment() {
try {
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
repaymentStrategyCode);
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
new PostChargesRequest().penalty(false).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
.chargeAppliesTo(1).locale("en").active(true));
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
.approvedOnDate("01 January 2023").locale("en"));
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
String loanDisbursementUUID = UUID.randomUUID().toString();
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
Long loanId = disbursedLoanResult.getResourceId();
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("07 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
String firstRepaymentUUID = UUID.randomUUID().toString();
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("07 January 2023")
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
.locale("en").amount(10.0).dueDate("10 January 2023"));
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
}
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
}
}
@Test
public void testLoanTransactionOrderAndRepaymentAllocationWithSameDayChargeBeforeRepaymentOnInstallmentDueDate() {
try {
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
LOG.info("-----------------------------------REPAYMENT SCEDULE CODE : {}-----------------------------------------",
repaymentStrategyCode);
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
repaymentStrategyCode);
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
new PostChargesRequest().penalty(true).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
.chargeAppliesTo(1).locale("en").active(true));
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
.approvedOnDate("01 January 2023").locale("en"));
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
String loanDisbursementUUID = UUID.randomUUID().toString();
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
Long loanId = disbursedLoanResult.getResourceId();
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("31 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
.locale("en").amount(10.0).dueDate("31 January 2023"));
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
String firstRepaymentUUID = UUID.randomUUID().toString();
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("31 January 2023")
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 0.0, 0.0, 0.0, 9.0, 0.0);
}
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
}
}
@Test
public void testLoanTransactionOrderAndRepaymentAllocationWithSameDayChargeAfterRepaymentOnInstallmentDueDate() {
try {
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
LOG.info("-----------------------------------REPAYMENT SCEDULE CODE : {}-----------------------------------------",
repaymentStrategyCode);
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
repaymentStrategyCode);
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
new PostChargesRequest().penalty(true).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
.chargeAppliesTo(1).locale("en").active(true));
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
.approvedOnDate("01 January 2023").locale("en"));
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
String loanDisbursementUUID = UUID.randomUUID().toString();
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
Long loanId = disbursedLoanResult.getResourceId();
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("31 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
String firstRepaymentUUID = UUID.randomUUID().toString();
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("31 January 2023")
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
.locale("en").amount(10.0).dueDate("31 January 2023"));
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
}
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
}
}
@Test
public void testLoanTransactionOrderAndRepaymentAllocationMultipleActions() {
try {
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
repaymentStrategyCode);
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
new PostChargesRequest().penalty(false).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
.chargeAppliesTo(1).locale("en").active(true));
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
.approvedOnDate("01 January 2023").locale("en"));
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
String loanDisbursementUUID = UUID.randomUUID().toString();
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
Long loanId = disbursedLoanResult.getResourceId();
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
.date("07 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
String firstRepaymentUUID = UUID.randomUUID().toString();
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("07 January 2023")
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
.locale("en").amount(10.0).dueDate("07 January 2023"));
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
String secondRepaymentUUID = UUID.randomUUID().toString();
PostLoansLoanIdTransactionsResponse secondRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("06 January 2023")
.locale("en").transactionAmount(9.0).externalId(secondRepaymentUUID));
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, secondRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
String thirdRepaymentUUID = UUID.randomUUID().toString();
PostLoansLoanIdTransactionsResponse thirdRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("07 January 2023")
.locale("en").transactionAmount(9.0).externalId(thirdRepaymentUUID));
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, thirdRepaymentResult.getResourceExternalId(), 9.0, 0.0, 0.0, 9.0, 0.0, 0.0);
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, secondRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
}
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
}
}
private void checkLoanTransactionAllocation(Long loanId, String transactionExternalId, double amount, double principalPortion,
double interestPortion, double feePortion, double penaltyPortion, double overpaymentPortion) {
GetLoansLoanIdTransactionsTransactionIdResponse transactionDetails = LOAN_TRANSACTION_HELPER.getLoanTransactionDetails(loanId,
transactionExternalId);
assertEquals(amount, transactionDetails.getAmount());
assertEquals(principalPortion, transactionDetails.getPrincipalPortion());
assertEquals(interestPortion, transactionDetails.getInterestPortion());
assertEquals(feePortion, transactionDetails.getFeeChargesPortion());
assertEquals(penaltyPortion, transactionDetails.getPenaltyChargesPortion());
assertEquals(overpaymentPortion, transactionDetails.getOverpaymentPortion());
}
private void checkLoanTransactionOrder(Long loanId, String... transactionUUIDs) {
LOG.info("-------------------------------CHECK LOAN TRANSACTION ORDER-------------------------------------------");
GetLoansLoanIdResponse loanDetailsResult = LOAN_TRANSACTION_HELPER.getLoanDetails(loanId);
@@ -8355,7 +8023,7 @@ public class ClientLoanIntegrationTest {
.principal(BigDecimal.valueOf(1000.0)).loanType("individual"));
}
private PostLoanProductsRequest createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(String repaymentStrategyCode) {
private PostLoanProductsRequest createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct() {
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6))//
.shortName(Utils.uniqueRandomStringGenerator("", 4))//
.description("Loan Product Description")//
@@ -8382,7 +8050,8 @@ public class ClientLoanIntegrationTest {
.interestType(0)//
.isEqualAmortization(false)//
.interestCalculationPeriodType(1)//
.transactionProcessingStrategyCode(repaymentStrategyCode)//
.transactionProcessingStrategyCode(
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY)//
.daysInYearType(1)//
.daysInMonthType(1)//
.canDefineInstallmentAmount(true)//