FINERACT-1724: Fee's are not included in total balance amount, when we have multiple disbursements on first day

This commit is contained in:
Adam Saghy
2023-05-03 10:27:25 +02:00
parent a29f95414e
commit 629d2da7f9
9 changed files with 167 additions and 148 deletions
@@ -655,7 +655,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
getId(), loanCharge.name());
}
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate(false));
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate());
loanCharge.update(this);
@@ -770,18 +770,18 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategyCode);
final List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<>();
LocalDate startDate = getDisbursementDate();
List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments();
for (final LoanRepaymentScheduleInstallment installment : installments) {
if (installmentNumber == null && charge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())) {
boolean isDue = installment.isFirstPeriod()
? charge.isDueForCollectionFromIncludingAndUpToAndIncluding(installment.getFromDate(), installment.getDueDate())
: charge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), installment.getDueDate());
if (installmentNumber == null && isDue) {
chargePaymentInstallments.add(installment);
break;
} else if (installmentNumber != null && installment.getInstallmentNumber().equals(installmentNumber)) {
chargePaymentInstallments.add(installment);
break;
}
startDate = installment.getDueDate();
}
final Set<LoanCharge> loanCharges = new HashSet<>(1);
loanCharges.add(charge);
@@ -808,13 +808,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
}
}
private void validateChargeHasValidSpecifiedDateIfApplicable(final LoanCharge loanCharge, final LocalDate disbursementDate,
final LocalDate lastRepaymentPeriodDueDate) {
if (isInterestBearing() && loanCharge.isSpecifiedDueDate()
&& !loanCharge.isDueForCollectionFromAndUpToAndIncluding(disbursementDate, lastRepaymentPeriodDueDate)) {
private void validateChargeHasValidSpecifiedDateIfApplicable(final LoanCharge loanCharge, final LocalDate disbursementDate) {
if (loanCharge.isSpecifiedDueDate() && loanCharge.getDueLocalDate().isBefore(disbursementDate)) {
final String defaultUserMessage = "This charge with specified due date cannot be added as the it is not in schedule range.";
throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage,
getDisbursementDate(), lastRepaymentPeriodDueDate, loanCharge.name());
getDisbursementDate(), loanCharge.name());
}
}
@@ -1745,7 +1743,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
if (loanCharge.isActive()) {
clearLoanInstallmentChargesBeforeRegeneration(loanCharge);
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, fetchNumberOfInstallmensAfterExceptions(), totalChargeAmt);
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate(false));
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate());
}
}
@@ -5560,7 +5558,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
List<LoanCharge> loanCharges = new ArrayList<>();
List<LoanInstallmentCharge> loanInstallmentCharges = new ArrayList<>();
for (LoanCharge loanCharge : this.getActiveCharges()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(fromDate, toDate)) {
boolean isDue = fromDate.isEqual(this.getDisbursementDate())
? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(fromDate, toDate)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(fromDate, toDate);
if (isDue) {
if (loanCharge.isPenaltyCharge() && !loanCharge.isInstalmentFee()) {
penalties = penalties.add(loanCharge.amount());
loanCharges.add(loanCharge);
@@ -6569,7 +6570,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
interestAccountedForCurrentPeriod = installment.getInterestWaived(getCurrency()).plus(installment.getInterestPaid(getCurrency()));
for (LoanCharge loanCharge : this.charges) {
if (loanCharge.isActive() && !loanCharge.isDueAtDisbursement()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), paymentDate)) {
boolean isDue = installment.isFirstPeriod()
? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(installment.getFromDate(), paymentDate)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), paymentDate);
if (isDue) {
if (loanCharge.isPenaltyCharge()) {
penaltyForCurrentPeriod = penaltyForCurrentPeriod.plus(loanCharge.getAmount(getCurrency()));
penaltyAccoutedForCurrentPeriod = penaltyAccoutedForCurrentPeriod
@@ -641,7 +641,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
}
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), chargesTillDate)) {
boolean isDue = installment.isFirstPeriod()
? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(installment.getFromDate(), chargesTillDate)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), chargesTillDate);
if (isDue) {
if (loanCharge.isFeeCharge()) {
dueDateFeeIncome = dueDateFeeIncome.add(loanCharge.amount());
} else if (loanCharge.isPenaltyCharge()) {
@@ -792,9 +795,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
loan.addLoanTransaction(accrualTransaction);
Set<LoanChargePaidBy> accrualCharges = accrualTransaction.getLoanChargesPaid();
for (LoanCharge loanCharge : loan.getActiveCharges()) {
if (loanCharge.isActive() && !loanCharge.isPaid()
&& (loanCharge.isDueForCollectionFromAndUpToAndIncluding(fromDate, foreClosureDate)
|| loanCharge.isInstalmentFee())) {
boolean isDue = fromDate.isEqual(loan.getDisbursementDate())
? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(fromDate, foreClosureDate)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(fromDate, foreClosureDate);
if (loanCharge.isActive() && !loanCharge.isPaid() && (isDue || loanCharge.isInstalmentFee())) {
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(accrualTransaction, loanCharge,
loanCharge.getAmountOutstanding(currency).getAmount(), null);
accrualCharges.add(loanChargePaidBy);
@@ -625,14 +625,25 @@ public class LoanCharge extends AbstractPersistableCustom {
public boolean isDueForCollectionFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) {
final LocalDate dueDate = getDueLocalDate();
return occursOnDayFromAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, dueDate);
return occursOnDayFromExclusiveAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, dueDate);
}
private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
public boolean isDueForCollectionFromIncludingAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive) {
final LocalDate dueDate = getDueLocalDate();
return occursOnDayFromAndUpToAndIncluding(fromAndInclusive, upToAndInclusive, dueDate);
}
private boolean occursOnDayFromExclusiveAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
final LocalDate target) {
return target != null && target.isAfter(fromNotInclusive) && !target.isAfter(upToAndInclusive);
}
private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive,
final LocalDate target) {
return target != null && (target.isEqual(fromAndInclusive) || target.isAfter(fromAndInclusive))
&& !target.isAfter(upToAndInclusive);
}
public boolean isFeeCharge() {
return !this.penaltyCharge;
}
@@ -47,14 +47,14 @@ public class LoanRepaymentScheduleProcessingWrapper {
final Money feeChargesWaivedForRepaymentPeriod = cumulativeFeeChargesWaivedWithin(startDate, period.getDueDate(), loanCharges,
currency, !period.isRecalculatedInterestComponent(), period.isFirstPeriod());
final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(),
loanCharges, currency, !period.isRecalculatedInterestComponent());
loanCharges, currency, !period.isRecalculatedInterestComponent(), period.isFirstPeriod());
final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(), loanCharges,
currency, period, totalPrincipal, totalInterest, !period.isRecalculatedInterestComponent());
final Money penaltyChargesWaivedForRepaymentPeriod = cumulativePenaltyChargesWaivedWithin(startDate, period.getDueDate(),
loanCharges, currency, !period.isRecalculatedInterestComponent(), period.isFirstPeriod());
final Money penaltyChargesWrittenOffForRepaymentPeriod = cumulativePenaltyChargesWrittenOffWithin(startDate,
period.getDueDate(), loanCharges, currency, !period.isRecalculatedInterestComponent());
period.getDueDate(), loanCharges, currency, !period.isRecalculatedInterestComponent(), period.isFirstPeriod());
period.updateChargePortion(feeChargesDueForRepaymentPeriod, feeChargesWaivedForRepaymentPeriod,
feeChargesWrittenOffForRepaymentPeriod, penaltyChargesDueForRepaymentPeriod, penaltyChargesWaivedForRepaymentPeriod,
@@ -71,6 +71,9 @@ public class LoanRepaymentScheduleProcessingWrapper {
Money cumulative = Money.zero(monetaryCurrency);
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isFeeCharge() && !loanCharge.isDueAtDisbursement()) {
boolean isDue = period.isFirstPeriod()
? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd);
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
if (loanCharge.getChargeCalculation().isPercentageBased()) {
BigDecimal amount = BigDecimal.ZERO;
@@ -87,12 +90,9 @@ public class LoanRepaymentScheduleProcessingWrapper {
} else {
cumulative = cumulative.plus(loanCharge.amountOrPercentage());
}
} else if (loanCharge.isOverdueInstallmentCharge()
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
&& loanCharge.getChargeCalculation().isPercentageBased()) {
} else if (loanCharge.isOverdueInstallmentCharge() && isDue && loanCharge.getChargeCalculation().isPercentageBased()) {
cumulative = cumulative.plus(loanCharge.chargeAmount());
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
&& loanCharge.getChargeCalculation().isPercentageBased()) {
} else if (isDue && loanCharge.getChargeCalculation().isPercentageBased()) {
BigDecimal amount = BigDecimal.ZERO;
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
amount = amount.add(totalPrincipal.getAmount()).add(totalInterest.getAmount());
@@ -116,10 +116,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
}
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
cumulative = cumulative.plus(loanChargeAmt);
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
cumulative = cumulative.plus(loanCharge.amount());
// Special case for Loan Charges (Due Date) added the same disbursement date
} else if (period.isFirstPeriod() && periodStart.equals(loanCharge.getDueDate())) {
} else if (isDue) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -136,15 +133,14 @@ public class LoanRepaymentScheduleProcessingWrapper {
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isFeeCharge() && !loanCharge.isDueAtDisbursement()) {
boolean isDue = isFirstPeriod ? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd);
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
if (loanChargePerInstallment != null) {
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWaived(currency));
}
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
// Special case for Loan Charges (Due Date) added the same disbursement date
} else if (isFirstPeriod && periodStart.equals(loanCharge.getDueDate())) {
} else if (isDue) {
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
}
}
@@ -154,18 +150,21 @@ public class LoanRepaymentScheduleProcessingWrapper {
}
private Money cumulativeFeeChargesWrittenOffWithin(final LocalDate periodStart, final LocalDate periodEnd,
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable) {
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable,
boolean isFirstPeriod) {
Money cumulative = Money.zero(currency);
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isFeeCharge() && !loanCharge.isDueAtDisbursement()) {
boolean isDue = isFirstPeriod ? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd);
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
if (loanChargePerInstallment != null) {
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWrittenOff(currency));
}
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
} else if (isDue) {
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
}
}
@@ -182,6 +181,9 @@ public class LoanRepaymentScheduleProcessingWrapper {
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isPenaltyCharge()) {
boolean isDue = period.isFirstPeriod()
? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd);
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
if (loanCharge.getChargeCalculation().isPercentageBased()) {
BigDecimal amount = BigDecimal.ZERO;
@@ -198,12 +200,9 @@ public class LoanRepaymentScheduleProcessingWrapper {
} else {
cumulative = cumulative.plus(loanCharge.amountOrPercentage());
}
} else if (loanCharge.isOverdueInstallmentCharge()
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
&& loanCharge.getChargeCalculation().isPercentageBased()) {
} else if (loanCharge.isOverdueInstallmentCharge() && isDue && loanCharge.getChargeCalculation().isPercentageBased()) {
cumulative = cumulative.plus(loanCharge.chargeAmount());
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
&& loanCharge.getChargeCalculation().isPercentageBased()) {
} else if (isDue && loanCharge.getChargeCalculation().isPercentageBased()) {
BigDecimal amount = BigDecimal.ZERO;
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
amount = amount.add(totalPrincipal.getAmount()).add(totalInterest.getAmount());
@@ -214,10 +213,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
}
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
cumulative = cumulative.plus(loanChargeAmt);
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
cumulative = cumulative.plus(loanCharge.amount());
// Special case for Loan Charges (Due Date) added the same disbursement date
} else if (period.isFirstPeriod() && periodStart.equals(loanCharge.getDueDate())) {
} else if (isDue) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -234,15 +230,14 @@ public class LoanRepaymentScheduleProcessingWrapper {
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isPenaltyCharge()) {
boolean isDue = isFirstPeriod ? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd);
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
if (loanChargePerInstallment != null) {
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWaived(currency));
}
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
// Special case for Loan Charges (Due Date) added the same disbursement date
} else if (isFirstPeriod && periodStart.equals(loanCharge.getDueDate())) {
} else if (isDue) {
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
}
}
@@ -252,18 +247,21 @@ public class LoanRepaymentScheduleProcessingWrapper {
}
private Money cumulativePenaltyChargesWrittenOffWithin(final LocalDate periodStart, final LocalDate periodEnd,
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable) {
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable,
final boolean isFirstPeriod) {
Money cumulative = Money.zero(currency);
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isPenaltyCharge()) {
boolean isDue = isFirstPeriod ? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd);
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
if (loanChargePerInstallment != null) {
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWrittenOff(currency));
}
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
} else if (isDue) {
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
}
}
@@ -110,10 +110,10 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
LocalDate startDate = disbursementDate;
for (final LoanRepaymentScheduleInstallment installment : installments) {
for (final LoanCharge loanCharge : transferCharges) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())
// Special case for Loan Charges (Due Date) added the same disbursement date
|| (startDate.equals(disbursementDate) && loanCharge.getDueDate() != null
&& loanCharge.getDueDate().equals(disbursementDate))) {
boolean isDue = installment.isFirstPeriod()
? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(startDate, installment.getDueDate())
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate());
if (isDue) {
Money amountForProcess = loanCharge.getAmount(currency);
if (amountForProcess.isGreaterThan(loanTransaction.getAmount(currency))) {
amountForProcess = loanTransaction.getAmount(currency);
@@ -477,4 +477,8 @@ public final class LoanScheduleParams {
public void setUnCompoundedAmount(Money unCompoundedAmount) {
this.unCompoundedAmount = unCompoundedAmount;
}
public boolean isFirstPeriod() {
return 1 == instalmentNumber;
}
}
@@ -437,12 +437,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
LoanScheduleParams scheduleParams, LocalDate scheduledDueDate, ScheduleCurrentPeriodParams currentPeriodParams) {
PrincipalInterest principalInterest = new PrincipalInterest(currentPeriodParams.getPrincipalForThisPeriod(),
currentPeriodParams.getInterestForThisPeriod(), null);
currentPeriodParams.setFeeChargesForInstallment(
cumulativeFeeChargesDueWithin(scheduleParams.getPeriodStartDate(), scheduledDueDate, loanCharges, currency,
principalInterest, scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativeInterest(), true));
currentPeriodParams.setPenaltyChargesForInstallment(
cumulativePenaltyChargesDueWithin(scheduleParams.getPeriodStartDate(), scheduledDueDate, loanCharges, currency,
principalInterest, scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativeInterest(), true));
currentPeriodParams.setFeeChargesForInstallment(cumulativeFeeChargesDueWithin(scheduleParams.getPeriodStartDate(), scheduledDueDate,
loanCharges, currency, principalInterest, scheduleParams.getPrincipalToBeScheduled(),
scheduleParams.getTotalCumulativeInterest(), true, scheduleParams.isFirstPeriod()));
currentPeriodParams.setPenaltyChargesForInstallment(cumulativePenaltyChargesDueWithin(scheduleParams.getPeriodStartDate(),
scheduledDueDate, loanCharges, currency, principalInterest, scheduleParams.getPrincipalToBeScheduled(),
scheduleParams.getTotalCumulativeInterest(), true, scheduleParams.isFirstPeriod()));
scheduleParams.addTotalFeeChargesCharged(currentPeriodParams.getFeeChargesForInstallment());
scheduleParams.addTotalPenaltyChargesCharged(currentPeriodParams.getPenaltyChargesForInstallment());
}
@@ -456,11 +456,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest,
scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativeInterest(),
!loanScheduleModelPeriod.isRecalculatedInterestComponent());
!loanScheduleModelPeriod.isRecalculatedInterestComponent(), scheduleParams.isFirstPeriod());
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest,
scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativeInterest(),
!loanScheduleModelPeriod.isRecalculatedInterestComponent());
!loanScheduleModelPeriod.isRecalculatedInterestComponent(), scheduleParams.isFirstPeriod());
scheduleParams.addTotalFeeChargesCharged(feeChargesForInstallment);
scheduleParams.addTotalPenaltyChargesCharged(penaltyChargesForInstallment);
scheduleParams.addTotalRepaymentExpected(feeChargesForInstallment.plus(penaltyChargesForInstallment));
@@ -561,10 +561,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
currentPeriodParams.getInterestForThisPeriod(), null);
oustanding = oustanding.minus(cumulativeFeeChargesDueWithin(transactionDate, scheduledDueDate, loanCharges,
totalInterestChargedForFullLoanTerm.getCurrency(), tempPrincipalInterest, scheduleParams.getPrincipalToBeScheduled(),
scheduleParams.getTotalCumulativeInterest(), true));
scheduleParams.getTotalCumulativeInterest(), true, scheduleParams.isFirstPeriod()));
oustanding = oustanding.minus(cumulativePenaltyChargesDueWithin(transactionDate, scheduledDueDate, loanCharges,
totalInterestChargedForFullLoanTerm.getCurrency(), tempPrincipalInterest, scheduleParams.getPrincipalToBeScheduled(),
scheduleParams.getTotalCumulativeInterest(), true));
scheduleParams.getTotalCumulativeInterest(), true, scheduleParams.isFirstPeriod()));
if (!oustanding.isGreaterThan(currentPeriodParams.getInterestForThisPeriod()) && !scheduledDueDate.equals(transactionDate)) {
final Collection<LoanTermVariationsData> interestRates = loanApplicationTerms.getLoanTermVariations().getInterestRateChanges();
@@ -717,7 +717,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
processTransactions.clear();
currentTransactions.addAll(createCurrentTransactionList(detail));
if (!transactionDate.isEqual(scheduleParams.getPeriodStartDate()) || scheduleParams.getInstalmentNumber() == 1) {
if (!transactionDate.isEqual(scheduleParams.getPeriodStartDate()) || scheduleParams.isFirstPeriod()) {
int periodDays = Math.toIntExact(ChronoUnit.DAYS.between(scheduleParams.getPeriodStartDate(), transactionDate));
// calculates period start date for interest
@@ -758,10 +758,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
null);
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(scheduleParams.getPeriodStartDate(),
transactionDate, loanCharges, currency, principalInterest, scheduleParams.getPrincipalToBeScheduled(),
scheduleParams.getTotalCumulativeInterest(), false);
scheduleParams.getTotalCumulativeInterest(), false, scheduleParams.isFirstPeriod());
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(scheduleParams.getPeriodStartDate(),
transactionDate, loanCharges, currency, principalInterest, scheduleParams.getPrincipalToBeScheduled(),
scheduleParams.getTotalCumulativeInterest(), false);
scheduleParams.getTotalCumulativeInterest(), false, scheduleParams.isFirstPeriod());
// sum up real totalInstallmentDue from
// components
@@ -1788,9 +1788,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
if (compoundingDate.isBefore(endDate)) {
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(lastCompoundingDate, compoundingDate, charges, currency,
null, loanApplicationTerms.getPrincipal(), null, false);
null, loanApplicationTerms.getPrincipal(), null, false, lastCompoundingDate.isEqual(startDate));
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(lastCompoundingDate, compoundingDate, charges,
currency, null, loanApplicationTerms.getPrincipal(), null, false);
currency, null, loanApplicationTerms.getPrincipal(), null, false, lastCompoundingDate.isEqual(startDate));
Money compoundAmount = feeChargesForInstallment.plus(penaltyChargesForInstallment);
if (addUncompounded) {
compoundAmount = compoundAmount.plus(scheduleParams.getUnCompoundedAmount());
@@ -1987,23 +1987,22 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable) {
final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, final boolean isFirstPeriod) {
Money cumulative = Money.zero(monetaryCurrency);
for (final LoanCharge loanCharge : loanCharges) {
if (!loanCharge.isDueAtDisbursement() && loanCharge.isFeeCharge()) {
boolean isDue = isFirstPeriod ? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd);
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
cumulative = calculateInstallmentCharge(principalInterestForThisPeriod, cumulative, loanCharge);
} else if (loanCharge.isOverdueInstallmentCharge()
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
&& loanCharge.getChargeCalculation().isPercentageBased()) {
} else if (loanCharge.isOverdueInstallmentCharge() && isDue && loanCharge.getChargeCalculation().isPercentageBased()) {
cumulative = cumulative.plus(loanCharge.chargeAmount());
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
&& loanCharge.getChargeCalculation().isPercentageBased()) {
} else if (isDue && loanCharge.getChargeCalculation().isPercentageBased()) {
cumulative = calculateSpecificDueDateChargeWithPercentage(principalDisbursed, totalInterestChargedForFullLoanTerm,
cumulative, loanCharge);
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
} else if (isDue) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -2050,23 +2049,22 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency,
final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable) {
final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, final boolean isFirstPeriod) {
Money cumulative = Money.zero(monetaryCurrency);
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isPenaltyCharge()) {
boolean isDue = isFirstPeriod ? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(periodStart, periodEnd)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd);
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
cumulative = calculateInstallmentCharge(principalInterestForThisPeriod, cumulative, loanCharge);
} else if (loanCharge.isOverdueInstallmentCharge()
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
&& loanCharge.getChargeCalculation().isPercentageBased()) {
} else if (loanCharge.isOverdueInstallmentCharge() && isDue && loanCharge.getChargeCalculation().isPercentageBased()) {
cumulative = cumulative.plus(loanCharge.chargeAmount());
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
&& loanCharge.getChargeCalculation().isPercentageBased()) {
} else if (isDue && loanCharge.getChargeCalculation().isPercentageBased()) {
cumulative = calculateSpecificDueDateChargeWithPercentage(principalDisbursed, totalInterestChargedForFullLoanTerm,
cumulative, loanCharge);
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
} else if (isDue) {
cumulative = cumulative.plus(loanCharge.amount());
}
}