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 FineractNotificationProperties notification;
|
||||||
|
|
||||||
|
private FineractLoanProperties loan;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public static class FineractTenantProperties {
|
public static class FineractTenantProperties {
|
||||||
@@ -295,4 +297,33 @@ public class FineractProperties {
|
|||||||
|
|
||||||
private boolean enabled;
|
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.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.persistence.CascadeType;
|
import javax.persistence.CascadeType;
|
||||||
@@ -1006,6 +1007,23 @@ public class LoanCharge extends AbstractPersistableCustom {
|
|||||||
return ChargeTimeType.fromInt(this.chargeTime);
|
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() {
|
public LoanChargeData toData() {
|
||||||
EnumOptionData chargeTimeTypeData = new EnumOptionData((long) getChargeTimeType().ordinal(), getChargeTimeType().getCode(),
|
EnumOptionData chargeTimeTypeData = new EnumOptionData((long) getChargeTimeType().ordinal(), getChargeTimeType().getCode(),
|
||||||
String.valueOf(getChargeTimeType().getValue()));
|
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()) {
|
if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||||
loanTransaction.resetDerivedComponents();
|
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> loanFees = extractFeeCharges(charges);
|
||||||
final Set<LoanCharge> loanPenalties = extractPenaltyCharges(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,
|
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;
|
int installmentIndex = 0;
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
@@ -479,20 +479,18 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
// is this transaction early/late/on-time with respect to
|
// is this transaction early/late/on-time with respect to
|
||||||
// the
|
// the
|
||||||
// current installment?
|
// current installment?
|
||||||
if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate,
|
if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate)) {
|
||||||
transactionAmountUnprocessed)) {
|
|
||||||
transactionAmountUnprocessed = handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment,
|
transactionAmountUnprocessed = handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment,
|
||||||
installments, loanTransaction, transactionDate, transactionAmountUnprocessed, transactionMappings);
|
installments, loanTransaction, transactionAmountUnprocessed, transactionMappings, charges);
|
||||||
} else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments,
|
} else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments, transactionDate)) {
|
||||||
loanTransaction.getTransactionDate())) {
|
|
||||||
// does this result in a late payment of existing
|
// does this result in a late payment of existing
|
||||||
// installment?
|
// installment?
|
||||||
transactionAmountUnprocessed = handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, installments,
|
transactionAmountUnprocessed = handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, installments,
|
||||||
loanTransaction, transactionAmountUnprocessed, transactionMappings);
|
loanTransaction, transactionAmountUnprocessed, transactionMappings, charges);
|
||||||
} else {
|
} else {
|
||||||
// standard transaction
|
// standard transaction
|
||||||
transactionAmountUnprocessed = handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment,
|
transactionAmountUnprocessed = handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment,
|
||||||
loanTransaction, transactionAmountUnprocessed, transactionMappings);
|
loanTransaction, transactionAmountUnprocessed, transactionMappings, charges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -630,10 +628,11 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
*
|
*
|
||||||
* @param transactionMappings
|
* @param transactionMappings
|
||||||
* TODO
|
* TODO
|
||||||
|
* @param charges
|
||||||
*/
|
*/
|
||||||
protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
|
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
|
* 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.
|
* Default implementation is check transaction date is before installment due date.
|
||||||
*/
|
*/
|
||||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate,
|
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
|
||||||
@SuppressWarnings("unused") final Money transactionAmount) {
|
|
||||||
|
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||||
|
|
||||||
@@ -655,20 +653,22 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
*
|
*
|
||||||
* @param transactionMappings
|
* @param transactionMappings
|
||||||
* TODO
|
* TODO
|
||||||
|
* @param charges
|
||||||
*/
|
*/
|
||||||
protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, LoanTransaction loanTransaction, LocalDate transactionDate,
|
List<LoanRepaymentScheduleInstallment> installments, LoanTransaction loanTransaction, Money paymentInAdvance,
|
||||||
Money paymentInAdvance, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings);
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For normal on-time repayments.
|
* For normal on-time repayments.
|
||||||
*
|
*
|
||||||
* @param transactionMappings
|
* @param transactionMappings
|
||||||
* TODO
|
* TODO
|
||||||
|
* @param charges
|
||||||
*/
|
*/
|
||||||
protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
|
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.
|
* Invoked when a transaction results in an over-payment of the full loan.
|
||||||
@@ -682,7 +682,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Money handleRepaymentSchedule(final List<LoanTransaction> transactionsPostDisbursement, final MonetaryCurrency currency,
|
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);
|
Money unProcessed = Money.zero(currency);
|
||||||
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
||||||
Money amountToProcess = null;
|
Money amountToProcess = null;
|
||||||
@@ -690,9 +690,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
loanTransaction.resetDerivedComponents();
|
loanTransaction.resetDerivedComponents();
|
||||||
}
|
}
|
||||||
if (loanTransaction.isInterestWaiver()) {
|
if (loanTransaction.isInterestWaiver()) {
|
||||||
processTransaction(loanTransaction, currency, installments, amountToProcess);
|
processTransaction(loanTransaction, currency, installments, loanCharges, amountToProcess);
|
||||||
} else {
|
} else {
|
||||||
unProcessed = processTransaction(loanTransaction, currency, installments, amountToProcess);
|
unProcessed = processTransaction(loanTransaction, currency, installments, loanCharges, amountToProcess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return unProcessed;
|
return unProcessed;
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@ public interface LoanRepaymentScheduleTransactionProcessor {
|
|||||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
||||||
|
|
||||||
Money handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
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.
|
* 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.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
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.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
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
|
* For creocore, early is defined as any date before the installment due date
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
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);
|
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||||
|
|
||||||
@@ -73,12 +74,11 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||||
transactionMappings);
|
charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,10 +88,11 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
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,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||||
transactionMappings);
|
transactionMappings, charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,7 +101,7 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
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.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
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.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||||
@@ -55,10 +57,10 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
||||||
Money transactionAmountRemaining = paymentInAdvance;
|
Money transactionAmountRemaining = paymentInAdvance;
|
||||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||||
@@ -112,10 +114,11 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
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,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||||
transactionMappings);
|
transactionMappings, charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,7 +127,7 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
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.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
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.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||||
@@ -56,7 +58,7 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
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);
|
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||||
|
|
||||||
@@ -68,12 +70,11 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||||
transactionMappings);
|
charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,10 +83,11 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
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,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||||
transactionMappings);
|
transactionMappings, charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,7 +96,7 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
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.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
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.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||||
@@ -61,15 +63,16 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
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,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||||
transactionMappings);
|
transactionMappings, charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
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;
|
boolean isInAdvance = false;
|
||||||
|
|
||||||
@@ -92,10 +95,10 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
||||||
Money transactionAmountRemaining = paymentInAdvance;
|
Money transactionAmountRemaining = paymentInAdvance;
|
||||||
Money principalPortion = Money.zero(currency);
|
Money principalPortion = Money.zero(currency);
|
||||||
@@ -158,7 +161,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
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.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
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.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||||
@@ -56,12 +58,11 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||||
transactionMappings);
|
charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,10 +72,11 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
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,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||||
transactionMappings);
|
transactionMappings, charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,7 +85,7 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
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.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
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.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||||
@@ -56,12 +58,11 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||||
transactionMappings);
|
charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,10 +72,11 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
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,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||||
transactionMappings);
|
transactionMappings, charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,7 +85,7 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
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.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
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.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||||
@@ -63,7 +65,7 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
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);
|
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||||
|
|
||||||
@@ -76,12 +78,11 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money paymentInAdvance,
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance,
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance,
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings,
|
||||||
transactionMappings);
|
charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,7 +91,8 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
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
|
// pay of overdue and current interest due given transaction date
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
@@ -203,7 +205,7 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
|||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
final List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||||
|
|||||||
+9
-8
@@ -505,7 +505,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// on scheduled installments to identify the
|
// on scheduled installments to identify the
|
||||||
// unprocessed(early payment ) amounts
|
// unprocessed(early payment ) amounts
|
||||||
Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency,
|
Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency,
|
||||||
scheduleParams.getInstallments());
|
scheduleParams.getInstallments(), loanCharges);
|
||||||
|
|
||||||
if (unprocessed.isGreaterThanZero()) {
|
if (unprocessed.isGreaterThanZero()) {
|
||||||
scheduleParams.reduceOutstandingBalance(unprocessed);
|
scheduleParams.reduceOutstandingBalance(unprocessed);
|
||||||
@@ -805,7 +805,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// on scheduled installments to identify the
|
// on scheduled installments to identify the
|
||||||
// unprocessed(early payment ) amounts
|
// unprocessed(early payment ) amounts
|
||||||
Money unprocessed = scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
|
Money unprocessed = scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
|
||||||
.handleRepaymentSchedule(currentTransactions, currency, scheduleParams.getInstallments());
|
.handleRepaymentSchedule(currentTransactions, currency, scheduleParams.getInstallments(), loanCharges);
|
||||||
if (unprocessed.isGreaterThanZero()) {
|
if (unprocessed.isGreaterThanZero()) {
|
||||||
|
|
||||||
if (loanApplicationTerms.getPreClosureInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) {
|
if (loanApplicationTerms.getPreClosureInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) {
|
||||||
@@ -850,7 +850,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
if (applicableDate.isBefore(scheduledDueDate)) {
|
if (applicableDate.isBefore(scheduledDueDate)) {
|
||||||
List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);
|
List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);
|
||||||
Money unprocessed = scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
|
Money unprocessed = scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
|
||||||
.handleRepaymentSchedule(currentTransactions, currency, scheduleParams.getInstallments());
|
.handleRepaymentSchedule(currentTransactions, currency, scheduleParams.getInstallments(), loanCharges);
|
||||||
Money arrears = fetchArrears(loanApplicationTerms, currency, detail.getTransaction());
|
Money arrears = fetchArrears(loanApplicationTerms, currency, detail.getTransaction());
|
||||||
if (unprocessed.isGreaterThanZero()) {
|
if (unprocessed.isGreaterThanZero()) {
|
||||||
updateMapWithAmount(scheduleParams.getPrincipalPortionMap(), unprocessed, applicableDate);
|
updateMapWithAmount(scheduleParams.getPrincipalPortionMap(), unprocessed, applicableDate);
|
||||||
@@ -1377,7 +1377,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
startDate = transactionDate;
|
startDate = transactionDate;
|
||||||
additionalPeriodsStartDate = startDate;
|
additionalPeriodsStartDate = startDate;
|
||||||
}
|
}
|
||||||
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency, params.getInstallments());
|
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency, params.getInstallments(),
|
||||||
|
loanCharges);
|
||||||
updateLatePaidAmountsToPrincipalMap(detail.getTransaction(), loanApplicationTerms, currency, holidayDetailDTO, lastRestDate,
|
updateLatePaidAmountsToPrincipalMap(detail.getTransaction(), loanApplicationTerms, currency, holidayDetailDTO, lastRestDate,
|
||||||
params);
|
params);
|
||||||
updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, params.getLatePaymentMap(), currentDate,
|
updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, params.getLatePaymentMap(), currentDate,
|
||||||
@@ -2323,7 +2324,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO,
|
Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO,
|
||||||
loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency, principalPortionMap,
|
loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency, principalPortionMap,
|
||||||
installment, applicableTransactions, actualPrincipalPortion);
|
installment, applicableTransactions, actualPrincipalPortion, loan.getActiveCharges());
|
||||||
|
|
||||||
// this block is to adjust the period number based on the
|
// this block is to adjust the period number based on the
|
||||||
// actual
|
// actual
|
||||||
@@ -2413,7 +2414,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges);
|
return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<LoanRepaymentScheduleInstallment> fetchRetainedInstallments(
|
private List<LoanRepaymentScheduleInstallment> fetchRetainedInstallments(
|
||||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom,
|
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom,
|
||||||
MonetaryCurrency currency) {
|
MonetaryCurrency currency) {
|
||||||
List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();
|
List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();
|
||||||
@@ -2455,7 +2456,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments, MonetaryCurrency currency,
|
final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments, MonetaryCurrency currency,
|
||||||
final Map<LocalDate, Money> principalPortionMap, LoanRepaymentScheduleInstallment installment,
|
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 unprocessed = Money.zero(currency);
|
||||||
Money totalUnprocessed = Money.zero(currency);
|
Money totalUnprocessed = Money.zero(currency);
|
||||||
for (RecalculationDetail detail : applicableTransactions) {
|
for (RecalculationDetail detail : applicableTransactions) {
|
||||||
@@ -2467,7 +2468,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// on scheduled installments to identify the
|
// on scheduled installments to identify the
|
||||||
// unprocessed(early payment ) amounts
|
// unprocessed(early payment ) amounts
|
||||||
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency,
|
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency,
|
||||||
newRepaymentScheduleInstallments);
|
newRepaymentScheduleInstallments, loanCharges);
|
||||||
|
|
||||||
// Identifies totalEarlyPayment and early paid amount with this
|
// Identifies totalEarlyPayment and early paid amount with this
|
||||||
// transaction
|
// 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.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
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.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.EarlyPaymentLoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor;
|
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.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
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.Bean;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class LoanAccountAutoStarter {
|
public class LoanAccountAutoStarter {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.creocore.enabled")
|
@Conditional(CreocoreLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||||
public CreocoreLoanRepaymentScheduleTransactionProcessor creocoreLoanRepaymentScheduleTransactionProcessor() {
|
public CreocoreLoanRepaymentScheduleTransactionProcessor creocoreLoanRepaymentScheduleTransactionProcessor() {
|
||||||
return new CreocoreLoanRepaymentScheduleTransactionProcessor();
|
return new CreocoreLoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.early-repayment.enabled")
|
@Conditional(EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||||
public EarlyPaymentLoanRepaymentScheduleTransactionProcessor earlyPaymentLoanRepaymentScheduleTransactionProcessor() {
|
public EarlyPaymentLoanRepaymentScheduleTransactionProcessor earlyPaymentLoanRepaymentScheduleTransactionProcessor() {
|
||||||
return new EarlyPaymentLoanRepaymentScheduleTransactionProcessor();
|
return new EarlyPaymentLoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.mifos-standard.enabled")
|
@Conditional(MifosStandardLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||||
public FineractStyleLoanRepaymentScheduleTransactionProcessor fineractStyleLoanRepaymentScheduleTransactionProcessor() {
|
public FineractStyleLoanRepaymentScheduleTransactionProcessor fineractStyleLoanRepaymentScheduleTransactionProcessor() {
|
||||||
return new FineractStyleLoanRepaymentScheduleTransactionProcessor();
|
return new FineractStyleLoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.heavensfamily.enabled")
|
@Conditional(HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||||
public HeavensFamilyLoanRepaymentScheduleTransactionProcessor heavensFamilyLoanRepaymentScheduleTransactionProcessor() {
|
public HeavensFamilyLoanRepaymentScheduleTransactionProcessor heavensFamilyLoanRepaymentScheduleTransactionProcessor() {
|
||||||
return new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
|
return new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled")
|
@Conditional(InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||||
public InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor interestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
|
public InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor interestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
|
||||||
return new InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
|
return new InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled")
|
@Conditional(PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||||
public PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor principalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
|
public PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor principalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
|
||||||
return new PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
|
return new PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty("fineract.loan.transactionprocessor.rbi-india.enabled")
|
@Conditional(RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||||
public RBILoanRepaymentScheduleTransactionProcessor rbiLoanRepaymentScheduleTransactionProcessor() {
|
public RBILoanRepaymentScheduleTransactionProcessor rbiLoanRepaymentScheduleTransactionProcessor() {
|
||||||
return new RBILoanRepaymentScheduleTransactionProcessor();
|
return new RBILoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Conditional(DueDateRespectiveLoanRepaymentScheduleTransactionProcessorCondition.class)
|
||||||
|
public DueDateRespectiveLoanRepaymentScheduleTransactionProcessor dueDateRespectiveTransactionProcessor() {
|
||||||
|
return new DueDateRespectiveLoanRepaymentScheduleTransactionProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(LoanRepaymentScheduleTransactionProcessorFactory.class)
|
@ConditionalOnMissingBean(LoanRepaymentScheduleTransactionProcessorFactory.class)
|
||||||
public LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory(
|
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.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.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.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.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}
|
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/0097_update_accounting_summary_table_reports.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0098_update_transaction_summary_table_report.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/0099_add_accrual_transaction_external_event_configuration.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="parts/0100_new_repayment_strategy.xml" relativeToChangelogFile="true" />
|
||||||
</databaseChangeLog>
|
</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.interest-principal-penalties-fees.enabled=true
|
||||||
fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled=true
|
fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled=true
|
||||||
fineract.loan.transactionprocessor.rbi-india.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.loan.transactionprocessor.error-not-found-fail=true
|
||||||
|
|
||||||
fineract.content.regex-whitelist-enabled=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 DEFAULT_STRATEGY = "mifos-standard-strategy";
|
||||||
public static final String RBI_INDIA_STRATEGY = "rbi-india-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 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 externalId = null;
|
||||||
private String principal = "10,000";
|
private String principal = "10,000";
|
||||||
|
|||||||
+1
@@ -42,6 +42,7 @@ public class LoanProductTestBuilder {
|
|||||||
private static final String FLAT_BALANCE = "1";
|
private static final String FLAT_BALANCE = "1";
|
||||||
public static final String DEFAULT_STRATEGY = "mifos-standard-strategy";
|
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 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 HEAVENS_FAMILY_STRATEGY ="heavensfamily-strategy";
|
||||||
// private static final String CREO_CORE_STRATEGY ="creocore-strategy";
|
// private static final String CREO_CORE_STRATEGY ="creocore-strategy";
|
||||||
public static final String RBI_INDIA_STRATEGY = "rbi-india-strategy";
|
public static final String RBI_INDIA_STRATEGY = "rbi-india-strategy";
|
||||||
|
|||||||
Reference in New Issue
Block a user