FINERACT-1981: Disbursement on overpaid loan

This commit is contained in:
Adam Saghy
2024-02-06 13:20:01 +01:00
parent 8a67b5af65
commit 99352c66b2
9 changed files with 186 additions and 72 deletions
@@ -784,7 +784,7 @@ public class AccountingProcessorHelper {
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 LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) {
final GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId, paymentTypeId);
@@ -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.LoanDTO;
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.springframework.stereotype.Component;
@@ -467,30 +468,34 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
// transaction properties
final String transactionId = loanTransactionDTO.getTransactionId();
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 Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
// create journal entries for the disbursement (or disbursement
// reversal)
if (loanTransactionDTO.isLoanToLoanTransfer()) {
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
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);
}
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalPortion, 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/>
* <br/>
* <br/>
@@ -520,7 +525,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
* @param loanTransactionDTO
* @param loanDTO
* @param office
*
*/
private void createJournalEntriesForRepaymentsAndWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
final Office office, final boolean writeOff, final boolean isIncomeFromFee) {
@@ -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.LoanDTO;
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.springframework.stereotype.Component;
@@ -468,23 +469,29 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
// transaction properties
final String transactionId = loanTransactionDTO.getTransactionId();
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 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);
}
}
/**
@@ -427,7 +427,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
disburseLoanToLoan(loan, command, loanOutstanding);
}
LoanTransaction disbursementTransaction = null;
if (isAccountTransfer) {
disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
existingTransactionIds.addAll(loan.findExistingTransactionIds());
@@ -435,11 +435,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} else {
existingTransactionIds.addAll(loan.findExistingTransactionIds());
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), amountToDisburse, paymentDetail,
disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), amountToDisburse, paymentDetail,
actualDisbursementDate, txnExternalId);
disbursementTransaction.updateLoan(loan);
loan.addLoanTransaction(disbursementTransaction);
LoanTransaction savedLoanTransaction = loanTransactionRepository.saveAndFlush(disbursementTransaction);
}
if (loan.getRepaymentScheduleInstallments().isEmpty()) {
/*
@@ -454,12 +453,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
}
if (isPaymentTypeApplicableForDisbursementCharge) {
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail,
downPaymentEnabled);
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail);
} else {
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null, downPaymentEnabled);
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null);
}
loan.adjustNetDisbursalAmount(amountToDisburse.getAmount());
if (disbursementTransaction != null) {
loanTransactionRepository.saveAndFlush(disbursementTransaction);
}
if (loan.isAutoRepaymentForDownPaymentEnabled()) {
// updating linked savings account for auto down payment transaction for disbursement to savings account
if (isAccountTransfer && loan.shouldCreateStandingInstructionAtDisbursement()) {
@@ -764,10 +765,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
}
if (configurationDomainService.isPaymentTypeApplicableForDisbursementCharge()) {
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail,
downPaymentEnabled);
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail);
} else {
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null, downPaymentEnabled);
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null);
}
}
if (!changes.isEmpty()) {