FINERACT-1981: Disbursement on overpaid loan
This commit is contained in:
+6
-5
@@ -2483,7 +2483,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
|
public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail, boolean downPaymentEnabled) {
|
final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail) {
|
||||||
|
|
||||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE);
|
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE);
|
||||||
|
|
||||||
@@ -2501,7 +2501,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
validateDisbursementDateIsOnHoliday(holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays());
|
validateDisbursementDateIsOnHoliday(holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays());
|
||||||
|
|
||||||
regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(this.repaymentScheduleDetail().isInterestRecalculationEnabled(),
|
regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(this.repaymentScheduleDetail().isInterestRecalculationEnabled(),
|
||||||
isDisbursementMissed(), scheduleGeneratorDTO, downPaymentEnabled);
|
isDisbursementMissed(), scheduleGeneratorDTO);
|
||||||
|
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
||||||
@@ -2533,7 +2533,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(boolean interestRecalculationEnabledParam,
|
private void regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(boolean interestRecalculationEnabledParam,
|
||||||
boolean disbursementMissedParam, ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean downPaymentEnabled) {
|
boolean disbursementMissedParam, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
|
|
||||||
LocalDate firstInstallmentDueDate = fetchRepaymentScheduleInstallment(1).getDueDate();
|
LocalDate firstInstallmentDueDate = fetchRepaymentScheduleInstallment(1).getDueDate();
|
||||||
if ((interestRecalculationEnabledParam && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
|
if ((interestRecalculationEnabledParam && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
|
||||||
@@ -3837,7 +3837,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
}
|
}
|
||||||
if (loanTransaction.isRefund() || loanTransaction.isRefundForActiveLoan()) {
|
if (loanTransaction.isRefund() || loanTransaction.isRefundForActiveLoan()) {
|
||||||
totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getAmount(currency));
|
totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getAmount(currency));
|
||||||
} else if (loanTransaction.isCreditBalanceRefund() || loanTransaction.isChargeback()) {
|
} else if (loanTransaction.isCreditBalanceRefund() || loanTransaction.isChargeback() || loanTransaction.isDisbursement()) {
|
||||||
totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getOverPaymentPortion(currency));
|
totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getOverPaymentPortion(currency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5940,7 +5940,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
List<LoanTransaction> loanTransactions = retrieveListOfTransactionsExcludeAccruals();
|
List<LoanTransaction> loanTransactions = retrieveListOfTransactionsExcludeAccruals();
|
||||||
for (LoanTransaction loanTransaction : loanTransactions) {
|
for (LoanTransaction loanTransaction : loanTransactions) {
|
||||||
if (loanTransaction.isDisbursement() || loanTransaction.isIncomePosting()) {
|
if (loanTransaction.isDisbursement() || loanTransaction.isIncomePosting()) {
|
||||||
outstanding = outstanding.plus(loanTransaction.getAmount(getCurrency()));
|
outstanding = outstanding.plus(loanTransaction.getAmount(getCurrency()))
|
||||||
|
.minus(loanTransaction.getOverPaymentPortion(getCurrency()));
|
||||||
loanTransaction.updateOutstandingLoanBalance(outstanding.getAmount());
|
loanTransaction.updateOutstandingLoanBalance(outstanding.getAmount());
|
||||||
} else if (loanTransaction.isChargeback() || loanTransaction.isCreditBalanceRefund()) {
|
} else if (loanTransaction.isChargeback() || loanTransaction.isCreditBalanceRefund()) {
|
||||||
Money transactionOutstanding = loanTransaction.getAmount(getCurrency());
|
Money transactionOutstanding = loanTransaction.getAmount(getCurrency());
|
||||||
|
|||||||
+36
-16
@@ -154,7 +154,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
public void processLatestTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
public void processLatestTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, MoneyHolder overpaymentHolder) {
|
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, MoneyHolder overpaymentHolder) {
|
||||||
switch (loanTransaction.getTypeOf()) {
|
switch (loanTransaction.getTypeOf()) {
|
||||||
case DISBURSEMENT -> handleDisbursement(loanTransaction, currency, installments);
|
case DISBURSEMENT -> handleDisbursement(loanTransaction, currency, installments, overpaymentHolder);
|
||||||
case WRITEOFF -> handleWriteOff(loanTransaction, currency, installments);
|
case WRITEOFF -> handleWriteOff(loanTransaction, currency, installments);
|
||||||
case REFUND_FOR_ACTIVE_LOAN -> handleRefund(loanTransaction, currency, installments, charges);
|
case REFUND_FOR_ACTIVE_LOAN -> handleRefund(loanTransaction, currency, installments, charges);
|
||||||
case CHARGEBACK -> handleChargeback(loanTransaction, currency, installments, overpaymentHolder);
|
case CHARGEBACK -> handleChargeback(loanTransaction, currency, installments, overpaymentHolder);
|
||||||
@@ -281,12 +281,12 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleDisbursement(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
private void handleDisbursement(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments) {
|
List<LoanRepaymentScheduleInstallment> installments, MoneyHolder overpaymentHolder) {
|
||||||
updateLoanSchedule(loanTransaction, currency, installments);
|
updateLoanSchedule(loanTransaction, currency, installments, overpaymentHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLoanSchedule(LoanTransaction disbursementTransaction, MonetaryCurrency currency,
|
private void updateLoanSchedule(LoanTransaction disbursementTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments) {
|
List<LoanRepaymentScheduleInstallment> installments, MoneyHolder overpaymentHolder) {
|
||||||
final MathContext mc = MoneyHelper.getMathContext();
|
final MathContext mc = MoneyHelper.getMathContext();
|
||||||
List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments = installments.stream().filter(
|
List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments = installments.stream().filter(
|
||||||
i -> i.getDueDate().isAfter(disbursementTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional())
|
i -> i.getDueDate().isAfter(disbursementTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional())
|
||||||
@@ -304,23 +304,43 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
downPaymentAmt = Money.roundToMultiplesOf(downPaymentAmt, installmentAmountInMultiplesOf);
|
downPaymentAmt = Money.roundToMultiplesOf(downPaymentAmt, installmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
downPaymentAmount = Money.of(currency, downPaymentAmt);
|
downPaymentAmount = Money.of(currency, downPaymentAmt);
|
||||||
|
Money autoPayFromOverpayment = overpaymentHolder.getMoneyObject();
|
||||||
downPaymentInstallment.addToPrincipal(disbursementTransaction.getTransactionDate(), downPaymentAmount);
|
downPaymentInstallment.addToPrincipal(disbursementTransaction.getTransactionDate(), downPaymentAmount);
|
||||||
|
Money paidAmount = downPaymentInstallment.payPrincipalComponent(disbursementTransaction.getTransactionDate(),
|
||||||
|
autoPayFromOverpayment);
|
||||||
|
disbursementTransaction.setOverPayments(paidAmount);
|
||||||
|
overpaymentHolder.setMoneyObject(overpaymentHolder.getMoneyObject().minus(paidAmount));
|
||||||
}
|
}
|
||||||
Money amortizableAmount = disbursementTransaction.getAmount(currency).minus(downPaymentAmount);
|
Money amortizableAmount = disbursementTransaction.getAmount(currency).minus(downPaymentAmount);
|
||||||
Money increasePrincipalBy = amortizableAmount.dividedBy(noCandidateRepaymentInstallments, mc.getRoundingMode());
|
Money overpaidAmount = overpaymentHolder.getMoneyObject();
|
||||||
if (installmentAmountInMultiplesOf != null) {
|
if (overpaidAmount.isGreaterThanZero()) {
|
||||||
increasePrincipalBy = Money.roundToMultiplesOf(increasePrincipalBy, installmentAmountInMultiplesOf);
|
if (amortizableAmount.isGreaterThan(overpaidAmount)) {
|
||||||
|
overpaymentHolder.setMoneyObject(Money.zero(currency));
|
||||||
|
amortizableAmount = amortizableAmount.minus(overpaidAmount);
|
||||||
|
disbursementTransaction.setOverPayments(disbursementTransaction.getOverPaymentPortion(currency).add(overpaidAmount));
|
||||||
|
} else {
|
||||||
|
overpaymentHolder.setMoneyObject(overpaymentHolder.getMoneyObject().minus(amortizableAmount));
|
||||||
|
amortizableAmount = Money.zero(currency);
|
||||||
|
disbursementTransaction.setOverPayments(disbursementTransaction.getOverPaymentPortion(currency).add(amortizableAmount));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Money remainingAmount = amortizableAmount
|
|
||||||
.minus(increasePrincipalBy.multiplyRetainScale(noCandidateRepaymentInstallments, mc.getRoundingMode()));
|
|
||||||
|
|
||||||
Money finalIncreasePrincipalBy = increasePrincipalBy;
|
if (amortizableAmount.isGreaterThanZero()) {
|
||||||
candidateRepaymentInstallments
|
Money increasePrincipalBy = amortizableAmount.dividedBy(noCandidateRepaymentInstallments, mc.getRoundingMode());
|
||||||
.forEach(i -> i.addToPrincipal(disbursementTransaction.getTransactionDate(), finalIncreasePrincipalBy));
|
if (installmentAmountInMultiplesOf != null) {
|
||||||
// Hence the rounding, we might need to amend the last installment amount
|
increasePrincipalBy = Money.roundToMultiplesOf(increasePrincipalBy, installmentAmountInMultiplesOf);
|
||||||
candidateRepaymentInstallments.get(noCandidateRepaymentInstallments - 1)
|
}
|
||||||
.addToPrincipal(disbursementTransaction.getTransactionDate(), remainingAmount);
|
|
||||||
|
Money remainingAmount = amortizableAmount
|
||||||
|
.minus(increasePrincipalBy.multiplyRetainScale(noCandidateRepaymentInstallments, mc.getRoundingMode()));
|
||||||
|
|
||||||
|
Money finalIncreasePrincipalBy = increasePrincipalBy;
|
||||||
|
candidateRepaymentInstallments
|
||||||
|
.forEach(i -> i.addToPrincipal(disbursementTransaction.getTransactionDate(), finalIncreasePrincipalBy));
|
||||||
|
// Hence the rounding, we might need to amend the last installment amount
|
||||||
|
candidateRepaymentInstallments.get(noCandidateRepaymentInstallments - 1)
|
||||||
|
.addToPrincipal(disbursementTransaction.getTransactionDate(), remainingAmount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleRepayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
private void handleRepayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
|
|||||||
+1
-1
@@ -784,7 +784,7 @@ public class AccountingProcessorHelper {
|
|||||||
return this.officeRepository.getReferenceById(officeId);
|
return this.officeRepository.getReferenceById(officeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode, final int accountMappingTypeId,
|
public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode, final int accountMappingTypeId,
|
||||||
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId,
|
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId,
|
||||||
final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) {
|
final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) {
|
||||||
final GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId, paymentTypeId);
|
final GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId, paymentTypeId);
|
||||||
|
|||||||
+20
-16
@@ -33,6 +33,7 @@ import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
|||||||
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
|
import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -467,30 +468,34 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
// transaction properties
|
// transaction properties
|
||||||
final String transactionId = loanTransactionDTO.getTransactionId();
|
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||||
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
||||||
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
|
final BigDecimal overpaymentPortion = loanTransactionDTO.getOverPayment() != null ? loanTransactionDTO.getOverPayment()
|
||||||
|
: BigDecimal.ZERO;
|
||||||
|
final BigDecimal principalPortion = loanTransactionDTO.getAmount().subtract(overpaymentPortion);
|
||||||
final boolean isReversed = loanTransactionDTO.isReversed();
|
final boolean isReversed = loanTransactionDTO.isReversed();
|
||||||
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||||
|
|
||||||
// create journal entries for the disbursement (or disbursement
|
// create journal entries for the disbursement (or disbursement
|
||||||
// reversal)
|
// reversal)
|
||||||
if (loanTransactionDTO.isLoanToLoanTransfer()) {
|
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
|
||||||
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalPortion, isReversed);
|
||||||
FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
|
|
||||||
disbursalAmount, isReversed);
|
|
||||||
} else if (loanTransactionDTO.isAccountTransfer()) {
|
|
||||||
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
|
|
||||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
|
|
||||||
disbursalAmount, isReversed);
|
|
||||||
} else {
|
|
||||||
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
|
|
||||||
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
|
|
||||||
disbursalAmount, isReversed);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (MathUtil.isGreaterThanZero(overpaymentPortion)) {
|
||||||
|
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overpaymentPortion, isReversed);
|
||||||
|
}
|
||||||
|
if (loanTransactionDTO.isLoanToLoanTransfer()) {
|
||||||
|
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversed);
|
||||||
|
} else if (loanTransactionDTO.isAccountTransfer()) {
|
||||||
|
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversed);
|
||||||
|
} else {
|
||||||
|
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Handles repayments using the following posting rules <br/>
|
* Handles repayments using the following posting rules <br/>
|
||||||
* <br/>
|
* <br/>
|
||||||
* <br/>
|
* <br/>
|
||||||
@@ -520,7 +525,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
* @param loanTransactionDTO
|
* @param loanTransactionDTO
|
||||||
* @param loanDTO
|
* @param loanDTO
|
||||||
* @param office
|
* @param office
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
private void createJournalEntriesForRepaymentsAndWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
|
private void createJournalEntriesForRepaymentsAndWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
|
||||||
final Office office, final boolean writeOff, final boolean isIncomeFromFee) {
|
final Office office, final boolean writeOff, final boolean isIncomeFromFee) {
|
||||||
|
|||||||
+21
-14
@@ -32,6 +32,7 @@ import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
|||||||
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
|
import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -468,23 +469,29 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
|||||||
// transaction properties
|
// transaction properties
|
||||||
final String transactionId = loanTransactionDTO.getTransactionId();
|
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||||
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
||||||
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
|
final BigDecimal overpaymentPortion = loanTransactionDTO.getOverPayment() != null ? loanTransactionDTO.getOverPayment()
|
||||||
|
: BigDecimal.ZERO;
|
||||||
|
final BigDecimal principalPortion = loanTransactionDTO.getAmount().subtract(overpaymentPortion);
|
||||||
final boolean isReversal = loanTransactionDTO.isReversed();
|
final boolean isReversal = loanTransactionDTO.isReversed();
|
||||||
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||||
if (loanTransactionDTO.isLoanToLoanTransfer()) {
|
|
||||||
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
|
|
||||||
FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
|
|
||||||
disbursalAmount, isReversal);
|
|
||||||
} else if (loanTransactionDTO.isAccountTransfer()) {
|
|
||||||
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
|
|
||||||
FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
|
|
||||||
disbursalAmount, isReversal);
|
|
||||||
} else {
|
|
||||||
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
|
|
||||||
CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
|
|
||||||
disbursalAmount, isReversal);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalPortion, isReversal);
|
||||||
|
|
||||||
|
if (MathUtil.isGreaterThanZero(overpaymentPortion)) {
|
||||||
|
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overpaymentPortion, isReversal);
|
||||||
|
}
|
||||||
|
if (loanTransactionDTO.isLoanToLoanTransfer()) {
|
||||||
|
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversal);
|
||||||
|
} else if (loanTransactionDTO.isAccountTransfer()) {
|
||||||
|
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversal);
|
||||||
|
} else {
|
||||||
|
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(),
|
||||||
|
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+9
-9
@@ -427,7 +427,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
disburseLoanToLoan(loan, command, loanOutstanding);
|
disburseLoanToLoan(loan, command, loanOutstanding);
|
||||||
}
|
}
|
||||||
|
LoanTransaction disbursementTransaction = null;
|
||||||
if (isAccountTransfer) {
|
if (isAccountTransfer) {
|
||||||
disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
|
disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
@@ -435,11 +435,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
} else {
|
} else {
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), amountToDisburse, paymentDetail,
|
disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), amountToDisburse, paymentDetail,
|
||||||
actualDisbursementDate, txnExternalId);
|
actualDisbursementDate, txnExternalId);
|
||||||
disbursementTransaction.updateLoan(loan);
|
disbursementTransaction.updateLoan(loan);
|
||||||
loan.addLoanTransaction(disbursementTransaction);
|
loan.addLoanTransaction(disbursementTransaction);
|
||||||
LoanTransaction savedLoanTransaction = loanTransactionRepository.saveAndFlush(disbursementTransaction);
|
|
||||||
}
|
}
|
||||||
if (loan.getRepaymentScheduleInstallments().isEmpty()) {
|
if (loan.getRepaymentScheduleInstallments().isEmpty()) {
|
||||||
/*
|
/*
|
||||||
@@ -454,12 +453,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
if (isPaymentTypeApplicableForDisbursementCharge) {
|
if (isPaymentTypeApplicableForDisbursementCharge) {
|
||||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail,
|
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail);
|
||||||
downPaymentEnabled);
|
|
||||||
} else {
|
} else {
|
||||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null, downPaymentEnabled);
|
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null);
|
||||||
}
|
}
|
||||||
loan.adjustNetDisbursalAmount(amountToDisburse.getAmount());
|
loan.adjustNetDisbursalAmount(amountToDisburse.getAmount());
|
||||||
|
if (disbursementTransaction != null) {
|
||||||
|
loanTransactionRepository.saveAndFlush(disbursementTransaction);
|
||||||
|
}
|
||||||
if (loan.isAutoRepaymentForDownPaymentEnabled()) {
|
if (loan.isAutoRepaymentForDownPaymentEnabled()) {
|
||||||
// updating linked savings account for auto down payment transaction for disbursement to savings account
|
// updating linked savings account for auto down payment transaction for disbursement to savings account
|
||||||
if (isAccountTransfer && loan.shouldCreateStandingInstructionAtDisbursement()) {
|
if (isAccountTransfer && loan.shouldCreateStandingInstructionAtDisbursement()) {
|
||||||
@@ -764,10 +765,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
if (configurationDomainService.isPaymentTypeApplicableForDisbursementCharge()) {
|
if (configurationDomainService.isPaymentTypeApplicableForDisbursementCharge()) {
|
||||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail,
|
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail);
|
||||||
downPaymentEnabled);
|
|
||||||
} else {
|
} else {
|
||||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null, downPaymentEnabled);
|
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
|
|||||||
+81
@@ -46,6 +46,8 @@ import org.apache.fineract.client.models.PaymentAllocationOrder;
|
|||||||
import org.apache.fineract.client.models.PostClientsResponse;
|
import org.apache.fineract.client.models.PostClientsResponse;
|
||||||
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
|
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
|
||||||
import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse;
|
import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse;
|
||||||
|
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
||||||
|
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||||
@@ -3124,6 +3126,85 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UC122: Advanced payment allocation, 2nd disbursement on overpaid loan
|
||||||
|
// ADVANCED_PAYMENT_ALLOCATION_STRATEGY
|
||||||
|
// 1. Create a Loan product with Adv. Pment. Alloc.
|
||||||
|
// 2. Submit Loan and approve
|
||||||
|
// 3. Disburse only 100 from 1000
|
||||||
|
// 4. Overpay the loan (110)
|
||||||
|
// 5. Disburse again 100
|
||||||
|
@Test
|
||||||
|
public void uc122() {
|
||||||
|
runAt("24 November 2023", () -> {
|
||||||
|
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||||
|
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||||
|
.numberOfRepayments(3).repaymentEvery(15).enableDownPayment(true)
|
||||||
|
.disbursedAmountPercentageForDownPayment(BigDecimal.valueOf(25)).enableAutoRepaymentForDownPayment(false);
|
||||||
|
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
|
||||||
|
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), "22 November 2023",
|
||||||
|
1000.0, 4);
|
||||||
|
|
||||||
|
applicationRequest = applicationRequest.numberOfRepayments(3).loanTermFrequency(45)
|
||||||
|
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY).repaymentEvery(15);
|
||||||
|
|
||||||
|
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
|
||||||
|
|
||||||
|
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
|
||||||
|
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
|
||||||
|
.approvedOnDate("22 November 2023").locale("en"));
|
||||||
|
|
||||||
|
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
|
||||||
|
new PostLoansLoanIdRequest().actualDisbursementDate("22 November 2023").dateFormat(DATETIME_PATTERN)
|
||||||
|
.transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 100.0, 0.0, 100.0, 0.0, null);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), 25.0, 0.0, 25.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 12, 7), 25.0, 0.0, 25.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 12, 22), 25.0, 0.0, 25.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2024, 1, 6), 25.0, 0.0, 25.0, 0.0, 0.0);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
|
||||||
|
loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(), new PostLoansLoanIdTransactionsRequest()
|
||||||
|
.dateFormat(DATETIME_PATTERN).transactionDate("23 November 2023").locale("en").transactionAmount(110.0));
|
||||||
|
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 10.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), 25.0, 25.0, 0.0, 0.0, 25.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 12, 7), 25.0, 25.0, 0.0, 25.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 12, 22), 25.0, 25.0, 0.0, 25.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2024, 1, 6), 25.0, 25.0, 0.0, 25.0, 0.0);
|
||||||
|
assertTrue(loanDetails.getStatus().getOverpaid());
|
||||||
|
|
||||||
|
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
|
||||||
|
new PostLoansLoanIdRequest().actualDisbursementDate("24 November 2023").dateFormat(DATETIME_PATTERN)
|
||||||
|
.transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
|
||||||
|
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 90.0, 110.0, 90.0, 110.0, null);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), 25.0, 25.0, 0.0, 0.0, 25.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 11, 24), 25.0, 10.0, 15.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 12, 7), 50.0, 25.0, 25.0, 25.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 12, 22), 50.0, 25.0, 25.0, 25.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 5, LocalDate.of(2024, 1, 6), 50.0, 25.0, 25.0, 25.0, 0.0);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
|
||||||
|
verifyTransactions(loanResponse.getLoanId(), //
|
||||||
|
transaction(100, "Disbursement", "22 November 2023", 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
||||||
|
transaction(110, "Repayment", "23 November 2023", 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 10.0), //
|
||||||
|
transaction(100, "Disbursement", "24 November 2023", 90.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0) //
|
||||||
|
);
|
||||||
|
// verify journal entries
|
||||||
|
verifyJournalEntries(loanResponse.getLoanId(), journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
|
||||||
|
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
|
||||||
|
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
|
||||||
|
journalEntry(10.0, overpaymentAccount, "CREDIT"), //
|
||||||
|
journalEntry(110.0, suspenseClearingAccount, "DEBIT"), //
|
||||||
|
journalEntry(90.0, loansReceivableAccount, "DEBIT"), //
|
||||||
|
journalEntry(10.0, overpaymentAccount, "DEBIT"), //
|
||||||
|
journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static void validateLoanSummaryBalances(GetLoansLoanIdResponse loanDetails, Double totalOutstanding, Double totalRepayment,
|
private static void validateLoanSummaryBalances(GetLoansLoanIdResponse loanDetails, Double totalOutstanding, Double totalRepayment,
|
||||||
Double principalOutstanding, Double principalPaid, Double totalOverpaid) {
|
Double principalOutstanding, Double principalPaid, Double totalOverpaid) {
|
||||||
assertEquals(totalOutstanding, loanDetails.getSummary().getTotalOutstanding());
|
assertEquals(totalOutstanding, loanDetails.getSummary().getTotalOutstanding());
|
||||||
|
|||||||
+9
-9
@@ -66,8 +66,8 @@ public class AdvancedPaymentAllocationWaiveLoanCharges extends BaseLoanIntegrati
|
|||||||
|
|
||||||
// Then verify
|
// Then verify
|
||||||
verifyTransactions(loanId, //
|
verifyTransactions(loanId, //
|
||||||
transaction(1000, "Disbursement", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
transaction(1000, "Disbursement", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(50, "Waive loan charges", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 50.0) //
|
transaction(50, "Waive loan charges", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 50.0, 0.0) //
|
||||||
);
|
);
|
||||||
|
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
||||||
@@ -99,8 +99,8 @@ public class AdvancedPaymentAllocationWaiveLoanCharges extends BaseLoanIntegrati
|
|||||||
|
|
||||||
// Then verify
|
// Then verify
|
||||||
verifyTransactions(loanId, //
|
verifyTransactions(loanId, //
|
||||||
transaction(1000, "Disbursement", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
transaction(1000, "Disbursement", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(50, "Waive loan charges", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 50.0) //
|
transaction(50, "Waive loan charges", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 50.0, 0.0) //
|
||||||
);
|
);
|
||||||
|
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
||||||
@@ -137,8 +137,8 @@ public class AdvancedPaymentAllocationWaiveLoanCharges extends BaseLoanIntegrati
|
|||||||
|
|
||||||
// Then verify
|
// Then verify
|
||||||
verifyTransactions(loanId, //
|
verifyTransactions(loanId, //
|
||||||
transaction(1000, "Disbursement", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
transaction(1000, "Disbursement", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(50, "Waive loan charges", "05 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 50.0) //
|
transaction(50, "Waive loan charges", "05 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 50.0, 0.0) //
|
||||||
);
|
);
|
||||||
|
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
||||||
@@ -151,9 +151,9 @@ public class AdvancedPaymentAllocationWaiveLoanCharges extends BaseLoanIntegrati
|
|||||||
addRepaymentForLoan(loanId, 200.0, "03 January 2023");
|
addRepaymentForLoan(loanId, 200.0, "03 January 2023");
|
||||||
|
|
||||||
verifyTransactions(loanId, //
|
verifyTransactions(loanId, //
|
||||||
transaction(1000, "Disbursement", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
transaction(1000, "Disbursement", "01 January 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(200, "Repayment", "03 January 2023", 800.0, 200.0, 0.0, 0.0, 0.0, 0.0), //
|
transaction(200, "Repayment", "03 January 2023", 800.0, 200.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(50, "Waive loan charges", "05 January 2023", 800.0, 0.0, 0.0, 0.0, 0.0, 50.0) //
|
transaction(50, "Waive loan charges", "05 January 2023", 800.0, 0.0, 0.0, 0.0, 0.0, 50.0, 0.0) //
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -489,9 +489,9 @@ public abstract class BaseLoanIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected TransactionExt transaction(double amount, String type, String date, double outstandingAmount, double principalPortion,
|
protected TransactionExt transaction(double amount, String type, String date, double outstandingAmount, double principalPortion,
|
||||||
double interestPortion, double feePortion, double penaltyPortion, double unrecognizedIncomePortion) {
|
double interestPortion, double feePortion, double penaltyPortion, double unrecognizedIncomePortion, double overpaymentPortion) {
|
||||||
return new TransactionExt(amount, type, date, outstandingAmount, principalPortion, interestPortion, feePortion, penaltyPortion,
|
return new TransactionExt(amount, type, date, outstandingAmount, principalPortion, interestPortion, feePortion, penaltyPortion,
|
||||||
unrecognizedIncomePortion);
|
unrecognizedIncomePortion, overpaymentPortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Installment installment(double principalAmount, Boolean completed, String dueDate) {
|
protected Installment installment(double principalAmount, Boolean completed, String dueDate) {
|
||||||
@@ -530,6 +530,7 @@ public abstract class BaseLoanIntegrationTest {
|
|||||||
Double feePortion;
|
Double feePortion;
|
||||||
Double penaltyPortion;
|
Double penaltyPortion;
|
||||||
Double unrecognizedPortion;
|
Double unrecognizedPortion;
|
||||||
|
Double overpaymentPortion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ToString
|
@ToString
|
||||||
|
|||||||
Reference in New Issue
Block a user