Skip to content

Commit

Permalink
FINERACT-2081: Payments after loan charge - payment allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
Marta Jankovics committed Sep 18, 2024
1 parent 534d17e commit 2aa4cf1
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -705,14 +705,9 @@ public void addLoanCharge(final LoanCharge loanCharge) {
this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());

// store Id's of existing loan transactions and existing reversed loan transactions
if (!loanCharge.isDueAtDisbursement() && !loanCharge.isPaid() && getStatus().isOverpaid()) {
reprocessTransactions(); // overpaid transactions will be reprocessed and pay this charge
doPostLoanTransactionChecks(loanCharge.getEffectiveDueDate(), loanLifecycleStateMachine);
} else {
final SingleLoanChargeRepaymentScheduleProcessingWrapper wrapper = new SingleLoanChargeRepaymentScheduleProcessingWrapper();
wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), loanCharge);
updateLoanSummaryDerivedFields();
}
final SingleLoanChargeRepaymentScheduleProcessingWrapper wrapper = new SingleLoanChargeRepaymentScheduleProcessingWrapper();
wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), loanCharge);
updateLoanSummaryDerivedFields();

loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_ADDED, this);
}
Expand All @@ -731,6 +726,13 @@ public ChangedTransactionDetail reprocessTransactions() {
return changedTransactionDetail;
}

public ChangedTransactionDetail reprocessTransactionsWithPostTransactionChecks(LocalDate transactionDate) {
ChangedTransactionDetail changedDetail = reprocessTransactions();
doPostLoanTransactionChecks(transactionDate, loanLifecycleStateMachine);
return changedDetail;
}


