diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 6b030c295..3773ff3b5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -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 chargePaymentInstallments = new ArrayList<>(); - LocalDate startDate = getDisbursementDate(); List 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 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 loanCharges = new ArrayList<>(); List 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 diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index b7a6e3dc8..b49497411 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -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 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); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java index eea10227a..905a8ae81 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java @@ -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; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java index f65f6c703..04ded2c13 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java @@ -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 loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable) { + final Set 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 loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable) { + final Set 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)); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index 6994ed8bd..72750cb1a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -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); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleParams.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleParams.java index c5c0bec83..0f28b88a7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleParams.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleParams.java @@ -477,4 +477,8 @@ public final class LoanScheduleParams { public void setUnCompoundedAmount(Money unCompoundedAmount) { this.unCompoundedAmount = unCompoundedAmount; } + + public boolean isFirstPeriod() { + return 1 == instalmentNumber; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index 796508e59..f6193dfe2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -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 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 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 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()); } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java index d52f64dc9..7e942b3fb 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java @@ -26,6 +26,7 @@ import io.restassured.builder.ResponseSpecBuilder; import io.restassured.http.ContentType; import io.restassured.specification.RequestSpecification; import io.restassured.specification.ResponseSpecification; +import java.math.BigDecimal; import java.time.LocalDate; import java.util.HashMap; import java.util.List; @@ -37,6 +38,7 @@ import org.apache.fineract.client.models.JournalEntryTransactionItem; import org.apache.fineract.client.models.PostChargesResponse; import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse; import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse; +import org.apache.fineract.client.models.PostLoansLoanIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.integrationtests.common.BusinessDateHelper; @@ -505,6 +507,64 @@ public class LoanChargeSpecificDueDateTest { } } + @Test + public void testApplyLoanSpecificDueDatePenaltyWithDisbursementDateWithMultipleDisbursement() { + + final LocalDate todaysDate = Utils.getLocalDateOfTenant(); + + // Client and Loan account creation + final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012"); + final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProductWithPeriodicAccrual(loanTransactionHelper, + null); + assertNotNull(getLoanProductsProductResponse); + + // Older date to have more than one overdue installment + LocalDate transactionDate = todaysDate; + String operationDate = Utils.dateFormatter.format(transactionDate); + log.info("Operation date {}", transactionDate); + + // Create Loan Account + final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(), + getLoanProductsProductResponse.getId().toString(), operationDate, "12", "0"); + + // Get loan details + GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId); + validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("0.00"), true); + + // Apply Loan Charge with specific due date + + final String feeAmount = "10.00"; + String payloadJSON = ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, feeAmount, true); + final PostChargesResponse postChargesResponse = ChargesHelper.createLoanCharge(requestSpec, responseSpec, payloadJSON); + assertNotNull(postChargesResponse); + final Long loanChargeId = postChargesResponse.getResourceId(); + assertNotNull(loanChargeId); + + payloadJSON = LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(loanChargeId.toString(), operationDate, feeAmount); + PostLoansLoanIdChargesResponse postLoansLoanIdChargesResponse = loanTransactionHelper.addChargeForLoan(loanId, payloadJSON, + responseSpec); + assertNotNull(postLoansLoanIdChargesResponse); + + // Get loan details expecting to have a delinquency classification + getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId); + validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("10.00"), true); + + loanTransactionHelper.disburseLoan((long) loanId, new PostLoansLoanIdRequest().actualDisbursementDate(operationDate) + .transactionAmount(new BigDecimal("1000")).locale("en").dateFormat("dd MMMM yyyy")); + + // Get loan details expecting to have a delinquency classification + getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId); + validateLoanAccount(getLoansLoanIdResponse, Double.parseDouble(principalAmount) * 2, Double.valueOf("10.00"), true); + + operationDate = Utils.dateFormatter.format(transactionDate.plusMonths(1)); + payloadJSON = LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(loanChargeId.toString(), operationDate, feeAmount); + postLoansLoanIdChargesResponse = loanTransactionHelper.addChargeForLoan(loanId, payloadJSON, responseSpec); + + // Get loan details expecting to have a delinquency classification + getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId); + validateLoanAccount(getLoansLoanIdResponse, Double.parseDouble(principalAmount) * 2, Double.valueOf("20.00"), true); + } + private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper, final Integer delinquencyBucketId) { final HashMap loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId); @@ -515,13 +575,14 @@ public class LoanChargeSpecificDueDateTest { private GetLoanProductsProductIdResponse createLoanProductWithPeriodicAccrual(final LoanTransactionHelper loanTransactionHelper, final Integer delinquencyBucketId) { final Account assetAccount = this.accountHelper.createAssetAccount(); - final Account assetFeeAndPenaltyAccount = this.accountHelper.createAssetAccount(); final Account incomeAccount = this.accountHelper.createIncomeAccount(); final Account expenseAccount = this.accountHelper.createExpenseAccount(); final Account overpaymentAccount = this.accountHelper.createLiabilityAccount(); - final HashMap loanProductMap = new LoanProductTestBuilder() + final HashMap loanProductMap = new LoanProductTestBuilder().withMultiDisburse() + .withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withInterestTypeAsDecliningBalance() .withAccountingRulePeriodicAccrual(new Account[] { assetAccount, incomeAccount, expenseAccount, overpaymentAccount }) + .withDisallowExpectedDisbursements(true) // .build(null, delinquencyBucketId); final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap)); return loanTransactionHelper.getLoanProduct(loanProductId); @@ -539,7 +600,7 @@ public class LoanChargeSpecificDueDateTest { .build(clientId, loanProductId, null); final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON); loanTransactionHelper.approveLoan(operationDate, principalAmount, loanId, null); - loanTransactionHelper.disburseLoanWithNetDisbursalAmount(operationDate, loanId, principalAmount); + loanTransactionHelper.disburseLoanWithTransactionAmount(operationDate, loanId, principalAmount); return loanId; } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java index 243595c5c..6945f57d1 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java @@ -95,53 +95,6 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { this.periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(requestSpec, responseSpec); } - @Test - public void onlyNonInterestBearingLoanCanAcceptChargeAfterMaturity() { - final Account assetAccount = this.accountHelper.createAssetAccount(); - final Account incomeAccount = this.accountHelper.createIncomeAccount(); - final Account expenseAccount = this.accountHelper.createExpenseAccount(); - final Account overpaymentAccount = this.accountHelper.createLiabilityAccount(); - - final Integer loanProductID = createLoanProductWithPeriodicAccrualAccountingWithInterestRecalculationEnabled(assetAccount, - incomeAccount, expenseAccount, overpaymentAccount); - - final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, DATE_OF_JOINING); - - final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1"); - - final float PENALTY_PORTION = 100.0f; - - Integer flatSpecifiedDueDate = ChargesHelper.createCharges(requestSpec, responseSpec, ChargesHelper - .getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, String.valueOf(PENALTY_PORTION), true)); - - HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID); - LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); - - loanStatusHashMap = this.loanTransactionHelper.approveLoan(EXPECTED_DISBURSAL_DATE, loanID); - LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap); - LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap); - - LocalDate targetDate = LocalDate.of(2011, 3, 4); - final String loanDisbursementDate = dateFormatter.format(targetDate); - - String loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID); - loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(loanDisbursementDate, loanID, - JsonPath.from(loanDetails).get("netDisbursalAmount").toString()); - LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap); - - targetDate = LocalDate.of(2011, 4, 5); - final String penaltyCharge1AddedDate = dateFormatter.format(targetDate); - - final String ADD_CHARGES_URL = "/fineract-provider/api/v1/loans/" + loanID + "/charges?" + Utils.TENANT_IDENTIFIER; - ResponseSpecification response403Spec = new ResponseSpecBuilder().expectStatusCode(403).build(); - final HashMap response = Utils.performServerPost(requestSpec, response403Spec, ADD_CHARGES_URL, - LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flatSpecifiedDueDate), penaltyCharge1AddedDate, - String.valueOf(PENALTY_PORTION)), - ""); - assertEquals("validation.msg.domain.rule.violation", response.get("userMessageGlobalisationCode")); - - } - @Test public void checkPeriodicAccrualAccountingAPIFlow() { final Account assetAccount = this.accountHelper.createAssetAccount(); @@ -458,20 +411,6 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { return this.loanTransactionHelper.getLoanId(loanApplicationJSON); } - private Integer createLoanProductWithPeriodicAccrualAccountingWithInterestRecalculationEnabled(final Account... accounts) { - LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); - final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("10000.0").withNumberOfRepayments("1") - .withinterestRatePerPeriod("1").withInterestRateFrequencyTypeAsYear().withInterestTypeAsDecliningBalance() - .withInterestCalculationPeriodTypeAsDays() - .withInterestRecalculationDetails(LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE, - LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS, - LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE) - .withInterestRecalculationRestFrequencyDetails(LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "0", null, null) - .withInterestRecalculationCompoundingFrequencyDetails(null, null, null, null).withMoratorium("0", "0") - .withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withAccountingRulePeriodicAccrual(accounts).build(null); - return this.loanTransactionHelper.getLoanProductId(loanProductJSON); - } - private Integer createLoanProductWithPeriodicAccrualAccountingNoInterest(final Account... accounts) { LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(LP_PRINCIPAL.toString()).withRepaymentTypeAsMonth()