FINERACT-1905: New repayment strategy
This commit is contained in:
committed by
Arnold Gálovics
parent
da810378f3
commit
24bc7bb5fb
+31
@@ -60,6 +60,8 @@ public class FineractProperties {
|
||||
|
||||
private FineractNotificationProperties notification;
|
||||
|
||||
private FineractLoanProperties loan;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractTenantProperties {
|
||||
@@ -295,4 +297,33 @@ public class FineractProperties {
|
||||
|
||||
private boolean enabled;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractLoanProperties {
|
||||
|
||||
private FineractTransactionProcessorProperties transactionProcessor;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractTransactionProcessorProperties {
|
||||
|
||||
private FineractTransactionProcessorItemProperties creocore;
|
||||
private FineractTransactionProcessorItemProperties earlyRepayment;
|
||||
private FineractTransactionProcessorItemProperties mifosStandard;
|
||||
private FineractTransactionProcessorItemProperties heavensFamily;
|
||||
private FineractTransactionProcessorItemProperties interestPrincipalPenaltiesFees;
|
||||
private FineractTransactionProcessorItemProperties principalInterestPenaltiesFees;
|
||||
private FineractTransactionProcessorItemProperties rbiIndia;
|
||||
private FineractTransactionProcessorItemProperties duePenaltyFeeInterestPrincipalInAdvancePrincipalPenaltyFeeInterest;
|
||||
private boolean errorNotFoundFail;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractTransactionProcessorItemProperties {
|
||||
|
||||
private boolean enabled;
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -28,6 +28,7 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.CascadeType;
|
||||
@@ -1006,6 +1007,23 @@ public class LoanCharge extends AbstractPersistableCustom {
|
||||
return ChargeTimeType.fromInt(this.chargeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the effective due date of the loan charge. For installment fee we are using the earliest not fully paid
|
||||
* installment due date
|
||||
*
|
||||
* @return LocalDate
|
||||
*/
|
||||
public LocalDate getEffectiveDueDate() {
|
||||
LocalDate dueDate;
|
||||
if (Objects.requireNonNull(getChargeTimeType()) == ChargeTimeType.INSTALMENT_FEE) {
|
||||
LoanInstallmentCharge firstUnpaidInstallment = getUnpaidInstallmentLoanCharge();
|
||||
dueDate = firstUnpaidInstallment != null ? firstUnpaidInstallment.getInstallment().getDueDate() : null;
|
||||
} else {
|
||||
dueDate = getDueLocalDate();
|
||||
}
|
||||
return dueDate;
|
||||
}
|
||||
|
||||
public LoanChargeData toData() {
|
||||
EnumOptionData chargeTimeTypeData = new EnumOptionData((long) getChargeTimeType().ordinal(), getChargeTimeType().getCode(),
|
||||
String.valueOf(getChargeTimeType().getValue()));
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Sort loan charges by effective due date
|
||||
*
|
||||
* Null values go to the end
|
||||
*/
|
||||
public final class LoanChargeEffectiveDueDateComparator implements Comparator<LoanCharge> {
|
||||
|
||||
public static final LoanChargeEffectiveDueDateComparator INSTANCE = new LoanChargeEffectiveDueDateComparator();
|
||||
|
||||
private LoanChargeEffectiveDueDateComparator() {}
|
||||
|
||||
@Override
|
||||
public int compare(final LoanCharge o1, final LoanCharge o2) {
|
||||
if (o1.getEffectiveDueDate() == null && o2.getEffectiveDueDate() != null) {
|
||||
return 1;
|
||||
} else if (o1.getEffectiveDueDate() == null) {
|
||||
return 0;
|
||||
} else if (o2.getEffectiveDueDate() == null) {
|
||||
return -1;
|
||||
}
|
||||
return o1.getEffectiveDueDate().compareTo(o2.getEffectiveDueDate());
|
||||
}
|
||||
}
|
||||
+18
-18
@@ -431,7 +431,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||
loanTransaction.resetDerivedComponents();
|
||||
}
|
||||
Money transactionAmountUnprocessed = processTransaction(loanTransaction, currency, installments, chargeAmountToProcess);
|
||||
Money transactionAmountUnprocessed = processTransaction(loanTransaction, currency, installments, charges, chargeAmountToProcess);
|
||||
|
||||
final Set<LoanCharge> loanFees = extractFeeCharges(charges);
|
||||
final Set<LoanCharge> loanPenalties = extractPenaltyCharges(charges);
|
||||
@@ -462,7 +462,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
}
|
||||
|
||||
private Money processTransaction(final LoanTransaction loanTransaction, final MonetaryCurrency currency,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, Money amountToProcess) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, Money amountToProcess) {
|
||||
int installmentIndex = 0;
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
@@ -479,20 +479,18 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
// is this transaction early/late/on-time with respect to
|
||||
// the
|
||||
// current installment?
|
||||
if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate,
|
||||
transactionAmountUnprocessed)) {
|
||||
if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate)) {
|
||||
transactionAmountUnprocessed = handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment,
|
||||
installments, loanTransaction, transactionDate, transactionAmountUnprocessed, transactionMappings);
|
||||
} else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments,
|
||||
loanTransaction.getTransactionDate())) {
|
||||
installments, loanTransaction, transactionAmountUnprocessed, transactionMappings, charges);
|
||||
} else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments, transactionDate)) {
|
||||
// does this result in a late payment of existing
|
||||
// installment?
|
||||
transactionAmountUnprocessed = handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, installments,
|
||||
loanTransaction, transactionAmountUnprocessed, transactionMappings);
|
||||
loanTransaction, transactionAmountUnprocessed, transactionMappings, charges);
|
||||
} else {
|
||||
// standard transaction
|
||||
transactionAmountUnprocessed = handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment,
|
||||
loanTransaction, transactionAmountUnprocessed, transactionMappings);
|
||||
loanTransaction, transactionAmountUnprocessed, transactionMappings, charges);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,10 +628,11 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
*
|
||||
* @param transactionMappings
|
||||
* TODO
|
||||
* @param charges
|
||||
*/
|
||||
protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
||||
List<LoanRepaymentScheduleInstallment> installments, LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings);
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges);
|
||||
|
||||
/**
|
||||
* This method is responsible for checking if the current transaction is 'an advance/early payment' based on the
|
||||
@@ -642,8 +641,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
* Default implementation is check transaction date is before installment due date.
|
||||
*/
|
||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate,
|
||||
@SuppressWarnings("unused") final Money transactionAmount) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
|
||||
|
||||
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||
|
||||
@@ -655,20 +653,22 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
*
|
||||
* @param transactionMappings
|
||||
* TODO
|
||||
* @param charges
|
||||
*/
|
||||
protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
||||
List<LoanRepaymentScheduleInstallment> installments, LoanTransaction loanTransaction, LocalDate transactionDate,
|
||||
Money paymentInAdvance, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings);
|
||||
List<LoanRepaymentScheduleInstallment> installments, LoanTransaction loanTransaction, Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges);
|
||||
|
||||
/**
|
||||
* For normal on-time repayments.
|
||||
*
|
||||
* @param transactionMappings
|
||||
* TODO
|
||||
* @param charges
|
||||
*/
|
||||
protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
||||
LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings);
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges);
|
||||
|
||||
/**
|
||||
* Invoked when a transaction results in an over-payment of the full loan.
|
||||
@@ -682,7 +682,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
|
||||
@Override
|
||||
public Money handleRepaymentSchedule(final List<LoanTransaction> transactionsPostDisbursement, final MonetaryCurrency currency,
|
||||
final List<LoanRepaymentScheduleInstallment> installments) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> loanCharges) {
|
||||
Money unProcessed = Money.zero(currency);
|
||||
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
||||
Money amountToProcess = null;
|
||||
@@ -690,9 +690,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
loanTransaction.resetDerivedComponents();
|
||||
}
|
||||
if (loanTransaction.isInterestWaiver()) {
|
||||
processTransaction(loanTransaction, currency, installments, amountToProcess);
|
||||
processTransaction(loanTransaction, currency, installments, loanCharges, amountToProcess);
|
||||
} else {
|
||||
unProcessed = processTransaction(loanTransaction, currency, installments, amountToProcess);
|
||||
unProcessed = processTransaction(loanTransaction, currency, installments, loanCharges, amountToProcess);
|
||||
}
|
||||
}
|
||||
return unProcessed;
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ public interface LoanRepaymentScheduleTransactionProcessor {
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
||||
|
||||
Money handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||
List<LoanRepaymentScheduleInstallment> installments);
|
||||
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> loanCharges);
|
||||
|
||||
/**
|
||||
* Used in interest recalculation to introduce new interest only installment.
|
||||
|
||||
+11
-10
@@ -20,8 +20,10 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
@@ -57,10 +59,9 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
|
||||
/**
|
||||
* For creocore, early is defined as any date before the installment due date
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate, final Money transactionAmount) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
|
||||
|
||||
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||
|
||||
@@ -73,12 +74,11 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
||||
transactionMappings);
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||
charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,10 +88,11 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed, final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final Money transactionAmountUnprocessed, final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
||||
Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||
transactionMappings);
|
||||
transactionMappings, charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +101,7 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeEffectiveDueDateComparator;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
|
||||
/**
|
||||
* `First due/late charges, interest, principal, after in advance principal, charges, interest` style
|
||||
* {@link LoanRepaymentScheduleTransactionProcessor}.
|
||||
*
|
||||
* For ALL types of transactions, pays off components in order of: Due/late penalty Due/late Fee Due/late interest
|
||||
* Due/late principal In advance principal In advance penalty In advance fee In advance interest
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DueDateRespectiveLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
|
||||
|
||||
private static final String STRATEGY_CODE = "due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy";
|
||||
|
||||
private static final String STRATEGY_NAME = "Due penalty, fee, interest, principal, In advance principal, penalty, fee, interest";
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return STRATEGY_CODE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return STRATEGY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
|
||||
|
||||
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||
|
||||
return transactionDate.isBefore(currentInstallment.getDueDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* For early/'in advance' repayments
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, 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 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 {
|
||||
boolean ignoreDueDateCheck = false;
|
||||
boolean rerun = false;
|
||||
|
||||
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
|
||||
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|
||||
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
|
||||
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
|
||||
Money calculatedPenaltyCharge = Money.zero(currency);
|
||||
Money calculatedFeeCharge = Money.zero(currency);
|
||||
// Calculate the amount of due charges
|
||||
for (LoanCharge charge : orderedLoanChargesByDueDate) {
|
||||
if (charge.isPenaltyCharge()) {
|
||||
calculatedPenaltyCharge = calculatedPenaltyCharge.add(charge.getAmount(currency));
|
||||
} else {
|
||||
calculatedFeeCharge = calculatedFeeCharge.add(charge.getAmount(currency));
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
Money subPenaltyPortion;
|
||||
if (!ignoreDueDateCheck) {
|
||||
if (calculatedPenaltyCharge.isGreaterThan(transactionAmountRemaining)) {
|
||||
calculatedPenaltyCharge = transactionAmountRemaining;
|
||||
}
|
||||
} else {
|
||||
calculatedPenaltyCharge = transactionAmountUnprocessed;
|
||||
}
|
||||
subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, calculatedPenaltyCharge);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
|
||||
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
|
||||
|
||||
Money subFeePortion;
|
||||
|
||||
if (!ignoreDueDateCheck) {
|
||||
if (calculatedFeeCharge.isGreaterThan(transactionAmountRemaining)) {
|
||||
calculatedFeeCharge = transactionAmountRemaining;
|
||||
}
|
||||
} else {
|
||||
calculatedFeeCharge = transactionAmountUnprocessed;
|
||||
}
|
||||
subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, calculatedFeeCharge);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
|
||||
feeChargesPortion = feeChargesPortion.add(subFeePortion);
|
||||
|
||||
if (ignoreDueDateCheck || !transactionDate.isBefore(currentInstallment.getDueDate())) {
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
}
|
||||
|
||||
principalPortion = principalPortion
|
||||
.add(currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining));
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
// If the transactionAmountRemaining is greater than zero, rerun the allocation without due date check
|
||||
// to distribute the in advance portions
|
||||
if (transactionAmountRemaining.isGreaterThanZero()) {
|
||||
ignoreDueDateCheck = true;
|
||||
}
|
||||
rerun = !rerun;
|
||||
} while (ignoreDueDateCheck && rerun);
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
}
|
||||
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
|
||||
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
|
||||
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
|
||||
}
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
+9
-6
@@ -20,8 +20,10 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
@@ -55,10 +57,10 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
||||
Money transactionAmountRemaining = paymentInAdvance;
|
||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
@@ -112,10 +114,11 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
||||
Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||
transactionMappings);
|
||||
transactionMappings, charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +127,7 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
|
||||
+11
-9
@@ -20,8 +20,10 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
@@ -56,7 +58,7 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
|
||||
@Override
|
||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate, final Money transactionAmount) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
|
||||
|
||||
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||
|
||||
@@ -68,12 +70,11 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
||||
transactionMappings);
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||
charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,10 +83,11 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
||||
Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||
transactionMappings);
|
||||
transactionMappings, charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +96,7 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
|
||||
+10
-7
@@ -20,8 +20,10 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
@@ -61,15 +63,16 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
||||
Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||
transactionMappings);
|
||||
transactionMappings, charges);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate, final Money transactionAmount) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
|
||||
|
||||
boolean isInAdvance = false;
|
||||
|
||||
@@ -92,10 +95,10 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
||||
Money transactionAmountRemaining = paymentInAdvance;
|
||||
Money principalPortion = Money.zero(currency);
|
||||
@@ -158,7 +161,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
|
||||
+10
-8
@@ -20,8 +20,10 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
@@ -56,12 +58,11 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
||||
transactionMappings);
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||
charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,10 +72,11 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
||||
Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||
transactionMappings);
|
||||
transactionMappings, charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +85,7 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
|
||||
+10
-8
@@ -20,8 +20,10 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
@@ -56,12 +58,11 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
||||
transactionMappings);
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||
charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,10 +72,11 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
||||
Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||
transactionMappings);
|
||||
transactionMappings, charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +85,7 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
|
||||
+10
-8
@@ -20,8 +20,10 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
@@ -63,7 +65,7 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate, final Money transactionAmount) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
|
||||
|
||||
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||
|
||||
@@ -76,12 +78,11 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
||||
transactionMappings);
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||
charges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +91,8 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
||||
Set<LoanCharge> charges) {
|
||||
|
||||
// pay of overdue and current interest due given transaction date
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
@@ -203,7 +205,7 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
|
||||
+9
-8
@@ -505,7 +505,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// on scheduled installments to identify the
|
||||
// unprocessed(early payment ) amounts
|
||||
Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency,
|
||||
scheduleParams.getInstallments());
|
||||
scheduleParams.getInstallments(), loanCharges);
|
||||
|
||||
if (unprocessed.isGreaterThanZero()) {
|
||||
scheduleParams.reduceOutstandingBalance(unprocessed);
|
||||
@@ -805,7 +805,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// on scheduled installments to identify the
|
||||
// unprocessed(early payment ) amounts
|
||||
Money unprocessed = scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
|
||||
.handleRepaymentSchedule(currentTransactions, currency, scheduleParams.getInstallments());
|
||||
.handleRepaymentSchedule(currentTransactions, currency, scheduleParams.getInstallments(), loanCharges);
|
||||
if (unprocessed.isGreaterThanZero()) {
|
||||
|
||||
if (loanApplicationTerms.getPreClosureInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) {
|
||||
@@ -850,7 +850,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
if (applicableDate.isBefore(scheduledDueDate)) {
|
||||
List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);
|
||||
Money unprocessed = scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
|
||||
.handleRepaymentSchedule(currentTransactions, currency, scheduleParams.getInstallments());
|
||||
.handleRepaymentSchedule(currentTransactions, currency, scheduleParams.getInstallments(), loanCharges);
|
||||
Money arrears = fetchArrears(loanApplicationTerms, currency, detail.getTransaction());
|
||||
if (unprocessed.isGreaterThanZero()) {
|
||||
updateMapWithAmount(scheduleParams.getPrincipalPortionMap(), unprocessed, applicableDate);
|
||||
@@ -1377,7 +1377,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
startDate = transactionDate;
|
||||
additionalPeriodsStartDate = startDate;
|
||||
}
|
||||
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency, params.getInstallments());
|
||||
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency, params.getInstallments(),
|
||||
loanCharges);
|
||||
updateLatePaidAmountsToPrincipalMap(detail.getTransaction(), loanApplicationTerms, currency, holidayDetailDTO, lastRestDate,
|
||||
params);
|
||||
updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, params.getLatePaymentMap(), currentDate,
|
||||
@@ -2323,7 +2324,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO,
|
||||
loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency, principalPortionMap,
|
||||
installment, applicableTransactions, actualPrincipalPortion);
|
||||
installment, applicableTransactions, actualPrincipalPortion, loan.getActiveCharges());
|
||||
|
||||
// this block is to adjust the period number based on the
|
||||
// actual
|
||||
@@ -2413,7 +2414,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges);
|
||||
}
|
||||
|
||||
public List<LoanRepaymentScheduleInstallment> fetchRetainedInstallments(
|
||||
private List<LoanRepaymentScheduleInstallment> fetchRetainedInstallments(
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom,
|
||||
MonetaryCurrency currency) {
|
||||
List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();
|
||||
@@ -2455,7 +2456,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments, MonetaryCurrency currency,
|
||||
final Map<LocalDate, Money> principalPortionMap, LoanRepaymentScheduleInstallment installment,
|
||||
Collection<RecalculationDetail> applicableTransactions, Money actualPrincipalPortion) {
|
||||
Collection<RecalculationDetail> applicableTransactions, Money actualPrincipalPortion, Set<LoanCharge> loanCharges) {
|
||||
Money unprocessed = Money.zero(currency);
|
||||
Money totalUnprocessed = Money.zero(currency);
|
||||
for (RecalculationDetail detail : applicableTransactions) {
|
||||
@@ -2467,7 +2468,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// on scheduled installments to identify the
|
||||
// unprocessed(early payment ) amounts
|
||||
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency,
|
||||
newRepaymentScheduleInstallments);
|
||||
newRepaymentScheduleInstallments, loanCharges);
|
||||
|
||||
// Identifies totalEarlyPayment and early paid amount with this
|
||||
// transaction
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class CreocoreLoanRepaymentScheduleTransactionProcessorCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getLoan().getTransactionProcessor().getCreocore().isEnabled();
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class DueDateRespectiveLoanRepaymentScheduleTransactionProcessorCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getLoan().getTransactionProcessor().getDuePenaltyFeeInterestPrincipalInAdvancePrincipalPenaltyFeeInterest()
|
||||
.isEnabled();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getLoan().getTransactionProcessor().getEarlyRepayment().isEnabled();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getLoan().getTransactionProcessor().getHeavensFamily().isEnabled();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getLoan().getTransactionProcessor().getInterestPrincipalPenaltiesFees().isEnabled();
|
||||
}
|
||||
}
|
||||
+15
-8
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.DueDateRespectiveLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.EarlyPaymentLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor;
|
||||
@@ -29,55 +30,61 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class LoanAccountAutoStarter {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.creocore.enabled")
|
||||
@Conditional(CreocoreLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||
public CreocoreLoanRepaymentScheduleTransactionProcessor creocoreLoanRepaymentScheduleTransactionProcessor() {
|
||||
return new CreocoreLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.early-repayment.enabled")
|
||||
@Conditional(EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||
public EarlyPaymentLoanRepaymentScheduleTransactionProcessor earlyPaymentLoanRepaymentScheduleTransactionProcessor() {
|
||||
return new EarlyPaymentLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.mifos-standard.enabled")
|
||||
@Conditional(MifosStandardLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||
public FineractStyleLoanRepaymentScheduleTransactionProcessor fineractStyleLoanRepaymentScheduleTransactionProcessor() {
|
||||
return new FineractStyleLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.heavensfamily.enabled")
|
||||
@Conditional(HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||
public HeavensFamilyLoanRepaymentScheduleTransactionProcessor heavensFamilyLoanRepaymentScheduleTransactionProcessor() {
|
||||
return new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled")
|
||||
@Conditional(InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||
public InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor interestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
|
||||
return new InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled")
|
||||
@Conditional(PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||
public PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor principalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
|
||||
return new PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.rbi-india.enabled")
|
||||
@Conditional(RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||
public RBILoanRepaymentScheduleTransactionProcessor rbiLoanRepaymentScheduleTransactionProcessor() {
|
||||
return new RBILoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(DueDateRespectiveLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||
public DueDateRespectiveLoanRepaymentScheduleTransactionProcessor dueDateRespectiveTransactionProcessor() {
|
||||
return new DueDateRespectiveLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanRepaymentScheduleTransactionProcessorFactory.class)
|
||||
public LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory(
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class MifosStandardLoanRepaymentScheduleTransactionProcessorCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getLoan().getTransactionProcessor().getMifosStandard().isEnabled();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getLoan().getTransactionProcessor().getPrincipalInterestPenaltiesFees().isEnabled();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getLoan().getTransactionProcessor().getRbiIndia().isEnabled();
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,7 @@ fineract.loan.transactionprocessor.heavensfamily.enabled=${FINERACT_LOAN_TRANSAC
|
||||
fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_INTEREST_PRINCIPAL_PENALTIES_FEES_ENABLED:true}
|
||||
fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_PRINCIPAL_INTEREST_PENALTIES_FEES_ENABLED:true}
|
||||
fineract.loan.transactionprocessor.rbi-india.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_RBI_INDIA_ENABLED:true}
|
||||
fineract.loan.transactionprocessor.due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_ENABLED:true}
|
||||
fineract.loan.transactionprocessor.error-not-found-fail=${FINERACT_LOAN_TRANSACTIONPROCESSOR_ERROR_NOT_FOUND_FAIL:true}
|
||||
|
||||
fineract.content.regex-whitelist-enabled=${FINERACT_CONTENT_REGEX_WHITELIST_ENABLED:true}
|
||||
|
||||
@@ -119,4 +119,5 @@
|
||||
<include file="parts/0097_update_accounting_summary_table_reports.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0098_update_transaction_summary_table_report.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0099_add_accrual_transaction_external_event_configuration.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0100_new_repayment_strategy.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<insert tableName="r_enum_value">
|
||||
<column name="enum_name" value="loan_transaction_strategy_id"/>
|
||||
<column name="enum_id" valueNumeric="8"/>
|
||||
<column name="enum_message_property" value="Due penalty, fee, interest, principal, In advance principal, penalty, fee, interest"/>
|
||||
<column name="enum_value" value="Due penalty, fee, interest, principal, In advance principal, penalty, fee, interest"/>
|
||||
<column name="enum_type" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
<changeSet id="2" author="fineract">
|
||||
<insert tableName="ref_loan_transaction_processing_strategy">
|
||||
<column name="id" valueNumeric="8"/>
|
||||
<column name="code" value="due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy"/>
|
||||
<column name="name" value="Due penalty, fee, interest, principal, In advance principal, penalty, fee, interest"/>
|
||||
<column name="sort_order" valueNumeric="8"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
public class LoanChargeEffectiveDueDateComparatorTest {
|
||||
|
||||
@Test
|
||||
public void testLoanChargeEffectiveDueDateComparator() {
|
||||
LoanCharge lc1 = new LoanCharge();
|
||||
ReflectionTestUtils.setField(lc1, "chargeTime", ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
ReflectionTestUtils.setField(lc1, "dueDate", LocalDate.of(2023, 3, 17));
|
||||
|
||||
LoanCharge lc2 = new LoanCharge();
|
||||
ReflectionTestUtils.setField(lc2, "chargeTime", ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
ReflectionTestUtils.setField(lc2, "dueDate", LocalDate.of(2023, 3, 18));
|
||||
|
||||
LoanCharge lc3 = new LoanCharge();
|
||||
ReflectionTestUtils.setField(lc3, "chargeTime", ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
ReflectionTestUtils.setField(lc3, "dueDate", LocalDate.of(2023, 3, 16));
|
||||
|
||||
LoanCharge lc4 = new LoanCharge();
|
||||
LoanInstallmentCharge installmentCharge = new LoanInstallmentCharge();
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment();
|
||||
ReflectionTestUtils.setField(installment, "dueDate", LocalDate.of(2023, 3, 15));
|
||||
ReflectionTestUtils.setField(installmentCharge, "installment", installment);
|
||||
ReflectionTestUtils.setField(lc4, "chargeTime", ChargeTimeType.INSTALMENT_FEE.getValue());
|
||||
ReflectionTestUtils.setField(lc4, "loanInstallmentCharge", Set.of(installmentCharge));
|
||||
|
||||
List<LoanCharge> list = new ArrayList<>();
|
||||
list.add(lc1);
|
||||
list.add(lc2);
|
||||
list.add(lc3);
|
||||
list.add(lc4);
|
||||
list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
|
||||
|
||||
assertEquals(LocalDate.of(2023, 3, 15), list.get(0).getEffectiveDueDate());
|
||||
assertEquals(LocalDate.of(2023, 3, 16), list.get(1).getEffectiveDueDate());
|
||||
assertEquals(LocalDate.of(2023, 3, 17), list.get(2).getEffectiveDueDate());
|
||||
assertEquals(LocalDate.of(2023, 3, 18), list.get(3).getEffectiveDueDate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBothNull() {
|
||||
LoanCharge lc1 = new LoanCharge();
|
||||
ReflectionTestUtils.setField(lc1, "chargeTime", ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
ReflectionTestUtils.setField(lc1, "dueDate", null);
|
||||
|
||||
LoanCharge lc2 = new LoanCharge();
|
||||
ReflectionTestUtils.setField(lc2, "chargeTime", ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
ReflectionTestUtils.setField(lc2, "dueDate", null);
|
||||
|
||||
List<LoanCharge> list = new ArrayList<>();
|
||||
list.add(lc1);
|
||||
list.add(lc2);
|
||||
list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
|
||||
|
||||
assertEquals(lc1, list.get(0));
|
||||
assertEquals(lc2, list.get(1));
|
||||
|
||||
list = new ArrayList<>();
|
||||
list.add(lc2);
|
||||
list.add(lc1);
|
||||
list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
|
||||
|
||||
assertEquals(lc2, list.get(0));
|
||||
assertEquals(lc1, list.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneOfThemIsNull() {
|
||||
LoanCharge lc1 = new LoanCharge();
|
||||
ReflectionTestUtils.setField(lc1, "chargeTime", ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
ReflectionTestUtils.setField(lc1, "dueDate", null);
|
||||
|
||||
LoanCharge lc2 = new LoanCharge();
|
||||
ReflectionTestUtils.setField(lc2, "chargeTime", ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
|
||||
ReflectionTestUtils.setField(lc2, "dueDate", LocalDate.of(2023, 3, 17));
|
||||
|
||||
LoanCharge lc3 = new LoanCharge();
|
||||
LoanInstallmentCharge installmentCharge1 = new LoanInstallmentCharge();
|
||||
LoanRepaymentScheduleInstallment installment1 = new LoanRepaymentScheduleInstallment();
|
||||
ReflectionTestUtils.setField(installment1, "dueDate", LocalDate.of(2023, 3, 15));
|
||||
ReflectionTestUtils.setField(installmentCharge1, "installment", installment1);
|
||||
ReflectionTestUtils.setField(lc3, "chargeTime", ChargeTimeType.INSTALMENT_FEE.getValue());
|
||||
ReflectionTestUtils.setField(lc3, "loanInstallmentCharge", Set.of(installmentCharge1));
|
||||
|
||||
LoanCharge lc4 = new LoanCharge();
|
||||
LoanInstallmentCharge installmentCharge2 = new LoanInstallmentCharge();
|
||||
LoanRepaymentScheduleInstallment installment2 = new LoanRepaymentScheduleInstallment();
|
||||
ReflectionTestUtils.setField(installment2, "dueDate", null);
|
||||
ReflectionTestUtils.setField(installmentCharge2, "paid", Boolean.TRUE);
|
||||
ReflectionTestUtils.setField(installmentCharge2, "installment", installment2);
|
||||
ReflectionTestUtils.setField(lc4, "chargeTime", ChargeTimeType.INSTALMENT_FEE.getValue());
|
||||
ReflectionTestUtils.setField(lc4, "loanInstallmentCharge", Set.of(installmentCharge2));
|
||||
|
||||
List<LoanCharge> list = new ArrayList<>();
|
||||
list.add(lc1);
|
||||
list.add(lc2);
|
||||
list.add(lc3);
|
||||
list.add(lc4);
|
||||
list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
|
||||
|
||||
assertEquals(lc3, list.get(0));
|
||||
assertEquals(lc2, list.get(1));
|
||||
assertEquals(lc1, list.get(2));
|
||||
assertEquals(lc4, list.get(3));
|
||||
|
||||
// Reversed order
|
||||
list = new ArrayList<>();
|
||||
list.add(lc4);
|
||||
list.add(lc3);
|
||||
list.add(lc2);
|
||||
list.add(lc1);
|
||||
list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
|
||||
|
||||
assertEquals(lc3, list.get(0));
|
||||
assertEquals(lc2, list.get(1));
|
||||
assertEquals(lc4, list.get(2));
|
||||
assertEquals(lc1, list.get(3));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -60,6 +60,7 @@ fineract.loan.transactionprocessor.heavensfamily.enabled=true
|
||||
fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled=true
|
||||
fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled=true
|
||||
fineract.loan.transactionprocessor.rbi-india.enabled=true
|
||||
fineract.loan.transactionprocessor.due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest.enabled=true
|
||||
fineract.loan.transactionprocessor.error-not-found-fail=true
|
||||
|
||||
fineract.content.regex-whitelist-enabled=true
|
||||
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import io.restassured.builder.RequestSpecBuilder;
|
||||
import io.restassured.builder.ResponseSpecBuilder;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.HashMap;
|
||||
import org.apache.fineract.client.models.BusinessDateRequest;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DueDateRespectiveLoanRepaymentScheduleTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DueDateRespectiveLoanRepaymentScheduleTest.class);
|
||||
private ResponseSpecification responseSpec;
|
||||
private RequestSpecification requestSpec;
|
||||
private BusinessDateHelper businessDateHelper;
|
||||
private LoanTransactionHelper loanTransactionHelper;
|
||||
|
||||
private AccountHelper accountHelper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
Utils.initializeRESTAssured();
|
||||
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
this.requestSpec.header("Fineract-Platform-TenantId", "default");
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
this.businessDateHelper = new BusinessDateHelper();
|
||||
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||
}
|
||||
|
||||
// Scenario1:
|
||||
// 1. Disburse the loan
|
||||
// 2. Adding a partial repayment
|
||||
// 3. Adding a charge
|
||||
// 3.1 No reverse-replay
|
||||
// 4 Adding a partial repayment
|
||||
// 4.1 Paying only principal portion
|
||||
// 4.2 Adding a partial repayment
|
||||
// 4.3 Paying only charge portion
|
||||
@Test
|
||||
public void scenario1() {
|
||||
try {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
||||
.date("2023.02.01").dateFormat("yyyy.MM.dd").locale("en"));
|
||||
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", true));
|
||||
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
|
||||
LoanProductTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
|
||||
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
|
||||
|
||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
|
||||
LoanApplicationTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, "01 January 2023", "01 January 2023");
|
||||
|
||||
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = loanTransactionHelper.approveLoan("01 January 2023", loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = loanTransactionHelper.disburseLoanWithTransactionAmount("01 January 2023", loanID, "1000");
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
Integer firstRepaymentId = (Integer) loanTransactionHelper.makeRepayment("10 January 2023", Float.parseFloat("500.00"), loanID)
|
||||
.get("resourceId");
|
||||
Integer firstChargeId = loanTransactionHelper.addChargesForLoan(loanID,
|
||||
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), "20 January 2023", "50"));
|
||||
Integer secondRepaymentId = (Integer) loanTransactionHelper.makeRepayment("17 January 2023", Float.parseFloat("450.00"), loanID)
|
||||
.get("resourceId");
|
||||
|
||||
Integer thirdRepaymentId = (Integer) loanTransactionHelper.makeRepayment("21 January 2023", Float.parseFloat("50.00"), loanID)
|
||||
.get("resourceId");
|
||||
|
||||
GetLoansLoanIdResponse response = loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
assertEquals(50.0, response.getSummary().getTotalOutstanding());
|
||||
assertEquals(50.0, response.getRepaymentSchedule().getTotalOutstanding());
|
||||
assertEquals(50.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
|
||||
assertEquals(50.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(1000.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
|
||||
assertEquals(950.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
|
||||
assertEquals(50.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
|
||||
assertTrue(response.getStatus().getActive());
|
||||
|
||||
assertEquals(firstRepaymentId, response.getTransactions().get(1).getId().intValue());
|
||||
assertNull(response.getTransactions().get(1).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(1).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(1).getType().getRepayment());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getAmount());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getPrincipalPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getInterestPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getFeeChargesPortion());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getOutstandingLoanBalance());
|
||||
assertEquals(secondRepaymentId, response.getTransactions().get(2).getId().intValue());
|
||||
assertNull(response.getTransactions().get(2).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(2).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(2).getType().getRepayment());
|
||||
assertEquals(450.0, response.getTransactions().get(2).getAmount());
|
||||
assertEquals(450.0, response.getTransactions().get(2).getPrincipalPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(2).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(2).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(2).getInterestPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(2).getFeeChargesPortion());
|
||||
assertEquals(50.0, response.getTransactions().get(2).getOutstandingLoanBalance());
|
||||
assertEquals(thirdRepaymentId, response.getTransactions().get(3).getId().intValue());
|
||||
assertNull(response.getTransactions().get(3).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(3).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(3).getType().getRepayment());
|
||||
assertEquals(50.0, response.getTransactions().get(3).getAmount());
|
||||
assertEquals(0.0, response.getTransactions().get(3).getPrincipalPortion());
|
||||
assertEquals(50.0, response.getTransactions().get(3).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(3).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(3).getInterestPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(3).getFeeChargesPortion());
|
||||
assertEquals(50.0, response.getTransactions().get(3).getOutstandingLoanBalance());
|
||||
assertEquals(firstChargeId, response.getTransactions().get(3).getLoanChargePaidByList().get(0).getChargeId().intValue());
|
||||
assertEquals(1, response.getTransactions().get(3).getLoanChargePaidByList().size());
|
||||
|
||||
} finally {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
// Scenario2:
|
||||
// 1. Disburse the loan
|
||||
// 2. Adding a partial repayment
|
||||
// 3. Adding a charge
|
||||
// 3.1 No reverse-replay
|
||||
// 4. Adding a partial repayment
|
||||
// 4.1 Paying only principal portion
|
||||
// 5. Adding a charge
|
||||
// 5.1 No any reverse-replay
|
||||
// 6. Adding a partial repayment
|
||||
// 6.1 Paying the 1st charge
|
||||
// 6.2 Paying secondly the in advance principal
|
||||
// 6.3 Not paying the 2nd charge (id: #5)
|
||||
@Test
|
||||
public void scenario2() {
|
||||
try {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
||||
.date("2023.02.01").dateFormat("yyyy.MM.dd").locale("en"));
|
||||
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", true));
|
||||
|
||||
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", false));
|
||||
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
|
||||
LoanProductTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
|
||||
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
|
||||
|
||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
|
||||
LoanApplicationTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, "01 January 2023", "01 January 2023");
|
||||
|
||||
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = loanTransactionHelper.approveLoan("01 January 2023", loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = loanTransactionHelper.disburseLoanWithTransactionAmount("01 January 2023", loanID, "1000");
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
Integer firstRepaymentId = (Integer) loanTransactionHelper.makeRepayment("10 January 2023", Float.parseFloat("500.00"), loanID)
|
||||
.get("resourceId");
|
||||
Integer firstChargeId = loanTransactionHelper.addChargesForLoan(loanID,
|
||||
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), "20 January 2023", "50"));
|
||||
Integer secondRepaymentId = (Integer) loanTransactionHelper.makeRepayment("17 January 2023", Float.parseFloat("100.00"), loanID)
|
||||
.get("resourceId");
|
||||
|
||||
Integer secondChargeId = loanTransactionHelper.addChargesForLoan(loanID,
|
||||
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), "23 January 2023", "10"));
|
||||
|
||||
Integer thirdRepaymentId = (Integer) loanTransactionHelper.makeRepayment("20 January 2023", Float.parseFloat("100.00"), loanID)
|
||||
.get("resourceId");
|
||||
|
||||
GetLoansLoanIdResponse response = loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
assertEquals(360.0, response.getSummary().getTotalOutstanding());
|
||||
assertEquals(360.0, response.getRepaymentSchedule().getTotalOutstanding());
|
||||
assertEquals(10.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
|
||||
assertEquals(10.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(50.0, response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
|
||||
assertEquals(50.0, response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
|
||||
assertEquals(1000.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
|
||||
assertEquals(650.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
|
||||
assertEquals(350.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
|
||||
assertTrue(response.getStatus().getActive());
|
||||
|
||||
assertEquals(firstRepaymentId, response.getTransactions().get(1).getId().intValue());
|
||||
assertNull(response.getTransactions().get(1).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(1).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(1).getType().getRepayment());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getAmount());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getPrincipalPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getInterestPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getFeeChargesPortion());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getOutstandingLoanBalance());
|
||||
assertEquals(secondRepaymentId, response.getTransactions().get(2).getId().intValue());
|
||||
assertNull(response.getTransactions().get(2).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(2).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(2).getType().getRepayment());
|
||||
assertEquals(100.0, response.getTransactions().get(2).getAmount());
|
||||
assertEquals(100.0, response.getTransactions().get(2).getPrincipalPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(2).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(2).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(2).getInterestPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(2).getFeeChargesPortion());
|
||||
assertEquals(400.0, response.getTransactions().get(2).getOutstandingLoanBalance());
|
||||
assertEquals(thirdRepaymentId, response.getTransactions().get(3).getId().intValue());
|
||||
assertNull(response.getTransactions().get(3).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(3).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(3).getType().getRepayment());
|
||||
assertEquals(100.0, response.getTransactions().get(3).getAmount());
|
||||
assertEquals(50.0, response.getTransactions().get(3).getPrincipalPortion());
|
||||
assertEquals(50.0, response.getTransactions().get(3).getFeeChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(3).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(3).getInterestPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(3).getPenaltyChargesPortion());
|
||||
assertEquals(350.0, response.getTransactions().get(3).getOutstandingLoanBalance());
|
||||
assertEquals(firstChargeId, response.getTransactions().get(3).getLoanChargePaidByList().get(0).getChargeId().intValue());
|
||||
assertEquals(1, response.getTransactions().get(3).getLoanChargePaidByList().size());
|
||||
} finally {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
// Scenario3:
|
||||
// 1. Disburse the loan
|
||||
// 2. Adding a partial repayment
|
||||
// 3. Adding a charge
|
||||
// 3.1 No reverse-replay
|
||||
// 4. Adding a full repayment
|
||||
// 4.1 Paying first the in advanced principal portion, and after the in advanced charges
|
||||
@Test
|
||||
public void scenario3() {
|
||||
try {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
||||
.date("2023.02.01").dateFormat("yyyy.MM.dd").locale("en"));
|
||||
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", false));
|
||||
final Integer loanProductID = createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
|
||||
LoanProductTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
|
||||
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2023");
|
||||
|
||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID, "1000", "1", "1", "1", "0",
|
||||
LoanApplicationTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, "01 January 2023", "01 January 2023");
|
||||
|
||||
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = loanTransactionHelper.approveLoan("01 January 2023", loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = loanTransactionHelper.disburseLoanWithTransactionAmount("01 January 2023", loanID, "1000");
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
Integer firstRepaymentId = (Integer) loanTransactionHelper.makeRepayment("10 January 2023", Float.parseFloat("500.00"), loanID)
|
||||
.get("resourceId");
|
||||
Integer firstChargeId = loanTransactionHelper.addChargesForLoan(loanID,
|
||||
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), "20 January 2023", "50"));
|
||||
Integer secondRepaymentId = (Integer) loanTransactionHelper.makeRepayment("17 January 2023", Float.parseFloat("550.00"), loanID)
|
||||
.get("resourceId");
|
||||
|
||||
GetLoansLoanIdResponse response = loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
assertEquals(0.0, response.getSummary().getTotalOutstanding());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getTotalOutstanding());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(50.0, response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
|
||||
assertEquals(50.0, response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
|
||||
assertEquals(1000.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
|
||||
assertEquals(1000.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
|
||||
assertEquals(0.0, response.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
|
||||
assertTrue(response.getStatus().getClosedObligationsMet());
|
||||
|
||||
assertEquals(firstRepaymentId, response.getTransactions().get(1).getId().intValue());
|
||||
assertNull(response.getTransactions().get(1).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(1).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(1).getType().getRepayment());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getAmount());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getPrincipalPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getInterestPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(1).getFeeChargesPortion());
|
||||
assertEquals(500.0, response.getTransactions().get(1).getOutstandingLoanBalance());
|
||||
|
||||
int repaymentOrderNo;
|
||||
int accrualOrderNo;
|
||||
|
||||
if (response.getTransactions().get(2).getType().getAccrual()) {
|
||||
accrualOrderNo = 2;
|
||||
repaymentOrderNo = 3;
|
||||
} else {
|
||||
accrualOrderNo = 3;
|
||||
repaymentOrderNo = 2;
|
||||
}
|
||||
|
||||
assertNull(response.getTransactions().get(accrualOrderNo).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(accrualOrderNo).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(accrualOrderNo).getType().getAccrual());
|
||||
assertEquals(50.0, response.getTransactions().get(accrualOrderNo).getAmount());
|
||||
assertEquals(0.0, response.getTransactions().get(accrualOrderNo).getPrincipalPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(accrualOrderNo).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(accrualOrderNo).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(accrualOrderNo).getInterestPortion());
|
||||
assertEquals(50.0, response.getTransactions().get(accrualOrderNo).getFeeChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(accrualOrderNo).getOutstandingLoanBalance());
|
||||
assertEquals(firstChargeId,
|
||||
response.getTransactions().get(accrualOrderNo).getLoanChargePaidByList().get(0).getChargeId().intValue());
|
||||
assertEquals(1, response.getTransactions().get(accrualOrderNo).getLoanChargePaidByList().size());
|
||||
|
||||
assertEquals(secondRepaymentId, response.getTransactions().get(repaymentOrderNo).getId().intValue());
|
||||
assertNull(response.getTransactions().get(repaymentOrderNo).getReversedOnDate());
|
||||
assertTrue(response.getTransactions().get(repaymentOrderNo).getTransactionRelations().isEmpty());
|
||||
assertTrue(response.getTransactions().get(repaymentOrderNo).getType().getRepayment());
|
||||
assertEquals(550.0, response.getTransactions().get(repaymentOrderNo).getAmount());
|
||||
assertEquals(500.0, response.getTransactions().get(repaymentOrderNo).getPrincipalPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(repaymentOrderNo).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(repaymentOrderNo).getOverpaymentPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(repaymentOrderNo).getInterestPortion());
|
||||
assertEquals(50.0, response.getTransactions().get(repaymentOrderNo).getFeeChargesPortion());
|
||||
assertEquals(0.0, response.getTransactions().get(repaymentOrderNo).getOutstandingLoanBalance());
|
||||
assertEquals(firstChargeId,
|
||||
response.getTransactions().get(repaymentOrderNo).getLoanChargePaidByList().get(0).getChargeId().intValue());
|
||||
assertEquals(1, response.getTransactions().get(repaymentOrderNo).getLoanChargePaidByList().size());
|
||||
} finally {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID, final String principal,
|
||||
final String loanTermFrequency, final String repaymentAfterEvery, final String numberOfRepayments, final String interestRate,
|
||||
final String repaymentStrategy, final String expectedDisbursementDate, final String submittedOnDate) {
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principal)
|
||||
.withLoanTermFrequency(loanTermFrequency).withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments)
|
||||
.withRepaymentEveryAfter(repaymentAfterEvery).withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod(interestRate)
|
||||
.withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments().withRepaymentStrategy(repaymentStrategy)
|
||||
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate(expectedDisbursementDate)
|
||||
.withSubmittedOnDate(submittedOnDate).withLoanType("individual").build(clientID.toString(), loanProductID.toString(), null);
|
||||
return loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
}
|
||||
|
||||
private Integer createLoanProductWithNoAccountingNoInterest(final String principal, final String repaymentAfterEvery,
|
||||
final String numberOfRepayments, final String interestRate, final String repaymentStrategy, final Account... accounts) {
|
||||
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(principal).withRepaymentTypeAsMonth()
|
||||
.withRepaymentAfterEvery(repaymentAfterEvery).withNumberOfRepayments(numberOfRepayments).withRepaymentTypeAsMonth()
|
||||
.withinterestRatePerPeriod(interestRate).withInterestRateFrequencyTypeAsMonths().withRepaymentStrategy(repaymentStrategy)
|
||||
.withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRulePeriodicAccrual(accounts)
|
||||
.withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", "0").build(null);
|
||||
return loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -42,6 +42,7 @@ public class LoanApplicationTestBuilder {
|
||||
public static final String DEFAULT_STRATEGY = "mifos-standard-strategy";
|
||||
public static final String RBI_INDIA_STRATEGY = "rbi-india-strategy";
|
||||
public static final String INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = "interest-principal-penalties-fees-order-strategy";
|
||||
public static final String DUE_DATE_RESPECTIVE_STRATEGY = "due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy";
|
||||
|
||||
private String externalId = null;
|
||||
private String principal = "10,000";
|
||||
|
||||
+1
@@ -42,6 +42,7 @@ public class LoanProductTestBuilder {
|
||||
private static final String FLAT_BALANCE = "1";
|
||||
public static final String DEFAULT_STRATEGY = "mifos-standard-strategy";
|
||||
public static final String INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = "interest-principal-penalties-fees-order-strategy";
|
||||
public static final String DUE_DATE_RESPECTIVE_STRATEGY = "due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy";
|
||||
// private static final String HEAVENS_FAMILY_STRATEGY ="heavensfamily-strategy";
|
||||
// private static final String CREO_CORE_STRATEGY ="creocore-strategy";
|
||||
public static final String RBI_INDIA_STRATEGY = "rbi-india-strategy";
|
||||
|
||||
Reference in New Issue
Block a user