/**
* Creates a loanTransaction for "Apply Charge Event" with transaction date set to "suppliedTransactionDate". The
* newly created transaction is also added to the Loan on which this method is called.
Expand Down Expand Up @@ -5484,12 +5486,12 @@ public List<LoanTransaction> getLoanTransactions(Predicate<LoanTransaction> pred
return getLoanTransactions().stream().filter(predicate).toList();
}

public LoanTransaction getLoanTransaction(Predicate<LoanTransaction> predicate) {
return getLoanTransactions().stream().filter(predicate).findFirst().orElse(null);
}

public LoanTransaction findChargedOffTransaction() {
return getLoanTransactions().stream() //
.filter(LoanTransaction::isNotReversed) //
.filter(LoanTransaction::isChargeOff) //
.findFirst() //
.orElse(null);
return getLoanTransaction(e -> e.isNotReversed() && e.isChargeOff());
}

public void handleMaturityDateActivate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -922,12 +922,12 @@ public void manuallyAdjustedOrReversed() {
public void updateLoanTransactionToRepaymentScheduleMappings(final Collection<LoanTransactionToRepaymentScheduleMapping> mappings) {
Collection<LoanTransactionToRepaymentScheduleMapping> retainMappings = new ArrayList<>();
for (LoanTransactionToRepaymentScheduleMapping updatedrepaymentScheduleMapping : mappings) {
updateMapingDetail(retainMappings, updatedrepaymentScheduleMapping);
updateMappingDetail(retainMappings, updatedrepaymentScheduleMapping);
}
this.loanTransactionToRepaymentScheduleMappings.retainAll(retainMappings);
}

private boolean updateMapingDetail(final Collection<LoanTransactionToRepaymentScheduleMapping> retainMappings,
private boolean updateMappingDetail(final Collection<LoanTransactionToRepaymentScheduleMapping> retainMappings,
final LoanTransactionToRepaymentScheduleMapping updatedrepaymentScheduleMapping) {
boolean isMappingUpdated = false;
for (LoanTransactionToRepaymentScheduleMapping repaymentScheduleMapping : this.loanTransactionToRepaymentScheduleMappings) {
Expand All @@ -952,6 +952,33 @@ private boolean updateMapingDetail(final Collection<LoanTransactionToRepaymentSc
return isMappingUpdated;
}

public void addLoanTransactionToRepaymentScheduleMappings(final Collection<LoanTransactionToRepaymentScheduleMapping> updatedMappings) {
for (LoanTransactionToRepaymentScheduleMapping updatedMapping : updatedMappings) {
addMappingDetail(updatedMapping);
}
}

private boolean addMappingDetail(final LoanTransactionToRepaymentScheduleMapping updatedMapping) {
boolean isMappingUpdated = false;
for (LoanTransactionToRepaymentScheduleMapping existingMapping : this.loanTransactionToRepaymentScheduleMappings) {
LoanRepaymentScheduleInstallment existingInstallment = existingMapping.getLoanRepaymentScheduleInstallment();
LoanRepaymentScheduleInstallment updatedInstallment = updatedMapping.getLoanRepaymentScheduleInstallment();
if (existingInstallment.getDueDate().equals(updatedInstallment.getDueDate())
&& updatedInstallment.getId() != null && updatedInstallment.getId().equals(existingInstallment.getId())) {
existingMapping.updateComponents(updatedMapping.getPrincipalPortion(),
updatedMapping.getInterestPortion(), updatedMapping.getFeeChargesPortion(),
updatedMapping.getPenaltyChargesPortion());
isMappingUpdated = true;
break;
}
}
if (!isMappingUpdated) {
updatedMapping.setLoanTransaction(this);
this.loanTransactionToRepaymentScheduleMappings.add(updatedMapping);
}
return isMappingUpdated;
}

public Set<LoanTransactionToRepaymentScheduleMapping> getLoanTransactionToRepaymentScheduleMappings() {
return this.loanTransactionToRepaymentScheduleMappings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;

import jakarta.validation.constraints.NotNull;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;

Expand Down Expand Up @@ -100,10 +103,13 @@ public LoanRepaymentScheduleInstallment getLoanRepaymentScheduleInstallment() {
return this.installment;
}

public void updateComponents(final Money principal, final Money interest, final Money feeCharges, final Money penaltyCharges) {
final MonetaryCurrency currency = principal.getCurrency();
this.principalPortion = defaultToNullIfZero(getPrincipalPortion(currency).plus(principal));
this.interestPortion = defaultToNullIfZero(getInterestPortion(currency).plus(interest));
public void updateComponents(@NotNull Money principal, @NotNull Money interest, @NotNull Money feeCharges, @NotNull Money penaltyCharges) {
updateComponents(principal.getAmount(), interest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount());
}

void updateComponents(final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges, final BigDecimal penaltyCharges) {
this.principalPortion = MathUtil.zeroToNull(MathUtil.add(getPrincipalPortion(), principal));
this.interestPortion = MathUtil.zeroToNull(MathUtil.add(getInterestPortion(), interest));
updateChargesComponents(feeCharges, penaltyCharges);
updateAmount();
}
Expand All @@ -122,10 +128,9 @@ public void setComponents(final BigDecimal principal, final BigDecimal interest,
updateAmount();
}

private void updateChargesComponents(final Money feeCharges, final Money penaltyCharges) {
final MonetaryCurrency currency = feeCharges.getCurrency();
this.feeChargesPortion = defaultToNullIfZero(getFeeChargesPortion(currency).plus(feeCharges));
this.penaltyChargesPortion = defaultToNullIfZero(getPenaltyChargesPortion(currency).plus(penaltyCharges));
private void updateChargesComponents(final BigDecimal feeCharges, final BigDecimal penaltyCharges) {
this.feeChargesPortion = MathUtil.zeroToNull(MathUtil.add(getFeeChargesPortion(), feeCharges));
this.penaltyChargesPortion = MathUtil.zeroToNull(MathUtil.add(getPenaltyChargesPortion(), penaltyCharges));
}

public Money getPrincipalPortion(final MonetaryCurrency currency) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ public ChangedTransactionDetail reprocessLoanTransactions(final LocalDate disbur
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(
newLoanTransaction.getLoanTransactionToRepaymentScheduleMappings());
} else {
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
createNewTransaction(loanTransaction, newLoanTransaction);
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
}
}

Expand Down Expand Up @@ -248,7 +249,8 @@ private void recalculateAccrualActivityTransaction(ChangedTransactionDetail chan
calculateAccrualActivity(newLoanTransaction, currency, installments);

if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
createNewTransaction(loanTransaction, newLoanTransaction);
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
}
}

Expand Down Expand Up @@ -403,7 +405,8 @@ private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTra

newLoanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
createNewTransaction(loanTransaction, newLoanTransaction);
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
}
}

Expand Down Expand Up @@ -493,7 +496,8 @@ private void recalculateCreditTransaction(ChangedTransactionDetail changedTransa

processCreditTransaction(newLoanTransaction, overpaymentHolder, currency, installments);
if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
createNewTransaction(loanTransaction, newLoanTransaction);
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
}
}

Expand All @@ -504,15 +508,13 @@ private List<LoanTransaction> getMergedTransactionList(List<LoanTransaction> tra
return mergedList;
}

protected void createNewTransaction(LoanTransaction loanTransaction, LoanTransaction newLoanTransaction,
ChangedTransactionDetail changedTransactionDetail) {
protected void createNewTransaction(LoanTransaction loanTransaction, LoanTransaction newLoanTransaction) {
loanTransaction.reverse();
loanTransaction.updateExternalId(null);
newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
// Adding Replayed relation from newly created transaction to reversed transaction
newLoanTransaction.getLoanTransactionRelations().add(
LoanTransactionRelation.linkToTransaction(newLoanTransaction, loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED));
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
}

protected void processCreditTransaction(LoanTransaction loanTransaction, MoneyHolder overpaymentHolder, MonetaryCurrency currency,
Expand Down
Loading

0 comments on commit 2aa4cf1

Please sign in to comment.