Revert "FINERACT-1905: Same day repayment + charge handling improvement"
This reverts commit c50fc832c1.
This commit is contained in:
+1
-1
@@ -5741,7 +5741,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
final List<LoanTransaction> copyTransactions = new ArrayList<>();
|
final List<LoanTransaction> copyTransactions = new ArrayList<>();
|
||||||
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
||||||
for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
|
for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
|
||||||
copyTransactions.add(LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction));
|
copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction));
|
||||||
}
|
}
|
||||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), copyTransactions, getCurrency(),
|
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), copyTransactions, getCurrency(),
|
||||||
getRepaymentScheduleInstallments(), getActiveCharges());
|
getRepaymentScheduleInstallments(), getActiveCharges());
|
||||||
|
|||||||
+2
-2
@@ -43,7 +43,7 @@ import java.util.Set;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
@@ -61,7 +61,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "m_loan_charge", uniqueConstraints = { @UniqueConstraint(columnNames = { "external_id" }, name = "external_id") })
|
@Table(name = "m_loan_charge", uniqueConstraints = { @UniqueConstraint(columnNames = { "external_id" }, name = "external_id") })
|
||||||
public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom {
|
public class LoanCharge extends AbstractPersistableCustom {
|
||||||
|
|
||||||
@ManyToOne(optional = false)
|
@ManyToOne(optional = false)
|
||||||
@JoinColumn(name = "loan_id", referencedColumnName = "id", nullable = false)
|
@JoinColumn(name = "loan_id", referencedColumnName = "id", nullable = false)
|
||||||
|
|||||||
+3
-5
@@ -269,13 +269,11 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
return new LoanTransaction(null, office, LoanTransactionType.REFUND, paymentDetail, amount.getAmount(), paymentDate, externalId);
|
return new LoanTransaction(null, office, LoanTransactionType.REFUND, paymentDetail, amount.getAmount(), paymentDate, externalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction copyTransactionPropertiesForReprocessing(final LoanTransaction loanTransaction) {
|
public static LoanTransaction copyTransactionProperties(final LoanTransaction loanTransaction) {
|
||||||
LoanTransaction loanTransactionCopy = new LoanTransaction(loanTransaction.loan, loanTransaction.office, loanTransaction.typeOf,
|
return new LoanTransaction(loanTransaction.loan, loanTransaction.office, loanTransaction.typeOf, loanTransaction.dateOf,
|
||||||
loanTransaction.dateOf, loanTransaction.amount, loanTransaction.principalPortion, loanTransaction.interestPortion,
|
loanTransaction.amount, loanTransaction.principalPortion, loanTransaction.interestPortion,
|
||||||
loanTransaction.feeChargesPortion, loanTransaction.penaltyChargesPortion, loanTransaction.overPaymentPortion,
|
loanTransaction.feeChargesPortion, loanTransaction.penaltyChargesPortion, loanTransaction.overPaymentPortion,
|
||||||
loanTransaction.reversed, loanTransaction.paymentDetail, loanTransaction.externalId);
|
loanTransaction.reversed, loanTransaction.paymentDetail, loanTransaction.externalId);
|
||||||
loanTransactionCopy.setCreatedDate(loanTransaction.getCreatedDateTime());
|
|
||||||
return loanTransactionCopy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction accrueLoanCharge(final Loan loan, final Office office, final Money amount, final LocalDate applyDate,
|
public static LoanTransaction accrueLoanCharge(final Loan loan, final Office office, final Money amount, final LocalDate applyDate,
|
||||||
|
|||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
<?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">
|
|
||||||
<!-- Sequence is starting from 1000 to make it easier to move existing liquibase changesets here -->
|
|
||||||
<include relativeToChangelogFile="true" file="parts/1001_add_audit_fields_to_loan_charge.xml"/>
|
|
||||||
</databaseChangeLog>
|
|
||||||
-77
@@ -1,77 +0,0 @@
|
|||||||
<?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="1001-1" context="mysql">
|
|
||||||
<addColumn tableName="m_loan_charge">
|
|
||||||
<column name="created_on_utc" type="DATETIME(6)"/>
|
|
||||||
<column name="last_modified_on_utc" type="DATETIME(6)"/>
|
|
||||||
</addColumn>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet author="fineract" id="1001-1" context="postgresql">
|
|
||||||
<addColumn tableName="m_loan_charge">
|
|
||||||
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
|
|
||||||
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
|
|
||||||
</addColumn>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet id="1001-2" author="fineract">
|
|
||||||
<addColumn tableName="m_loan_charge">
|
|
||||||
<column name="created_by" type="BIGINT"/>
|
|
||||||
<column name="last_modified_by" type="BIGINT"/>
|
|
||||||
</addColumn>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet author="fineract" id="1001-3">
|
|
||||||
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_loan_charge"
|
|
||||||
constraintName="FK_loan_charge_created_by" deferrable="false" initiallyDeferred="false"
|
|
||||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
|
||||||
referencedTableName="m_appuser" validate="true"/>
|
|
||||||
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_loan_charge"
|
|
||||||
constraintName="FK_loan_charge_last_modified_by" deferrable="false"
|
|
||||||
initiallyDeferred="false"
|
|
||||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
|
||||||
referencedTableName="m_appuser" validate="true"/>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet id="1001-4" author="fineract" context="mysql">
|
|
||||||
<preConditions onFail="MARK_RAN">
|
|
||||||
<sqlCheck expectedResult="0">select count(*) from m_loan_charge</sqlCheck>
|
|
||||||
</preConditions>
|
|
||||||
<addNotNullConstraint tableName="m_loan_charge" columnName="created_on_utc" columnDataType="DATETIME(6)"/>
|
|
||||||
<addNotNullConstraint tableName="m_loan_charge" columnName="last_modified_on_utc" columnDataType="DATETIME(6)"/>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet id="1001-4" author="fineract" context="postgresql">
|
|
||||||
<preConditions onFail="MARK_RAN">
|
|
||||||
<sqlCheck expectedResult="0">select count(*) from m_loan_charge</sqlCheck>
|
|
||||||
</preConditions>
|
|
||||||
<addNotNullConstraint tableName="m_loan_charge" columnName="created_on_utc"
|
|
||||||
columnDataType="TIMESTAMP WITH TIME ZONE"/>
|
|
||||||
<addNotNullConstraint tableName="m_loan_charge" columnName="last_modified_on_utc"
|
|
||||||
columnDataType="TIMESTAMP WITH TIME ZONE"/>
|
|
||||||
</changeSet>
|
|
||||||
<changeSet id="1001-5" author="fineract">
|
|
||||||
<preConditions onFail="MARK_RAN">
|
|
||||||
<sqlCheck expectedResult="0">select count(*) from m_loan_charge</sqlCheck>
|
|
||||||
</preConditions>
|
|
||||||
<addNotNullConstraint tableName="m_loan_charge" columnName="created_by" columnDataType="BIGINT"/>
|
|
||||||
<addNotNullConstraint tableName="m_loan_charge" columnName="last_modified_by" columnDataType="BIGINT"/>
|
|
||||||
</changeSet>
|
|
||||||
</databaseChangeLog>
|
|
||||||
+3
-3
@@ -175,7 +175,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
* For existing transactions, check if the re-payment breakup (principal, interest, fees, penalties)
|
* For existing transactions, check if the re-payment breakup (principal, interest, fees, penalties)
|
||||||
* has changed.<br>
|
* has changed.<br>
|
||||||
**/
|
**/
|
||||||
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction);
|
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||||
|
|
||||||
// Reset derived component of new loan transaction and
|
// Reset derived component of new loan transaction and
|
||||||
// re-process transaction
|
// re-process transaction
|
||||||
@@ -214,7 +214,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction,
|
private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction,
|
||||||
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments) {
|
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments) {
|
||||||
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction);
|
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||||
newLoanTransaction.resetDerivedComponents();
|
newLoanTransaction.resetDerivedComponents();
|
||||||
// determine how much is outstanding total and breakdown for principal, interest and charges
|
// determine how much is outstanding total and breakdown for principal, interest and charges
|
||||||
Money principalPortion = Money.zero(currency);
|
Money principalPortion = Money.zero(currency);
|
||||||
@@ -280,7 +280,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
if (loanTransaction.getId() == null) {
|
if (loanTransaction.getId() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction);
|
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||||
|
|
||||||
List<LoanTransaction> mergedList = getMergedTransactionList(transactionsToBeProcessed, changedTransactionDetail);
|
List<LoanTransaction> mergedList = getMergedTransactionList(transactionsToBeProcessed, changedTransactionDetail);
|
||||||
Money overpaidAmount = calculateOverpaidAmount(loanTransaction, mergedList, installments, currency);
|
Money overpaidAmount = calculateOverpaidAmount(loanTransaction, mergedList, installments, currency);
|
||||||
|
|||||||
+123
-75
@@ -61,81 +61,9 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
|
|||||||
*/
|
*/
|
||||||
@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 Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
|
||||||
Set<LoanCharge> charges) {
|
|
||||||
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
|
|
||||||
charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For late repayments
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
|
||||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
|
||||||
Set<LoanCharge> charges) {
|
|
||||||
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
|
|
||||||
charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For normal on-time repayments
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
|
|
||||||
charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
|
||||||
// TODO - KW - dont do anything with loan over-payment for now
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
|
||||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
|
||||||
Money principalPortion = Money.zero(currency);
|
|
||||||
Money interestPortion = Money.zero(currency);
|
|
||||||
Money feeChargesPortion = Money.zero(currency);
|
|
||||||
Money penaltyChargesPortion = Money.zero(currency);
|
|
||||||
|
|
||||||
principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
|
||||||
|
|
||||||
if (transactionAmountRemaining.isGreaterThanZero()) {
|
|
||||||
interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transactionAmountRemaining.isGreaterThanZero()) {
|
|
||||||
feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transactionAmountRemaining.isGreaterThanZero()) {
|
|
||||||
penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
|
||||||
}
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
|
||||||
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
|
|
||||||
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
|
|
||||||
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
|
|
||||||
}
|
|
||||||
return transactionAmountRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Money doTransactionAmountAllocation(LoanRepaymentScheduleInstallment currentInstallment, LoanTransaction loanTransaction,
|
|
||||||
Money paymentInAdvance, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
|
|
||||||
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
||||||
@@ -175,8 +103,6 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
|
|||||||
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
|
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
|
||||||
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|
||||||
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
|
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
|
||||||
.filter(loanCharge -> loanCharge.getCreatedDate().isEmpty() || loanTransaction.getCreatedDate().isEmpty()
|
|
||||||
|| !loanCharge.getCreatedDate().get().isAfter(loanTransaction.getCreatedDate().get()))
|
|
||||||
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
|
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
|
||||||
Money calculatedPenaltyCharge = Money.zero(currency);
|
Money calculatedPenaltyCharge = Money.zero(currency);
|
||||||
Money calculatedFeeCharge = Money.zero(currency);
|
Money calculatedFeeCharge = Money.zero(currency);
|
||||||
@@ -239,4 +165,126 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
|
|||||||
}
|
}
|
||||||
return transactionAmountRemaining;
|
return transactionAmountRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For late repayments
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||||
|
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
||||||
|
Set<LoanCharge> charges) {
|
||||||
|
|
||||||
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed,
|
||||||
|
transactionMappings, charges);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For normal on-time repayments
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
|
|
||||||
|
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||||
|
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||||
|
Money principalPortion = Money.zero(currency);
|
||||||
|
Money interestPortion = Money.zero(currency);
|
||||||
|
Money feeChargesPortion = Money.zero(currency);
|
||||||
|
Money penaltyChargesPortion = Money.zero(currency);
|
||||||
|
|
||||||
|
if (loanTransaction.isChargesWaiver()) {
|
||||||
|
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate,
|
||||||
|
loanTransaction.getPenaltyChargesPortion(currency));
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.waiveFeeChargesComponent(transactionDate,
|
||||||
|
loanTransaction.getFeeChargesPortion(currency));
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
|
} else if (loanTransaction.isInterestWaiver()) {
|
||||||
|
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
} else if (loanTransaction.isChargePayment()) {
|
||||||
|
if (loanTransaction.isPenaltyPayment()) {
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
} else {
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
}
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
} else {
|
||||||
|
Money subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
|
||||||
|
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
|
||||||
|
|
||||||
|
Money subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
|
||||||
|
feeChargesPortion = feeChargesPortion.add(subFeePortion);
|
||||||
|
|
||||||
|
Money subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
|
||||||
|
interestPortion = interestPortion.add(subInterestPortion);
|
||||||
|
|
||||||
|
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
|
||||||
|
principalPortion = principalPortion.add(subPrincipalPortion);
|
||||||
|
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
}
|
||||||
|
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
|
||||||
|
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
|
||||||
|
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
|
||||||
|
}
|
||||||
|
return transactionAmountRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
||||||
|
// TODO - KW - dont do anything with loan over-payment for now
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||||
|
|
||||||
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
|
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||||
|
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||||
|
Money principalPortion = Money.zero(currency);
|
||||||
|
Money interestPortion = Money.zero(currency);
|
||||||
|
Money feeChargesPortion = Money.zero(currency);
|
||||||
|
Money penaltyChargesPortion = Money.zero(currency);
|
||||||
|
|
||||||
|
principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
|
|
||||||
|
if (transactionAmountRemaining.isGreaterThanZero()) {
|
||||||
|
interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionAmountRemaining.isGreaterThanZero()) {
|
||||||
|
feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionAmountRemaining.isGreaterThanZero()) {
|
||||||
|
penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
}
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
|
||||||
|
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
|
||||||
|
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
|
||||||
|
}
|
||||||
|
return transactionAmountRemaining;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+122
-76
@@ -61,81 +61,8 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
|
|||||||
*/
|
*/
|
||||||
@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 inAdvancePayment,
|
||||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
|
||||||
Set<LoanCharge> charges) {
|
|
||||||
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
|
|
||||||
charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For late repayments
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
|
||||||
final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
|
|
||||||
Set<LoanCharge> charges) {
|
|
||||||
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
|
|
||||||
charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For normal on-time repayments
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
return doTransactionAmountAllocation(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings,
|
|
||||||
charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
|
||||||
// TODO - KW - dont do anything with loan over-payment for now
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
|
||||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
|
||||||
Money principalPortion = Money.zero(currency);
|
|
||||||
Money interestPortion = Money.zero(currency);
|
|
||||||
Money feeChargesPortion = Money.zero(currency);
|
|
||||||
Money penaltyChargesPortion = Money.zero(currency);
|
|
||||||
|
|
||||||
principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
|
||||||
|
|
||||||
if (transactionAmountRemaining.isGreaterThanZero()) {
|
|
||||||
interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transactionAmountRemaining.isGreaterThanZero()) {
|
|
||||||
feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transactionAmountRemaining.isGreaterThanZero()) {
|
|
||||||
penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
|
||||||
}
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
|
||||||
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
|
|
||||||
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
|
|
||||||
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
|
|
||||||
}
|
|
||||||
return transactionAmountRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Money doTransactionAmountAllocation(LoanRepaymentScheduleInstallment currentInstallment, LoanTransaction loanTransaction,
|
|
||||||
Money inAdvancePayment, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
|
|
||||||
final MonetaryCurrency currency = inAdvancePayment.getCurrency();
|
final MonetaryCurrency currency = inAdvancePayment.getCurrency();
|
||||||
@@ -176,8 +103,6 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
|
|||||||
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
|
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
|
||||||
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|
||||||
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
|
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate))
|
||||||
.filter(loanCharge -> loanCharge.getCreatedDate().isEmpty() || loanTransaction.getCreatedDate().isEmpty()
|
|
||||||
|| loanCharge.getCreatedDate().get().isBefore(loanTransaction.getCreatedDate().get()))
|
|
||||||
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
|
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
|
||||||
Money calculatedPenaltyCharge = Money.zero(currency);
|
Money calculatedPenaltyCharge = Money.zero(currency);
|
||||||
Money calculatedFeeCharge = Money.zero(currency);
|
Money calculatedFeeCharge = Money.zero(currency);
|
||||||
@@ -243,4 +168,125 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
|
|||||||
}
|
}
|
||||||
return transactionAmountRemaining;
|
return transactionAmountRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For late repayments
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, final Money latePayment,
|
||||||
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, latePayment, transactionMappings,
|
||||||
|
charges);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For normal on-time repayments
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
|
final LoanTransaction loanTransaction, final Money onTimePayment,
|
||||||
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
|
||||||
|
|
||||||
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
|
|
||||||
|
final MonetaryCurrency currency = onTimePayment.getCurrency();
|
||||||
|
Money transactionAmountRemaining = onTimePayment;
|
||||||
|
Money principalPortion = Money.zero(currency);
|
||||||
|
Money interestPortion = Money.zero(currency);
|
||||||
|
Money feeChargesPortion = Money.zero(currency);
|
||||||
|
Money penaltyChargesPortion = Money.zero(currency);
|
||||||
|
|
||||||
|
if (loanTransaction.isChargesWaiver()) {
|
||||||
|
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate,
|
||||||
|
loanTransaction.getPenaltyChargesPortion(currency));
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.waiveFeeChargesComponent(transactionDate,
|
||||||
|
loanTransaction.getFeeChargesPortion(currency));
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
|
} else if (loanTransaction.isInterestWaiver()) {
|
||||||
|
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
} else if (loanTransaction.isChargePayment()) {
|
||||||
|
if (loanTransaction.isPenaltyPayment()) {
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
} else {
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
}
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
} else {
|
||||||
|
Money subPenaltyPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(subPenaltyPortion);
|
||||||
|
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
|
||||||
|
|
||||||
|
Money subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
|
||||||
|
interestPortion = interestPortion.add(subInterestPortion);
|
||||||
|
|
||||||
|
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
|
||||||
|
principalPortion = principalPortion.add(subPrincipalPortion);
|
||||||
|
|
||||||
|
Money subFeePortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(subFeePortion);
|
||||||
|
feeChargesPortion = feeChargesPortion.add(subFeePortion);
|
||||||
|
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
}
|
||||||
|
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
|
||||||
|
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
|
||||||
|
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
|
||||||
|
}
|
||||||
|
return transactionAmountRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
||||||
|
// TODO - KW - dont do anything with loan over-payment for now
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed,
|
||||||
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
|
||||||
|
|
||||||
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
|
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||||
|
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||||
|
Money principalPortion = Money.zero(currency);
|
||||||
|
Money interestPortion = Money.zero(currency);
|
||||||
|
Money feeChargesPortion = Money.zero(currency);
|
||||||
|
Money penaltyChargesPortion = Money.zero(currency);
|
||||||
|
|
||||||
|
principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
|
|
||||||
|
if (transactionAmountRemaining.isGreaterThanZero()) {
|
||||||
|
interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionAmountRemaining.isGreaterThanZero()) {
|
||||||
|
feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionAmountRemaining.isGreaterThanZero()) {
|
||||||
|
penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
}
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) {
|
||||||
|
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
|
||||||
|
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion));
|
||||||
|
}
|
||||||
|
return transactionAmountRemaining;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2098,7 +2098,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
for (LoanTransaction loanTransaction : transactions) {
|
for (LoanTransaction loanTransaction : transactions) {
|
||||||
if (loanTransaction.isPaymentTransaction()) {
|
if (loanTransaction.isPaymentTransaction()) {
|
||||||
recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(),
|
recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(),
|
||||||
LoanTransaction.copyTransactionPropertiesForReprocessing(loanTransaction)));
|
LoanTransaction.copyTransactionProperties(loanTransaction)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final boolean applyInterestRecalculation = loanApplicationTerms.isInterestRecalculationEnabled();
|
final boolean applyInterestRecalculation = loanApplicationTerms.isInterestRecalculationEnabled();
|
||||||
|
|||||||
+442
-291
File diff suppressed because it is too large
Load Diff
+435
-292
File diff suppressed because it is too large
Load Diff
+4
-335
@@ -6802,8 +6802,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
|
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
|
||||||
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
|
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
|
||||||
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
|
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
|
||||||
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
|
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
|
||||||
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY);
|
|
||||||
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
|
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
|
||||||
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
|
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
|
||||||
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(new PostChargesRequest().penalty(true).amount(10.0)
|
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(new PostChargesRequest().penalty(true).amount(10.0)
|
||||||
@@ -6863,337 +6862,6 @@ public class ClientLoanIntegrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLoanTransactionOrderAndRepaymentAllocationWithSameDayChargeBeforeRepayment() {
|
|
||||||
try {
|
|
||||||
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
|
|
||||||
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
|
|
||||||
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
|
|
||||||
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
|
|
||||||
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
|
|
||||||
repaymentStrategyCode);
|
|
||||||
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
|
|
||||||
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
|
|
||||||
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
|
|
||||||
new PostChargesRequest().penalty(true).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
|
|
||||||
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
|
||||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
|
||||||
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
|
|
||||||
.chargeAppliesTo(1).locale("en").active(true));
|
|
||||||
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
|
|
||||||
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
|
|
||||||
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
|
|
||||||
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
|
|
||||||
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
|
|
||||||
.approvedOnDate("01 January 2023").locale("en"));
|
|
||||||
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
|
|
||||||
String loanDisbursementUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
|
|
||||||
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
|
|
||||||
Long loanId = disbursedLoanResult.getResourceId();
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("07 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
|
|
||||||
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
|
|
||||||
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
|
|
||||||
.locale("en").amount(10.0).dueDate("10 January 2023"));
|
|
||||||
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
|
|
||||||
String firstRepaymentUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
|
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("07 January 2023")
|
|
||||||
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 0.0, 0.0, 0.0, 9.0, 0.0);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLoanTransactionOrderAndRepaymentAllocationWithSameDayChargeAfterRepayment() {
|
|
||||||
try {
|
|
||||||
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
|
|
||||||
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
|
|
||||||
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
|
|
||||||
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
|
|
||||||
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
|
|
||||||
repaymentStrategyCode);
|
|
||||||
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
|
|
||||||
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
|
|
||||||
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
|
|
||||||
new PostChargesRequest().penalty(false).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
|
|
||||||
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
|
||||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
|
||||||
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
|
|
||||||
.chargeAppliesTo(1).locale("en").active(true));
|
|
||||||
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
|
|
||||||
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
|
|
||||||
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
|
|
||||||
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
|
|
||||||
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
|
|
||||||
.approvedOnDate("01 January 2023").locale("en"));
|
|
||||||
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
|
|
||||||
String loanDisbursementUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
|
|
||||||
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
|
|
||||||
Long loanId = disbursedLoanResult.getResourceId();
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("07 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
|
|
||||||
String firstRepaymentUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
|
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("07 January 2023")
|
|
||||||
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
|
|
||||||
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
|
|
||||||
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
|
|
||||||
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
|
|
||||||
.locale("en").amount(10.0).dueDate("10 January 2023"));
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLoanTransactionOrderAndRepaymentAllocationWithSameDayChargeBeforeRepaymentOnInstallmentDueDate() {
|
|
||||||
try {
|
|
||||||
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
|
|
||||||
LOG.info("-----------------------------------REPAYMENT SCEDULE CODE : {}-----------------------------------------",
|
|
||||||
repaymentStrategyCode);
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
|
|
||||||
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
|
|
||||||
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
|
|
||||||
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
|
|
||||||
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
|
|
||||||
repaymentStrategyCode);
|
|
||||||
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
|
|
||||||
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
|
|
||||||
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
|
|
||||||
new PostChargesRequest().penalty(true).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
|
|
||||||
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
|
||||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
|
||||||
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
|
|
||||||
.chargeAppliesTo(1).locale("en").active(true));
|
|
||||||
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
|
|
||||||
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
|
|
||||||
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
|
|
||||||
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
|
|
||||||
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
|
|
||||||
.approvedOnDate("01 January 2023").locale("en"));
|
|
||||||
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
|
|
||||||
String loanDisbursementUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
|
|
||||||
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
|
|
||||||
Long loanId = disbursedLoanResult.getResourceId();
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("31 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
|
|
||||||
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
|
|
||||||
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
|
|
||||||
.locale("en").amount(10.0).dueDate("31 January 2023"));
|
|
||||||
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
|
|
||||||
String firstRepaymentUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
|
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("31 January 2023")
|
|
||||||
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 0.0, 0.0, 0.0, 9.0, 0.0);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLoanTransactionOrderAndRepaymentAllocationWithSameDayChargeAfterRepaymentOnInstallmentDueDate() {
|
|
||||||
try {
|
|
||||||
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
|
|
||||||
LOG.info("-----------------------------------REPAYMENT SCEDULE CODE : {}-----------------------------------------",
|
|
||||||
repaymentStrategyCode);
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
|
|
||||||
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
|
|
||||||
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
|
|
||||||
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
|
|
||||||
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
|
|
||||||
repaymentStrategyCode);
|
|
||||||
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
|
|
||||||
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
|
|
||||||
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
|
|
||||||
new PostChargesRequest().penalty(true).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
|
|
||||||
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
|
||||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
|
||||||
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
|
|
||||||
.chargeAppliesTo(1).locale("en").active(true));
|
|
||||||
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
|
|
||||||
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
|
|
||||||
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
|
|
||||||
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
|
|
||||||
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
|
|
||||||
.approvedOnDate("01 January 2023").locale("en"));
|
|
||||||
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
|
|
||||||
String loanDisbursementUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
|
|
||||||
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
|
|
||||||
Long loanId = disbursedLoanResult.getResourceId();
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("31 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
|
|
||||||
String firstRepaymentUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
|
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("31 January 2023")
|
|
||||||
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
|
|
||||||
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
|
|
||||||
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
|
|
||||||
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
|
|
||||||
.locale("en").amount(10.0).dueDate("31 January 2023"));
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLoanTransactionOrderAndRepaymentAllocationMultipleActions() {
|
|
||||||
try {
|
|
||||||
List<String> dueDateRelatedRepaymentStrategies = Arrays.asList(
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY,
|
|
||||||
LoanProductTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
for (String repaymentStrategyCode : dueDateRelatedRepaymentStrategies) {
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", true);
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.TRUE);
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("01 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-----------------------------------NEW CLIENT-----------------------------------------");
|
|
||||||
final PostClientsRequest newClient = createRandomClientWithDate("01 January 2023");
|
|
||||||
final PostClientsResponse clientResponse = CLIENT_HELPER.createClient(newClient);
|
|
||||||
LOG.info("-----------------------------------NEW LOAN PRODUCT-----------------------------------------");
|
|
||||||
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(
|
|
||||||
repaymentStrategyCode);
|
|
||||||
final PostLoanProductsResponse loanProductResponse = LOAN_PRODUCT_HELPER.createLoanProduct(loanProductsRequest);
|
|
||||||
LOG.info("-----------------------------------CREATE CHARGES-----------------------------------------");
|
|
||||||
PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(
|
|
||||||
new PostChargesRequest().penalty(false).amount(10.0).chargeCalculationType(ChargeCalculationType.FLAT.getValue())
|
|
||||||
.chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
|
||||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
|
||||||
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
|
|
||||||
.chargeAppliesTo(1).locale("en").active(true));
|
|
||||||
LOG.info("-----------------------------------SUBMIT LOAN-----------------------------------------");
|
|
||||||
final PostLoansResponse loanApplicationResult = applyForLoanApplicationForOnePeriod30DaysLongNoInterestPeriodicAccrual(
|
|
||||||
clientResponse.getResourceId(), loanProductResponse.getResourceId(), "01 January 2023",
|
|
||||||
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY);
|
|
||||||
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
|
|
||||||
PostLoansLoanIdResponse approvedLoanResult = LOAN_TRANSACTION_HELPER.approveLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
|
|
||||||
.approvedOnDate("01 January 2023").locale("en"));
|
|
||||||
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
|
|
||||||
String loanDisbursementUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdResponse disbursedLoanResult = LOAN_TRANSACTION_HELPER.disburseLoan(loanApplicationResult.getResourceId(),
|
|
||||||
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
|
|
||||||
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en").externalId(loanDisbursementUUID));
|
|
||||||
Long loanId = disbursedLoanResult.getResourceId();
|
|
||||||
BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
|
||||||
.date("07 January 2023").dateFormat(DATETIME_PATTERN).locale("en"));
|
|
||||||
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
|
|
||||||
String firstRepaymentUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdTransactionsResponse firstRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
|
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("07 January 2023")
|
|
||||||
.locale("en").transactionAmount(9.0).externalId(firstRepaymentUUID));
|
|
||||||
LOG.info("-------------------------------ADD CHARGES-------------------------------------------");
|
|
||||||
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = LOAN_TRANSACTION_HELPER.addChargesForLoan(loanId,
|
|
||||||
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN)
|
|
||||||
.locale("en").amount(10.0).dueDate("07 January 2023"));
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
|
|
||||||
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
|
|
||||||
String secondRepaymentUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdTransactionsResponse secondRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
|
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("06 January 2023")
|
|
||||||
.locale("en").transactionAmount(9.0).externalId(secondRepaymentUUID));
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, secondRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
|
|
||||||
LOG.info("-------------------------------DO A PARTIAL REPAYMENT-------------------------------------------");
|
|
||||||
String thirdRepaymentUUID = UUID.randomUUID().toString();
|
|
||||||
PostLoansLoanIdTransactionsResponse thirdRepaymentResult = LOAN_TRANSACTION_HELPER.makeLoanRepayment(loanId,
|
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("07 January 2023")
|
|
||||||
.locale("en").transactionAmount(9.0).externalId(thirdRepaymentUUID));
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, thirdRepaymentResult.getResourceExternalId(), 9.0, 0.0, 0.0, 9.0, 0.0, 0.0);
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, secondRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
|
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ALLOCATION-------------------------------------------");
|
|
||||||
checkLoanTransactionAllocation(loanId, firstRepaymentResult.getResourceExternalId(), 9.0, 9.0, 0.0, 0.0, 0.0, 0.0);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, RESPONSE_SPEC, Boolean.FALSE);
|
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(REQUEST_SPEC, RESPONSE_SPEC, "42", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkLoanTransactionAllocation(Long loanId, String transactionExternalId, double amount, double principalPortion,
|
|
||||||
double interestPortion, double feePortion, double penaltyPortion, double overpaymentPortion) {
|
|
||||||
GetLoansLoanIdTransactionsTransactionIdResponse transactionDetails = LOAN_TRANSACTION_HELPER.getLoanTransactionDetails(loanId,
|
|
||||||
transactionExternalId);
|
|
||||||
assertEquals(amount, transactionDetails.getAmount());
|
|
||||||
assertEquals(principalPortion, transactionDetails.getPrincipalPortion());
|
|
||||||
assertEquals(interestPortion, transactionDetails.getInterestPortion());
|
|
||||||
assertEquals(feePortion, transactionDetails.getFeeChargesPortion());
|
|
||||||
assertEquals(penaltyPortion, transactionDetails.getPenaltyChargesPortion());
|
|
||||||
assertEquals(overpaymentPortion, transactionDetails.getOverpaymentPortion());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkLoanTransactionOrder(Long loanId, String... transactionUUIDs) {
|
private void checkLoanTransactionOrder(Long loanId, String... transactionUUIDs) {
|
||||||
LOG.info("-------------------------------CHECK LOAN TRANSACTION ORDER-------------------------------------------");
|
LOG.info("-------------------------------CHECK LOAN TRANSACTION ORDER-------------------------------------------");
|
||||||
GetLoansLoanIdResponse loanDetailsResult = LOAN_TRANSACTION_HELPER.getLoanDetails(loanId);
|
GetLoansLoanIdResponse loanDetailsResult = LOAN_TRANSACTION_HELPER.getLoanDetails(loanId);
|
||||||
@@ -8355,7 +8023,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
.principal(BigDecimal.valueOf(1000.0)).loanType("individual"));
|
.principal(BigDecimal.valueOf(1000.0)).loanType("individual"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PostLoanProductsRequest createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct(String repaymentStrategyCode) {
|
private PostLoanProductsRequest createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct() {
|
||||||
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6))//
|
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6))//
|
||||||
.shortName(Utils.uniqueRandomStringGenerator("", 4))//
|
.shortName(Utils.uniqueRandomStringGenerator("", 4))//
|
||||||
.description("Loan Product Description")//
|
.description("Loan Product Description")//
|
||||||
@@ -8382,7 +8050,8 @@ public class ClientLoanIntegrationTest {
|
|||||||
.interestType(0)//
|
.interestType(0)//
|
||||||
.isEqualAmortization(false)//
|
.isEqualAmortization(false)//
|
||||||
.interestCalculationPeriodType(1)//
|
.interestCalculationPeriodType(1)//
|
||||||
.transactionProcessingStrategyCode(repaymentStrategyCode)//
|
.transactionProcessingStrategyCode(
|
||||||
|
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY)//
|
||||||
.daysInYearType(1)//
|
.daysInYearType(1)//
|
||||||
.daysInMonthType(1)//
|
.daysInMonthType(1)//
|
||||||
.canDefineInstallmentAmount(true)//
|
.canDefineInstallmentAmount(true)//
|
||||||
|
|||||||
Reference in New Issue
Block a user