FINERACT-1968: Fix overpayment calculation
This commit is contained in:
+7
-6
@@ -111,6 +111,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanCollateralManagementDa
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||||
@@ -797,7 +798,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
final Set<LoanCharge> loanCharges = new HashSet<>(1);
|
final Set<LoanCharge> loanCharges = new HashSet<>(1);
|
||||||
loanCharges.add(charge);
|
loanCharges.add(charge);
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargesPayment, getCurrency(), chargePaymentInstallments,
|
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargesPayment, getCurrency(), chargePaymentInstallments,
|
||||||
loanCharges, getTotalOverpaidAsMoney());
|
loanCharges, new MoneyHolder(getTotalOverpaidAsMoney()));
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), loanLifecycleStateMachine);
|
doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), loanLifecycleStateMachine);
|
||||||
@@ -3321,7 +3322,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
if (isTransactionChronologicallyLatest && adjustedTransaction == null
|
if (isTransactionChronologicallyLatest && adjustedTransaction == null
|
||||||
&& (!reprocess || !this.repaymentScheduleDetail().isInterestRecalculationEnabled()) && !isForeclosure()) {
|
&& (!reprocess || !this.repaymentScheduleDetail().isInterestRecalculationEnabled()) && !isForeclosure()) {
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, getCurrency(),
|
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, getCurrency(),
|
||||||
getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney());
|
getRepaymentScheduleInstallments(), getActiveCharges(), new MoneyHolder(getTotalOverpaidAsMoney()));
|
||||||
reprocess = false;
|
reprocess = false;
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) {
|
if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) {
|
||||||
@@ -3914,7 +3915,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
|
|
||||||
addLoanTransaction(loanTransaction);
|
addLoanTransaction(loanTransaction);
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, loanCurrency(),
|
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, loanCurrency(),
|
||||||
getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney());
|
getRepaymentScheduleInstallments(), getActiveCharges(), new MoneyHolder(getTotalOverpaidAsMoney()));
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
}
|
}
|
||||||
@@ -4019,7 +4020,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
|
|
||||||
addLoanTransaction(loanTransaction);
|
addLoanTransaction(loanTransaction);
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, loanCurrency(),
|
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, loanCurrency(),
|
||||||
getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney());
|
getRepaymentScheduleInstallments(), getActiveCharges(), new MoneyHolder(getTotalOverpaidAsMoney()));
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
} else if (totalOutstanding.isGreaterThanZero()) {
|
} else if (totalOutstanding.isGreaterThanZero()) {
|
||||||
@@ -6373,7 +6374,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
// If is a refund
|
// If is a refund
|
||||||
if (adjustedTransaction == null) {
|
if (adjustedTransaction == null) {
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, getCurrency(),
|
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, getCurrency(),
|
||||||
getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney());
|
getRepaymentScheduleInstallments(), getActiveCharges(), new MoneyHolder(getTotalOverpaidAsMoney()));
|
||||||
} else {
|
} else {
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(),
|
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(),
|
||||||
@@ -6404,7 +6405,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
|
|
||||||
addLoanTransaction(chargebackTransaction);
|
addLoanTransaction(chargebackTransaction);
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargebackTransaction, getCurrency(),
|
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargebackTransaction, getCurrency(),
|
||||||
getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney());
|
getRepaymentScheduleInstallments(), getActiveCharges(), new MoneyHolder(getTotalOverpaidAsMoney()));
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
if (!doPostLoanTransactionChecks(chargebackTransaction.getTransactionDate(), loanLifecycleStateMachine)) {
|
if (!doPostLoanTransactionChecks(chargebackTransaction.getTransactionDate(), loanLifecycleStateMachine)) {
|
||||||
|
|||||||
-5
@@ -483,11 +483,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
.plus(getPenaltyChargesPortion(currency)).getAmount();
|
.plus(getPenaltyChargesPortion(currency)).getAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateOverPayments(final Money overPayment) {
|
|
||||||
final MonetaryCurrency currency = overPayment.getCurrency();
|
|
||||||
this.overPaymentPortion = defaultToNullIfZero(getOverPaymentPortion(currency).plus(overPayment).getAmount());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOverPayments(final Money overPayment) {
|
public void setOverPayments(final Money overPayment) {
|
||||||
if (overPayment != null) {
|
if (overPayment != null) {
|
||||||
this.overPaymentPortion = defaultToNullIfZero(overPayment.getAmount());
|
this.overPaymentPortion = defaultToNullIfZero(overPayment.getAmount());
|
||||||
|
|||||||
+27
-55
@@ -144,7 +144,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
if (unprocessed.isGreaterThanZero()) {
|
if (unprocessed.isGreaterThanZero()) {
|
||||||
onLoanOverpayment(loanTransaction, unprocessed);
|
onLoanOverpayment(loanTransaction, unprocessed);
|
||||||
loanTransaction.updateOverPayments(unprocessed);
|
loanTransaction.setOverPayments(unprocessed);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -152,6 +152,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MoneyHolder overpaymentHolder = new MoneyHolder(Money.zero(currency));
|
||||||
for (final LoanTransaction loanTransaction : transactionsToBeProcessed) {
|
for (final LoanTransaction loanTransaction : transactionsToBeProcessed) {
|
||||||
// TODO: analyze and remove this
|
// TODO: analyze and remove this
|
||||||
if (!loanTransaction.getTypeOf().equals(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN)) {
|
if (!loanTransaction.getTypeOf().equals(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN)) {
|
||||||
@@ -163,7 +164,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||||
// pass through for new transactions
|
// pass through for new transactions
|
||||||
if (loanTransaction.getId() == null) {
|
if (loanTransaction.getId() == null) {
|
||||||
processLatestTransaction(loanTransaction, currency, installments, charges, null);
|
processLatestTransaction(loanTransaction, currency, installments, charges, overpaymentHolder);
|
||||||
loanTransaction.adjustInterestComponent(currency);
|
loanTransaction.adjustInterestComponent(currency);
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
@@ -174,7 +175,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
// Reset derived component of new loan transaction and
|
// Reset derived component of new loan transaction and
|
||||||
// re-process transaction
|
// re-process transaction
|
||||||
processLatestTransaction(newLoanTransaction, currency, installments, charges, null);
|
processLatestTransaction(newLoanTransaction, currency, installments, charges, overpaymentHolder);
|
||||||
newLoanTransaction.adjustInterestComponent(currency);
|
newLoanTransaction.adjustInterestComponent(currency);
|
||||||
/**
|
/**
|
||||||
* Check if the transaction amounts have changed. If so, reverse the original transaction and update
|
* Check if the transaction amounts have changed. If so, reverse the original transaction and update
|
||||||
@@ -195,9 +196,11 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
loanTransaction.resetDerivedComponents();
|
loanTransaction.resetDerivedComponents();
|
||||||
handleRefund(loanTransaction, currency, installments, charges);
|
handleRefund(loanTransaction, currency, installments, charges);
|
||||||
} else if (loanTransaction.isCreditBalanceRefund()) {
|
} else if (loanTransaction.isCreditBalanceRefund()) {
|
||||||
recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, transactionsToBeProcessed);
|
recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, transactionsToBeProcessed,
|
||||||
|
overpaymentHolder);
|
||||||
} else if (loanTransaction.isChargeback()) {
|
} else if (loanTransaction.isChargeback()) {
|
||||||
recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, transactionsToBeProcessed);
|
recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, transactionsToBeProcessed,
|
||||||
|
overpaymentHolder);
|
||||||
reprocessChargebackTransactionRelation(changedTransactionDetail, transactionsToBeProcessed);
|
reprocessChargebackTransactionRelation(changedTransactionDetail, transactionsToBeProcessed);
|
||||||
} else if (loanTransaction.isChargeOff()) {
|
} else if (loanTransaction.isChargeOff()) {
|
||||||
recalculateChargeOffTransaction(changedTransactionDetail, loanTransaction, currency, installments);
|
recalculateChargeOffTransaction(changedTransactionDetail, loanTransaction, currency, installments);
|
||||||
@@ -209,11 +212,11 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processLatestTransaction(final LoanTransaction loanTransaction, final MonetaryCurrency currency,
|
public void processLatestTransaction(final LoanTransaction loanTransaction, final MonetaryCurrency currency,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, Money overpaidAmount) {
|
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, MoneyHolder overpaymentHolder) {
|
||||||
switch (loanTransaction.getTypeOf()) {
|
switch (loanTransaction.getTypeOf()) {
|
||||||
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, overpaidAmount, installments);
|
case CHARGEBACK -> handleChargeback(loanTransaction, currency, installments, overpaymentHolder);
|
||||||
default -> {
|
default -> {
|
||||||
Money transactionAmountUnprocessed = handleTransactionAndCharges(loanTransaction, currency, installments, charges, null,
|
Money transactionAmountUnprocessed = handleTransactionAndCharges(loanTransaction, currency, installments, charges, null,
|
||||||
false);
|
false);
|
||||||
@@ -223,8 +226,11 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
transactionAmountUnprocessed.zero(), transactionAmountUnprocessed.zero());
|
transactionAmountUnprocessed.zero(), transactionAmountUnprocessed.zero());
|
||||||
} else {
|
} else {
|
||||||
onLoanOverpayment(loanTransaction, transactionAmountUnprocessed);
|
onLoanOverpayment(loanTransaction, transactionAmountUnprocessed);
|
||||||
loanTransaction.updateOverPayments(transactionAmountUnprocessed);
|
loanTransaction.setOverPayments(transactionAmountUnprocessed);
|
||||||
}
|
}
|
||||||
|
overpaymentHolder.setMoneyObject(transactionAmountUnprocessed);
|
||||||
|
} else {
|
||||||
|
overpaymentHolder.setMoneyObject(Money.zero(currency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,17 +441,15 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void recalculateCreditTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction,
|
private void recalculateCreditTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction,
|
||||||
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments,
|
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments, List<LoanTransaction> transactionsToBeProcessed,
|
||||||
List<LoanTransaction> transactionsToBeProcessed) {
|
MoneyHolder overpaymentHolder) {
|
||||||
// pass through for new transactions
|
// pass through for new transactions
|
||||||
if (loanTransaction.getId() == null) {
|
if (loanTransaction.getId() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||||
|
|
||||||
List<LoanTransaction> mergedList = getMergedTransactionList(transactionsToBeProcessed, changedTransactionDetail);
|
processCreditTransaction(newLoanTransaction, overpaymentHolder, currency, installments);
|
||||||
Money overpaidAmount = calculateOverpaidAmount(loanTransaction, mergedList, installments, currency);
|
|
||||||
processCreditTransaction(newLoanTransaction, overpaidAmount, currency, installments);
|
|
||||||
if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
|
if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
|
||||||
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
|
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
|
||||||
}
|
}
|
||||||
@@ -470,40 +474,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Money calculateOverpaidAmount(LoanTransaction loanTransaction, List<LoanTransaction> transactions,
|
private void processCreditTransaction(LoanTransaction loanTransaction, MoneyHolder overpaymentHolder, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency) {
|
|
||||||
Money totalPaidInRepayments = Money.zero(currency);
|
|
||||||
|
|
||||||
Money cumulativeTotalPaidOnInstallments = Money.zero(currency);
|
|
||||||
for (final LoanRepaymentScheduleInstallment scheduledRepayment : installments) {
|
|
||||||
cumulativeTotalPaidOnInstallments = cumulativeTotalPaidOnInstallments
|
|
||||||
.plus(scheduledRepayment.getPrincipalCompleted(currency).plus(scheduledRepayment.getInterestPaid(currency)))
|
|
||||||
.plus(scheduledRepayment.getFeeChargesPaid(currency)).plus(scheduledRepayment.getPenaltyChargesPaid(currency));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final LoanTransaction transaction : transactions) {
|
|
||||||
if (transaction.isReversed()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (transaction.equals(loanTransaction)) {
|
|
||||||
// We want to process only the transactions prior to the actual one
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (transaction.isRefund() || transaction.isRefundForActiveLoan()) {
|
|
||||||
totalPaidInRepayments = totalPaidInRepayments.minus(transaction.getAmount(currency));
|
|
||||||
} else if (transaction.isCreditBalanceRefund() || transaction.isChargeback()) {
|
|
||||||
totalPaidInRepayments = totalPaidInRepayments.minus(transaction.getOverPaymentPortion(currency));
|
|
||||||
} else if (transaction.isRepaymentLikeType()) {
|
|
||||||
totalPaidInRepayments = totalPaidInRepayments.plus(transaction.getAmount(currency));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if total paid in transactions higher than repayment schedule then
|
|
||||||
// theres an overpayment.
|
|
||||||
return MathUtil.negativeToZero(totalPaidInRepayments.minus(cumulativeTotalPaidOnInstallments));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processCreditTransaction(LoanTransaction loanTransaction, Money overpaidAmount, MonetaryCurrency currency,
|
|
||||||
List<LoanRepaymentScheduleInstallment> installments) {
|
List<LoanRepaymentScheduleInstallment> installments) {
|
||||||
loanTransaction.resetDerivedComponents();
|
loanTransaction.resetDerivedComponents();
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = new ArrayList<>();
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = new ArrayList<>();
|
||||||
@@ -511,9 +482,10 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
installments.sort(byDate);
|
installments.sort(byDate);
|
||||||
final Money zeroMoney = Money.zero(currency);
|
final Money zeroMoney = Money.zero(currency);
|
||||||
Money transactionAmount = loanTransaction.getAmount(currency);
|
Money transactionAmount = loanTransaction.getAmount(currency);
|
||||||
Money principalPortion = MathUtil.negativeToZero(loanTransaction.getAmount(currency).minus(overpaidAmount));
|
Money principalPortion = MathUtil.negativeToZero(loanTransaction.getAmount(currency).minus(overpaymentHolder.getMoneyObject()));
|
||||||
Money repaidAmount = MathUtil.negativeToZero(transactionAmount.minus(principalPortion));
|
Money repaidAmount = MathUtil.negativeToZero(transactionAmount.minus(principalPortion));
|
||||||
loanTransaction.updateOverPayments(repaidAmount);
|
loanTransaction.setOverPayments(repaidAmount);
|
||||||
|
overpaymentHolder.setMoneyObject(overpaymentHolder.getMoneyObject().minus(repaidAmount));
|
||||||
loanTransaction.updateComponents(principalPortion, zeroMoney, zeroMoney, zeroMoney);
|
loanTransaction.updateComponents(principalPortion, zeroMoney, zeroMoney, zeroMoney);
|
||||||
|
|
||||||
if (principalPortion.isGreaterThanZero()) {
|
if (principalPortion.isGreaterThanZero()) {
|
||||||
@@ -770,14 +742,14 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
|
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleChargeback(LoanTransaction loanTransaction, MonetaryCurrency currency, Money overpaidAmount,
|
protected void handleChargeback(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments) {
|
List<LoanRepaymentScheduleInstallment> installments, MoneyHolder overpaidAmountHolder) {
|
||||||
processCreditTransaction(loanTransaction, overpaidAmount, currency, installments);
|
processCreditTransaction(loanTransaction, overpaidAmountHolder, currency, installments);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleCreditBalanceRefund(LoanTransaction loanTransaction, MonetaryCurrency currency, Money overpaidAmount,
|
protected void handleCreditBalanceRefund(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments) {
|
List<LoanRepaymentScheduleInstallment> installments, MoneyHolder overpaidAmountHolder) {
|
||||||
processCreditTransaction(loanTransaction, overpaidAmount, currency, installments);
|
processCreditTransaction(loanTransaction, overpaidAmountHolder, currency, installments);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
protected void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ public interface LoanRepaymentScheduleTransactionProcessor {
|
|||||||
* schedule.
|
* schedule.
|
||||||
*/
|
*/
|
||||||
void processLatestTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
void processLatestTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, Money overpaidAmount);
|
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, MoneyHolder overpaymentHolder);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides support for passing all {@link LoanTransaction}'s so it will completely re-process the entire loan
|
* Provides support for passing all {@link LoanTransaction}'s so it will completely re-process the entire loan
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class MoneyHolder {
|
||||||
|
|
||||||
|
private Money moneyObject;
|
||||||
|
}
|
||||||
+24
-18
@@ -55,6 +55,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.DueType;
|
import org.apache.fineract.portfolio.loanproduct.domain.DueType;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.FutureInstallmentAllocationRule;
|
import org.apache.fineract.portfolio.loanproduct.domain.FutureInstallmentAllocationRule;
|
||||||
@@ -136,9 +137,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
List<ChargeOrTransaction> chargeOrTransactions = createSortedChargesAndTransactionsList(loanTransactions, charges);
|
List<ChargeOrTransaction> chargeOrTransactions = createSortedChargesAndTransactionsList(loanTransactions, charges);
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
|
final ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
|
||||||
|
MoneyHolder overpaymentHolder = new MoneyHolder(Money.zero(currency));
|
||||||
for (final ChargeOrTransaction chargeOrTransaction : chargeOrTransactions) {
|
for (final ChargeOrTransaction chargeOrTransaction : chargeOrTransactions) {
|
||||||
chargeOrTransaction.getLoanTransaction().ifPresent(loanTransaction -> processSingleTransaction(loanTransaction, currency,
|
chargeOrTransaction.getLoanTransaction().ifPresent(loanTransaction -> processSingleTransaction(loanTransaction, currency,
|
||||||
installments, charges, changedTransactionDetail));
|
installments, charges, changedTransactionDetail, overpaymentHolder));
|
||||||
chargeOrTransaction.getLoanCharge()
|
chargeOrTransaction.getLoanCharge()
|
||||||
.ifPresent(loanCharge -> processSingleCharge(loanCharge, currency, installments, disbursementDate));
|
.ifPresent(loanCharge -> processSingleCharge(loanCharge, currency, installments, disbursementDate));
|
||||||
}
|
}
|
||||||
@@ -150,18 +152,18 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processLatestTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
public void processLatestTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, Money overpaidAmount) {
|
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);
|
||||||
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, overpaidAmount, installments);
|
case CHARGEBACK -> handleChargeback(loanTransaction, currency, installments, overpaymentHolder);
|
||||||
case CREDIT_BALANCE_REFUND -> handleCreditBalanceRefund(loanTransaction, currency, overpaidAmount, installments);
|
case CREDIT_BALANCE_REFUND -> handleCreditBalanceRefund(loanTransaction, currency, installments, overpaymentHolder);
|
||||||
case REPAYMENT, MERCHANT_ISSUED_REFUND, PAYOUT_REFUND, GOODWILL_CREDIT, CHARGE_REFUND, CHARGE_ADJUSTMENT, DOWN_PAYMENT,
|
case REPAYMENT, MERCHANT_ISSUED_REFUND, PAYOUT_REFUND, GOODWILL_CREDIT, CHARGE_REFUND, CHARGE_ADJUSTMENT, DOWN_PAYMENT,
|
||||||
WAIVE_INTEREST, RECOVERY_REPAYMENT ->
|
WAIVE_INTEREST, RECOVERY_REPAYMENT ->
|
||||||
handleRepayment(loanTransaction, currency, installments, charges);
|
handleRepayment(loanTransaction, currency, installments, charges, overpaymentHolder);
|
||||||
case CHARGE_OFF -> handleChargeOff(loanTransaction, currency, installments);
|
case CHARGE_OFF -> handleChargeOff(loanTransaction, currency, installments);
|
||||||
case CHARGE_PAYMENT -> handleChargePayment(loanTransaction, currency, installments, charges);
|
case CHARGE_PAYMENT -> handleChargePayment(loanTransaction, currency, installments, charges, overpaymentHolder);
|
||||||
case WAIVE_CHARGES -> log.debug("WAIVE_CHARGES transaction will not be processed.");
|
case WAIVE_CHARGES -> log.debug("WAIVE_CHARGES transaction will not be processed.");
|
||||||
// TODO: Cover rest of the transaction types
|
// TODO: Cover rest of the transaction types
|
||||||
default -> {
|
default -> {
|
||||||
@@ -226,10 +228,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processSingleTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
private void processSingleTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges,
|
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, ChangedTransactionDetail changedTransactionDetail,
|
||||||
ChangedTransactionDetail changedTransactionDetail) {
|
MoneyHolder overpaymentHolder) {
|
||||||
if (loanTransaction.getId() == null) {
|
if (loanTransaction.getId() == null) {
|
||||||
processLatestTransaction(loanTransaction, currency, installments, charges, Money.zero(currency));
|
processLatestTransaction(loanTransaction, currency, installments, charges, overpaymentHolder);
|
||||||
if (loanTransaction.isInterestWaiver()) {
|
if (loanTransaction.isInterestWaiver()) {
|
||||||
loanTransaction.adjustInterestComponent(currency);
|
loanTransaction.adjustInterestComponent(currency);
|
||||||
}
|
}
|
||||||
@@ -242,7 +244,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
|
|
||||||
// Reset derived component of new loan transaction and
|
// Reset derived component of new loan transaction and
|
||||||
// re-process transaction
|
// re-process transaction
|
||||||
processLatestTransaction(newLoanTransaction, currency, installments, charges, Money.zero(currency));
|
processLatestTransaction(newLoanTransaction, currency, installments, charges, overpaymentHolder);
|
||||||
if (loanTransaction.isInterestWaiver()) {
|
if (loanTransaction.isInterestWaiver()) {
|
||||||
newLoanTransaction.adjustInterestComponent(currency);
|
newLoanTransaction.adjustInterestComponent(currency);
|
||||||
}
|
}
|
||||||
@@ -322,12 +324,12 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleRepayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
private void handleRepayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges) {
|
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, MoneyHolder overpaymentHolder) {
|
||||||
if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||||
loanTransaction.resetDerivedComponents();
|
loanTransaction.resetDerivedComponents();
|
||||||
}
|
}
|
||||||
Money transactionAmountUnprocessed = loanTransaction.getAmount(currency);
|
Money transactionAmountUnprocessed = loanTransaction.getAmount(currency);
|
||||||
processTransaction(loanTransaction, currency, installments, transactionAmountUnprocessed, charges);
|
processTransaction(loanTransaction, currency, installments, transactionAmountUnprocessed, charges, overpaymentHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanTransactionToRepaymentScheduleMapping getTransactionMapping(
|
private LoanTransactionToRepaymentScheduleMapping getTransactionMapping(
|
||||||
@@ -399,10 +401,13 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
loanTransactionToRepaymentScheduleMapping.setComponents(aggregatedPrincipal, aggregatedInterest, aggregatedFee, aggregatedPenalty);
|
loanTransactionToRepaymentScheduleMapping.setComponents(aggregatedPrincipal, aggregatedInterest, aggregatedFee, aggregatedPenalty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleOverpayment(Money overpaymentPortion, LoanTransaction loanTransaction) {
|
private void handleOverpayment(Money overpaymentPortion, LoanTransaction loanTransaction, MoneyHolder overpaymentHolder) {
|
||||||
if (overpaymentPortion.isGreaterThanZero()) {
|
if (overpaymentPortion.isGreaterThanZero()) {
|
||||||
onLoanOverpayment(loanTransaction, overpaymentPortion);
|
onLoanOverpayment(loanTransaction, overpaymentPortion);
|
||||||
loanTransaction.updateOverPayments(overpaymentPortion);
|
overpaymentHolder.setMoneyObject(overpaymentPortion);
|
||||||
|
loanTransaction.setOverPayments(overpaymentPortion);
|
||||||
|
} else {
|
||||||
|
overpaymentHolder.setMoneyObject(overpaymentPortion.zero());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +432,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleChargePayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
private void handleChargePayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges) {
|
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, MoneyHolder overpaymentHolder) {
|
||||||
Money zero = Money.zero(currency);
|
Money zero = Money.zero(currency);
|
||||||
Money feeChargesPortion = zero;
|
Money feeChargesPortion = zero;
|
||||||
Money penaltyChargesPortion = zero;
|
Money penaltyChargesPortion = zero;
|
||||||
@@ -473,7 +478,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unprocessed.isGreaterThanZero()) {
|
if (unprocessed.isGreaterThanZero()) {
|
||||||
processTransaction(loanTransaction, currency, installments, unprocessed, charges);
|
processTransaction(loanTransaction, currency, installments, unprocessed, charges, overpaymentHolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,7 +663,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
private void processTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, Money transactionAmountUnprocessed, Set<LoanCharge> charges) {
|
List<LoanRepaymentScheduleInstallment> installments, Money transactionAmountUnprocessed, Set<LoanCharge> charges,
|
||||||
|
MoneyHolder overpaymentHolder) {
|
||||||
Money zero = Money.zero(currency);
|
Money zero = Money.zero(currency);
|
||||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = new ArrayList<>();
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = new ArrayList<>();
|
||||||
|
|
||||||
@@ -683,7 +689,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
balances.getAggregatedFeeChargesPortion(), balances.getAggregatedPenaltyChargesPortion());
|
balances.getAggregatedFeeChargesPortion(), balances.getAggregatedPenaltyChargesPortion());
|
||||||
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
|
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
|
||||||
|
|
||||||
handleOverpayment(transactionAmountUnprocessed, loanTransaction);
|
handleOverpayment(transactionAmountUnprocessed, loanTransaction, overpaymentHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Money processPeriodsHorizontally(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
private Money processPeriodsHorizontally(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
|
|||||||
+2
-1
@@ -114,6 +114,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationT
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.InstallmentNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.InstallmentNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||||
@@ -832,7 +833,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||||
.determineProcessor(loan.transactionProcessingStrategy());
|
.determineProcessor(loan.transactionProcessingStrategy());
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction, loan.getCurrency(),
|
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction, loan.getCurrency(),
|
||||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), loan.getTotalOverpaidAsMoney());
|
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()));
|
||||||
|
|
||||||
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
||||||
loan.updateLoanSummaryAndStatus();
|
loan.updateLoanSummaryAndStatus();
|
||||||
|
|||||||
+7
-3
@@ -43,6 +43,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRul
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationTransactionType;
|
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationTransactionType;
|
||||||
@@ -121,7 +122,8 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
|||||||
Mockito.when(charge.updatePaidAmountBy(refEq(chargeAmountMoney), eq(1), refEq(zero))).thenReturn(chargeAmountMoney);
|
Mockito.when(charge.updatePaidAmountBy(refEq(chargeAmountMoney), eq(1), refEq(zero))).thenReturn(chargeAmountMoney);
|
||||||
Mockito.when(loanTransaction.isPenaltyPayment()).thenReturn(false);
|
Mockito.when(loanTransaction.isPenaltyPayment()).thenReturn(false);
|
||||||
|
|
||||||
underTest.processLatestTransaction(loanTransaction, currency, List.of(installment), Set.of(charge), overpaidAmount);
|
underTest.processLatestTransaction(loanTransaction, currency, List.of(installment), Set.of(charge),
|
||||||
|
new MoneyHolder(overpaidAmount));
|
||||||
|
|
||||||
Mockito.verify(installment, Mockito.times(1)).payFeeChargesComponent(eq(transactionDate), eq(chargeAmountMoney));
|
Mockito.verify(installment, Mockito.times(1)).payFeeChargesComponent(eq(transactionDate), eq(chargeAmountMoney));
|
||||||
Mockito.verify(loanTransaction, Mockito.times(1)).updateComponents(refEq(zero), refEq(zero), refEq(chargeAmountMoney), refEq(zero));
|
Mockito.verify(loanTransaction, Mockito.times(1)).updateComponents(refEq(zero), refEq(zero), refEq(chargeAmountMoney), refEq(zero));
|
||||||
@@ -165,7 +167,8 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
|||||||
Mockito.when(charge.updatePaidAmountBy(refEq(transactionAmountMoney), eq(1), refEq(zero))).thenReturn(transactionAmountMoney);
|
Mockito.when(charge.updatePaidAmountBy(refEq(transactionAmountMoney), eq(1), refEq(zero))).thenReturn(transactionAmountMoney);
|
||||||
Mockito.when(loanTransaction.isPenaltyPayment()).thenReturn(false);
|
Mockito.when(loanTransaction.isPenaltyPayment()).thenReturn(false);
|
||||||
|
|
||||||
underTest.processLatestTransaction(loanTransaction, currency, List.of(installment), Set.of(charge), overpaidAmount);
|
underTest.processLatestTransaction(loanTransaction, currency, List.of(installment), Set.of(charge),
|
||||||
|
new MoneyHolder(overpaidAmount));
|
||||||
|
|
||||||
Mockito.verify(installment, Mockito.times(1)).payFeeChargesComponent(eq(transactionDate), eq(transactionAmountMoney));
|
Mockito.verify(installment, Mockito.times(1)).payFeeChargesComponent(eq(transactionDate), eq(transactionAmountMoney));
|
||||||
Mockito.verify(loanTransaction, Mockito.times(1)).updateComponents(refEq(zero), refEq(zero), refEq(transactionAmountMoney),
|
Mockito.verify(loanTransaction, Mockito.times(1)).updateComponents(refEq(zero), refEq(zero), refEq(transactionAmountMoney),
|
||||||
@@ -218,7 +221,8 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
|||||||
Mockito.when(loanPaymentAllocationRule.getAllocationTypes()).thenReturn(List.of(PaymentAllocationType.DUE_PRINCIPAL));
|
Mockito.when(loanPaymentAllocationRule.getAllocationTypes()).thenReturn(List.of(PaymentAllocationType.DUE_PRINCIPAL));
|
||||||
Mockito.when(loanTransaction.isOn(eq(transactionDate))).thenReturn(true);
|
Mockito.when(loanTransaction.isOn(eq(transactionDate))).thenReturn(true);
|
||||||
|
|
||||||
underTest.processLatestTransaction(loanTransaction, currency, List.of(installment), Set.of(charge), overpaidAmount);
|
underTest.processLatestTransaction(loanTransaction, currency, List.of(installment), Set.of(charge),
|
||||||
|
new MoneyHolder(overpaidAmount));
|
||||||
|
|
||||||
Mockito.verify(installment, Mockito.times(1)).payFeeChargesComponent(eq(transactionDate), eq(chargeAmountMoney));
|
Mockito.verify(installment, Mockito.times(1)).payFeeChargesComponent(eq(transactionDate), eq(chargeAmountMoney));
|
||||||
Mockito.verify(loanTransaction, Mockito.times(1)).updateComponents(refEq(zero), refEq(zero), refEq(chargeAmountMoney), refEq(zero));
|
Mockito.verify(loanTransaction, Mockito.times(1)).updateComponents(refEq(zero), refEq(zero), refEq(chargeAmountMoney), refEq(zero));
|
||||||
|
|||||||
-4
@@ -54,7 +54,6 @@ import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHe
|
|||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Named;
|
import org.junit.jupiter.api.Named;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
@@ -65,7 +64,6 @@ import org.junit.jupiter.params.provider.MethodSource;
|
|||||||
public class LoanChargebackOnPaymentTypeRepaymentTransactionsTest {
|
public class LoanChargebackOnPaymentTypeRepaymentTransactionsTest {
|
||||||
|
|
||||||
private ResponseSpecification responseSpec;
|
private ResponseSpecification responseSpec;
|
||||||
private ResponseSpecification responseSpecErr400;
|
|
||||||
private ResponseSpecification responseSpecErr503;
|
private ResponseSpecification responseSpecErr503;
|
||||||
private RequestSpecification requestSpec;
|
private RequestSpecification requestSpec;
|
||||||
private ClientHelper clientHelper;
|
private ClientHelper clientHelper;
|
||||||
@@ -77,13 +75,11 @@ public class LoanChargebackOnPaymentTypeRepaymentTransactionsTest {
|
|||||||
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||||
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||||
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||||
this.responseSpecErr400 = new ResponseSpecBuilder().expectStatusCode(400).build();
|
|
||||||
this.responseSpecErr503 = new ResponseSpecBuilder().expectStatusCode(503).build();
|
this.responseSpecErr503 = new ResponseSpecBuilder().expectStatusCode(503).build();
|
||||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
this.clientHelper = new ClientHelper(this.requestSpec, this.responseSpec);
|
this.clientHelper = new ClientHelper(this.requestSpec, this.responseSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Disabled("Issue with Chargeback and Adv Pment Alloc")
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("loanProductFactory")
|
@MethodSource("loanProductFactory")
|
||||||
public void loanTransactionChargebackForPaymentTypeRepaymentTransactionTest(LoanProductTestBuilder loanProductTestBuilder) {
|
public void loanTransactionChargebackForPaymentTypeRepaymentTransactionTest(LoanProductTestBuilder loanProductTestBuilder) {
|
||||||
|
|||||||
Reference in New Issue
Block a user