FINERACT-2060: Accrual reverse replay logic and Handling
This commit is contained in:
committed by
Adam Saghy
parent
8d25b0c765
commit
d57e72ec2f
+27
-29
@@ -36,6 +36,7 @@ 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.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@@ -51,69 +52,66 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
for (final LoanTransactionDTO loanTransactionDTO : loanDTO.getNewLoanTransactions()) {
|
||||
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
||||
this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
|
||||
final LoanTransactionEnumData transactionType = loanTransactionDTO.getTransactionType();
|
||||
|
||||
/** Handle Disbursements **/
|
||||
if (loanTransactionDTO.getTransactionType().isDisbursement()) {
|
||||
// Handle Disbursements
|
||||
if (transactionType.isDisbursement()) {
|
||||
createJournalEntriesForDisbursements(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
|
||||
/*** Handle Accruals ***/
|
||||
if (loanTransactionDTO.getTransactionType().isAccrual()) {
|
||||
// Handle Accruals
|
||||
if (transactionType.isAccrual() || transactionType.isAccrualAdjustment()) {
|
||||
createJournalEntriesForAccruals(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
|
||||
/***
|
||||
/*
|
||||
* Handle repayments, loan refunds, repayments at disbursement and reversal of Repayments and Repayments at
|
||||
* disbursement (except charge adjustment)
|
||||
***/
|
||||
else if ((loanTransactionDTO.getTransactionType().isRepaymentType()
|
||||
&& !loanTransactionDTO.getTransactionType().isChargeAdjustment())
|
||||
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()
|
||||
|| loanTransactionDTO.getTransactionType().isChargePayment()) {
|
||||
*/
|
||||
else if ((transactionType.isRepaymentType() && !transactionType.isChargeAdjustment())
|
||||
|| transactionType.isRepaymentAtDisbursement() || transactionType.isChargePayment()) {
|
||||
createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, false,
|
||||
loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement());
|
||||
transactionType.isRepaymentAtDisbursement());
|
||||
}
|
||||
|
||||
/** Logic for handling recovery payments **/
|
||||
else if (loanTransactionDTO.getTransactionType().isRecoveryRepayment()) {
|
||||
// Logic for handling recovery payments
|
||||
else if (transactionType.isRecoveryRepayment()) {
|
||||
createJournalEntriesForRecoveryRepayments(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
|
||||
/** Logic for Refunds of Overpayments **/
|
||||
else if (loanTransactionDTO.getTransactionType().isRefund()) {
|
||||
// Logic for Refunds of Overpayments
|
||||
else if (transactionType.isRefund()) {
|
||||
createJournalEntriesForRefund(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
|
||||
/** Logic for Credit Balance Refunds **/
|
||||
else if (loanTransactionDTO.getTransactionType().isCreditBalanceRefund()) {
|
||||
// Logic for Credit Balance Refunds
|
||||
else if (transactionType.isCreditBalanceRefund()) {
|
||||
createJournalEntriesForCreditBalanceRefund(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
|
||||
/** Handle Write Offs, waivers and their reversals **/
|
||||
else if ((loanTransactionDTO.getTransactionType().isWriteOff() || loanTransactionDTO.getTransactionType().isWaiveInterest()
|
||||
|| loanTransactionDTO.getTransactionType().isWaiveCharges())) {
|
||||
// Handle Write Offs, waivers and their reversals
|
||||
else if ((transactionType.isWriteOff() || transactionType.isWaiveInterest() || transactionType.isWaiveCharges())) {
|
||||
createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, true, false);
|
||||
}
|
||||
|
||||
/** Logic for Refunds of Active Loans **/
|
||||
else if (loanTransactionDTO.getTransactionType().isRefundForActiveLoans()) {
|
||||
// Logic for Refunds of Active Loans
|
||||
else if (transactionType.isRefundForActiveLoans()) {
|
||||
createJournalEntriesForRefundForActiveLoan(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
// Logic for Chargebacks
|
||||
else if (loanTransactionDTO.getTransactionType().isChargeback()) {
|
||||
else if (transactionType.isChargeback()) {
|
||||
createJournalEntriesForChargeback(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
// Logic for Charge Adjustment
|
||||
else if (loanTransactionDTO.getTransactionType().isChargeAdjustment()) {
|
||||
else if (transactionType.isChargeAdjustment()) {
|
||||
createJournalEntriesForChargeAdjustment(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
// Logic for Charge-Off
|
||||
else if (loanTransactionDTO.getTransactionType().isChargeoff()) {
|
||||
else if (transactionType.isChargeoff()) {
|
||||
createJournalEntriesForChargeOff(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
// Logic for Interest Payment Waiver
|
||||
else if (loanTransactionDTO.getTransactionType().isInterestPaymentWaiver()
|
||||
|| loanTransactionDTO.getTransactionType().isInterestRefund()) {
|
||||
else if (transactionType.isInterestPaymentWaiver() || transactionType.isInterestRefund()) {
|
||||
createJournalEntriesForInterestPaymentWaiverOrInterestRefund(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
}
|
||||
@@ -1144,7 +1142,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
* @param office
|
||||
*/
|
||||
private void createJournalEntriesForAccruals(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) {
|
||||
|
||||
// loan properties
|
||||
final Long loanProductId = loanDTO.getLoanProductId();
|
||||
final Long loanId = loanDTO.getLoanId();
|
||||
@@ -1153,10 +1150,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
// transaction properties
|
||||
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
||||
final LoanTransactionEnumData transactionType = loanTransactionDTO.getTransactionType();
|
||||
final BigDecimal interestAmount = loanTransactionDTO.getInterest();
|
||||
final BigDecimal feesAmount = loanTransactionDTO.getFees();
|
||||
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
|
||||
final boolean isReversed = loanTransactionDTO.isReversed();
|
||||
final boolean isReversed = transactionType.isAccrualAdjustment() != loanTransactionDTO.isReversed();
|
||||
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||
|
||||
// create journal entries for recognizing interest (or reversal)
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ChargeData> retrieveLoanAccountApplicableCharges(final Long loanId, ChargeTimeType[] excludeChargeTimes) {
|
||||
public List<ChargeData> retrieveLoanAccountApplicableCharges(final Long loanId, ChargeTimeType[] excludeChargeTimes) {
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
StringBuilder excludeClause = new StringBuilder("");
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
|
||||
+3
-2
@@ -42,6 +42,7 @@ import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
@@ -442,7 +443,7 @@ public class LoanChargesApiResource {
|
||||
final LoanChargeData loanCharge = this.loanChargeReadPlatformService.retrieveLoanChargeDetails(resolvedLoanChargeId,
|
||||
resolvedLoanId);
|
||||
|
||||
final Collection<LoanInstallmentChargeData> installmentChargeData = this.loanChargeReadPlatformService
|
||||
final List<LoanInstallmentChargeData> installmentChargeData = this.loanChargeReadPlatformService
|
||||
.retrieveInstallmentLoanCharges(resolvedLoanChargeId, true);
|
||||
|
||||
final LoanChargeData loanChargeData = new LoanChargeData(loanCharge, installmentChargeData);
|
||||
@@ -536,7 +537,7 @@ public class LoanChargesApiResource {
|
||||
ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr);
|
||||
Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
|
||||
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(resolvedLoanId,
|
||||
final List<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(resolvedLoanId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
|
||||
final LoanChargeData loanChargeTemplate = LoanChargeData.template(chargeOptions);
|
||||
|
||||
|
||||
+2
-10
@@ -759,7 +759,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = null;
|
||||
final LoanRepaymentScheduleInstallment foreCloseDetail = loan.fetchLoanForeclosureDetail(foreClosureDate);
|
||||
|
||||
loanAccrualsProcessingService.processAccrualsForLoanForeClosure(loan, foreClosureDate, newTransactions);
|
||||
loanAccrualsProcessingService.processAccrualsOnLoanForeClosure(loan, foreClosureDate, newTransactions);
|
||||
|
||||
Money interestPayable = foreCloseDetail.getInterestCharged(currency);
|
||||
Money feePayable = foreCloseDetail.getFeeChargesCharged(currency);
|
||||
@@ -922,15 +922,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
if (interestRefundTransaction != null) {
|
||||
loan.addLoanTransaction(interestRefundTransaction);
|
||||
}
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = loan.retrieveListOfTransactionsForReprocessing();
|
||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(
|
||||
loan.getDisbursementDate(), allNonContraTransactionsPostDisbursement, loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
mapEntry.getValue().updateLoan(loan);
|
||||
}
|
||||
|
||||
loan.getLoanTransactions().addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
|
||||
// Store and flush newly created transaction to generate PK
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(refundTransaction);
|
||||
|
||||
+11
-29
@@ -18,15 +18,12 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.jobs.addaccrualentries;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.time.LocalDate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.core.exception.MultiException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
@@ -45,30 +42,15 @@ public class AddAccrualEntriesTasklet implements Tasklet {
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList = loanReadPlatformService.retriveScheduleAccrualData();
|
||||
Map<Long, Collection<LoanScheduleAccrualData>> loanDataMap = new HashMap<>();
|
||||
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDataList) {
|
||||
if (loanDataMap.containsKey(accrualData.getLoanId())) {
|
||||
loanDataMap.get(accrualData.getLoanId()).add(accrualData);
|
||||
} else {
|
||||
Collection<LoanScheduleAccrualData> accrualDataList = new ArrayList<>();
|
||||
accrualDataList.add(accrualData);
|
||||
loanDataMap.put(accrualData.getLoanId(), accrualDataList);
|
||||
}
|
||||
}
|
||||
|
||||
List<Throwable> errors = new ArrayList<>();
|
||||
for (Map.Entry<Long, Collection<LoanScheduleAccrualData>> mapEntry : loanDataMap.entrySet()) {
|
||||
try {
|
||||
loanAccrualsProcessingService.addAccrualAccounting(mapEntry.getKey(), mapEntry.getValue());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to add accrual transaction for loan {}", mapEntry.getKey(), e);
|
||||
errors.add(e);
|
||||
}
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
throw new JobExecutionException(errors);
|
||||
try {
|
||||
addAccruals(DateUtils.getBusinessLocalDate());
|
||||
} catch (MultiException e) {
|
||||
throw new JobExecutionException(e);
|
||||
}
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
private void addAccruals(final LocalDate tilldate) throws MultiException {
|
||||
loanAccrualsProcessingService.addAccruals(tilldate);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -440,7 +440,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
}
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loan.recalculateAllCharges();
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.processTransactions();
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
|
||||
this.loanRepaymentScheduleHistoryRepository.saveAll(loanRepaymentScheduleHistoryList);
|
||||
|
||||
|
||||
+4
-2
@@ -635,7 +635,8 @@ public final class LoanApplicationValidator {
|
||||
"error.msg.loan.loanIdToClose.no.active.loan.associated.to.client.found",
|
||||
"loanIdToClose is invalid, No Active Loan associated with the given Client ID found.");
|
||||
}
|
||||
if (loanToClose.isMultiDisburmentLoan() && !loanToClose.isInterestRecalculationEnabledForProduct()) {
|
||||
if (loanToClose.isMultiDisburmentLoan()
|
||||
&& !loanToClose.getLoanProductRelatedDetail().isInterestRecalculationEnabled()) {
|
||||
throw new GeneralPlatformDomainRuleException(
|
||||
"error.msg.loan.topup.on.multi.tranche.loan.without.interest.recalculation.not.supported",
|
||||
"Topup on loan with multi-tranche disbursal and without interest recalculation is not supported.");
|
||||
@@ -1346,7 +1347,8 @@ public final class LoanApplicationValidator {
|
||||
"error.msg.loan.loanIdToClose.no.active.loan.associated.to.client.found",
|
||||
"loanIdToClose is invalid, No Active Loan associated with the given Client ID found.");
|
||||
}
|
||||
if (loanToClose.isMultiDisburmentLoan() && !loanToClose.isInterestRecalculationEnabledForProduct()) {
|
||||
if (loanToClose.isMultiDisburmentLoan()
|
||||
&& !loanToClose.getLoanProductRelatedDetail().isInterestRecalculationEnabled()) {
|
||||
throw new GeneralPlatformDomainRuleException(
|
||||
"error.msg.loan.topup.on.multi.tranche.loan.without.interest.recalculation.not.supported",
|
||||
"Topup on loan with multi-tranche disbursal and without interest recalculation is not supported.");
|
||||
|
||||
+814
-1073
File diff suppressed because it is too large
Load Diff
+11
-1
@@ -104,7 +104,7 @@ import org.apache.fineract.portfolio.rate.service.RateAssembler;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoanAssembler {
|
||||
public class LoanAssemblerImpl implements LoanAssembler {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
@@ -136,6 +136,7 @@ public class LoanAssembler {
|
||||
private final LoanCollateralManagementMapper loanCollateralManagementMapper;
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
|
||||
@Override
|
||||
public Loan assembleFrom(final Long accountId) {
|
||||
final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true);
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
|
||||
@@ -144,11 +145,13 @@ public class LoanAssembler {
|
||||
return loanAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelpers(final Loan loanAccount) {
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loan assembleFrom(final JsonCommand command) {
|
||||
final JsonElement element = command.parsedJson();
|
||||
|
||||
@@ -281,6 +284,7 @@ public class LoanAssembler {
|
||||
|
||||
// TODO: Review... it might be better somewhere else and rethink due to the account number generation logic is
|
||||
// intertwined with GLIM logic
|
||||
@Override
|
||||
public void accountNumberGeneration(JsonCommand command, Loan loan) {
|
||||
if (loan.isAccountNumberRequiresAutoGeneration()) {
|
||||
JsonElement element = command.parsedJson();
|
||||
@@ -371,6 +375,7 @@ public class LoanAssembler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValue findCodeValueByIdIfProvided(final Long codeValueId) {
|
||||
CodeValue codeValue = null;
|
||||
if (codeValueId != null) {
|
||||
@@ -379,6 +384,7 @@ public class LoanAssembler {
|
||||
return codeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fund findFundByIdIfProvided(final Long fundId) {
|
||||
Fund fund = null;
|
||||
if (fundId != null) {
|
||||
@@ -387,6 +393,7 @@ public class LoanAssembler {
|
||||
return fund;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Staff findLoanOfficerByIdIfProvided(final Long loanOfficerId) {
|
||||
Staff staff = null;
|
||||
if (loanOfficerId != null) {
|
||||
@@ -419,6 +426,7 @@ public class LoanAssembler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> updateFrom(JsonCommand command, Loan loan) {
|
||||
final Map<String, Object> changes = new HashMap<>();
|
||||
LoanProduct loanProduct;
|
||||
@@ -844,6 +852,7 @@ public class LoanAssembler {
|
||||
return changes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> updateLoanApplicationAttributesForWithdrawal(Loan loan, JsonCommand command, AppUser currentUser) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
||||
|
||||
@@ -868,6 +877,7 @@ public class LoanAssembler {
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> updateLoanApplicationAttributesForRejection(Loan loan, JsonCommand command, AppUser currentUser) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
||||
|
||||
+1
-241
@@ -24,9 +24,7 @@ import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
@@ -214,7 +212,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanInstallmentChargeData> retrieveInstallmentLoanCharges(Long loanChargeId, boolean onlyPaymentPendingCharges) {
|
||||
public List<LoanInstallmentChargeData> retrieveInstallmentLoanCharges(Long loanChargeId, boolean onlyPaymentPendingCharges) {
|
||||
final LoanInstallmentChargeMapper rm = new LoanInstallmentChargeMapper();
|
||||
String sql = "select " + rm.schema() + "where lic.loan_charge_id= ? ";
|
||||
if (onlyPaymentPendingCharges) {
|
||||
@@ -263,244 +261,6 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
return frequencyNumbers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanChargeData> retrieveLoanChargesForAccrual(final Long loanId) {
|
||||
|
||||
final LoanChargeAccrualMapper rm = new LoanChargeAccrualMapper();
|
||||
|
||||
final String sql = "select " + rm.schema() + " where lc.loan_id=? AND lc.is_active = true group by lc.id "
|
||||
+ " order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
|
||||
|
||||
Collection<LoanChargeData> charges = this.jdbcTemplate.query(sql, rm, // NOSONAR
|
||||
LoanTransactionType.ACCRUAL.getValue(), loanId, loanId);
|
||||
charges = updateLoanChargesWithUnrecognizedIncome(loanId, charges);
|
||||
|
||||
Collection<LoanChargeData> removeCharges = new ArrayList<>();
|
||||
for (LoanChargeData loanChargeData : charges) {
|
||||
if (loanChargeData.isInstallmentFee()) {
|
||||
removeCharges.add(loanChargeData);
|
||||
}
|
||||
}
|
||||
charges.removeAll(removeCharges);
|
||||
for (LoanChargeData loanChargeData : removeCharges) {
|
||||
if (loanChargeData.isInstallmentFee()) {
|
||||
Collection<LoanInstallmentChargeData> installmentChargeDatas = retrieveInstallmentLoanChargesForAccrual(
|
||||
loanChargeData.getId());
|
||||
LoanChargeData modifiedChargeData = new LoanChargeData(loanChargeData, installmentChargeDatas);
|
||||
charges.add(modifiedChargeData);
|
||||
}
|
||||
}
|
||||
|
||||
return charges;
|
||||
}
|
||||
|
||||
private static final class LoanChargeAccrualMapper implements RowMapper<LoanChargeData> {
|
||||
|
||||
private final String schemaSql;
|
||||
|
||||
LoanChargeAccrualMapper() {
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
sb.append(" lc.id as id, lc.charge_id as chargeId, lc.external_id as externalId, ");
|
||||
sb.append(" lc.amount as amountDue, ");
|
||||
sb.append(" lc.amount_waived_derived as amountWaived, ");
|
||||
sb.append(" lc.charge_time_enum as chargeTime, ");
|
||||
sb.append(" sum(cp.amount) as amountAccrued, ");
|
||||
sb.append(" lc.is_penalty as penalty, ");
|
||||
sb.append(" lc.due_for_collection_as_of_date as dueAsOfDate, ");
|
||||
sb.append(" lc.submitted_on_date as submittedOnDate ");
|
||||
sb.append(" from m_loan_charge lc ");
|
||||
sb.append(" left join ( ");
|
||||
sb.append(" select lcp.loan_charge_id, lcp.amount ");
|
||||
sb.append(" from m_loan_charge_paid_by lcp ");
|
||||
sb.append(
|
||||
" inner join m_loan_transaction lt on lt.id = lcp.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum = ? and lt.loan_id = ? ");
|
||||
sb.append(" ) cp on cp.loan_charge_id= lc.id ");
|
||||
|
||||
schemaSql = sb.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
final Long chargeId = rs.getLong("chargeId");
|
||||
final BigDecimal amount = rs.getBigDecimal("amountDue");
|
||||
final BigDecimal amountAccrued = rs.getBigDecimal("amountAccrued");
|
||||
final BigDecimal amountWaived = rs.getBigDecimal("amountWaived");
|
||||
|
||||
final int chargeTime = rs.getInt("chargeTime");
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
|
||||
|
||||
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
|
||||
final boolean penalty = rs.getBoolean("penalty");
|
||||
|
||||
final String externalIdStr = rs.getString("externalId");
|
||||
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
|
||||
|
||||
return new LoanChargeData(id, chargeId, dueAsOfDate, submittedOnDate, chargeTimeType, amount, amountAccrued, amountWaived,
|
||||
penalty, externalId);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<LoanChargeData> updateLoanChargesWithUnrecognizedIncome(final Long loanId,
|
||||
Collection<LoanChargeData> loanChargeDatas) {
|
||||
|
||||
final LoanChargeUnRecognizedIncomeMapper rm = new LoanChargeUnRecognizedIncomeMapper(loanChargeDatas);
|
||||
|
||||
final String sql = "select " + rm.schema() + " where lc.loan_id=? AND lc.is_active = true group by lc.id "
|
||||
+ " order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, LoanTransactionType.WAIVE_CHARGES.getValue(), loanId, loanId); // NOSONAR
|
||||
}
|
||||
|
||||
private static final class LoanChargeUnRecognizedIncomeMapper implements RowMapper<LoanChargeData> {
|
||||
|
||||
private final String schemaSql;
|
||||
private final Map<Long, LoanChargeData> chargeDataMap;
|
||||
|
||||
LoanChargeUnRecognizedIncomeMapper(final Collection<LoanChargeData> datas) {
|
||||
this.chargeDataMap = new HashMap<>();
|
||||
for (LoanChargeData chargeData : datas) {
|
||||
this.chargeDataMap.put(chargeData.getId(), chargeData);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
sb.append("lc.id as id, ");
|
||||
sb.append(" sum(wt.unrecognized_income_portion) as amountUnrecognized ");
|
||||
sb.append(" from m_loan_charge lc ");
|
||||
sb.append("left join (");
|
||||
sb.append("select cpb.loan_charge_id, lt.unrecognized_income_portion");
|
||||
sb.append(" from m_loan_charge_paid_by cpb ");
|
||||
sb.append(
|
||||
"inner join m_loan_transaction lt on lt.id = cpb.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum = ? and lt.loan_id = ? ");
|
||||
sb.append(") wt on wt.loan_charge_id= lc.id ");
|
||||
|
||||
schemaSql = sb.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
final BigDecimal amountUnrecognized = rs.getBigDecimal("amountUnrecognized");
|
||||
|
||||
LoanChargeData chargeData = this.chargeDataMap.get(id);
|
||||
return new LoanChargeData(amountUnrecognized, chargeData);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<LoanInstallmentChargeData> retrieveInstallmentLoanChargesForAccrual(Long loanChargeId) {
|
||||
final LoanInstallmentChargeAccrualMapper rm = new LoanInstallmentChargeAccrualMapper();
|
||||
String sql = "select " + rm.schema()
|
||||
+ " where lic.loan_charge_id= ? group by lsi.installment, lsi.duedate, lic.amount_outstanding_derived, lic.amount, lic.is_paid_derived, lic.amount_waived_derived, lic.waived";
|
||||
Collection<LoanInstallmentChargeData> chargeDatas = this.jdbcTemplate.query(sql, rm, // NOSONAR
|
||||
LoanTransactionType.ACCRUAL.getValue(), loanChargeId);
|
||||
final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas = new HashMap<>();
|
||||
for (LoanInstallmentChargeData installmentChargeData : chargeDatas) {
|
||||
installmentChargeDatas.put(installmentChargeData.getInstallmentNumber(), installmentChargeData);
|
||||
}
|
||||
chargeDatas = updateInstallmentLoanChargesWithUnrecognizedIncome(loanChargeId, installmentChargeDatas);
|
||||
for (LoanInstallmentChargeData installmentChargeData : chargeDatas) {
|
||||
installmentChargeDatas.put(installmentChargeData.getInstallmentNumber(), installmentChargeData);
|
||||
}
|
||||
return installmentChargeDatas.values();
|
||||
|
||||
}
|
||||
|
||||
private static final class LoanInstallmentChargeAccrualMapper implements RowMapper<LoanInstallmentChargeData> {
|
||||
|
||||
private final String schemaSql;
|
||||
|
||||
LoanInstallmentChargeAccrualMapper() {
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
sb.append(" lsi.installment as installmentNumber, lsi.duedate as dueAsOfDate, ");
|
||||
sb.append("lic.amount_outstanding_derived as amountOutstanding,");
|
||||
sb.append("lic.amount as amount, ");
|
||||
sb.append("lic.is_paid_derived as paid, ");
|
||||
sb.append("lic.amount_waived_derived as amountWaived, ");
|
||||
sb.append(" sum(cp.amount) as amountAccrued, ");
|
||||
sb.append("lic.waived as waived ");
|
||||
sb.append("from m_loan_installment_charge lic ");
|
||||
sb.append("join m_loan_repayment_schedule lsi on lsi.id = lic.loan_schedule_id ");
|
||||
sb.append("left join (");
|
||||
sb.append("select lcp.loan_charge_id, lcp.amount as amount, lcp.installment_number ");
|
||||
sb.append(" from m_loan_charge_paid_by lcp ");
|
||||
sb.append(
|
||||
"inner join m_loan_transaction lt on lt.id = lcp.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum = ?");
|
||||
sb.append(") cp on cp.loan_charge_id= lic.loan_charge_id and cp.installment_number = lsi.installment ");
|
||||
schemaSql = sb.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanInstallmentChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
final Integer installmentNumber = rs.getInt("installmentNumber");
|
||||
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
|
||||
final BigDecimal amount = rs.getBigDecimal("amount");
|
||||
final BigDecimal amountWaived = rs.getBigDecimal("amountWaived");
|
||||
final boolean paid = rs.getBoolean("paid");
|
||||
final boolean waived = rs.getBoolean("waived");
|
||||
final BigDecimal amountAccrued = rs.getBigDecimal("amountAccrued");
|
||||
|
||||
return LoanInstallmentChargeData.builder().installmentNumber(installmentNumber).dueDate(dueAsOfDate).amount(amount)
|
||||
.amountOutstanding(amountOutstanding).amountWaived(amountWaived).paid(paid).waived(waived).amountAccrued(amountAccrued)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<LoanInstallmentChargeData> updateInstallmentLoanChargesWithUnrecognizedIncome(final Long loanChargeId,
|
||||
final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas) {
|
||||
final LoanInstallmentChargeUnRecognizedIncomeMapper rm = new LoanInstallmentChargeUnRecognizedIncomeMapper(installmentChargeDatas);
|
||||
String sql = "select " + rm.schema() + " where cpb.loan_charge_id = ? group by cpb.installment_number ";
|
||||
return this.jdbcTemplate.query(sql, rm, LoanTransactionType.WAIVE_CHARGES.getValue(), loanChargeId); // NOSONAR
|
||||
}
|
||||
|
||||
private static final class LoanInstallmentChargeUnRecognizedIncomeMapper implements RowMapper<LoanInstallmentChargeData> {
|
||||
|
||||
private final String schemaSql;
|
||||
private final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas;
|
||||
|
||||
LoanInstallmentChargeUnRecognizedIncomeMapper(final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas) {
|
||||
this.installmentChargeDatas = installmentChargeDatas;
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
sb.append(" cpb.installment_number as installmentNumber, ");
|
||||
sb.append(" sum(lt.unrecognized_income_portion) as amountUnrecognized ");
|
||||
sb.append(" from m_loan_charge_paid_by cpb ");
|
||||
sb.append(
|
||||
"inner join m_loan_transaction lt on lt.id = cpb.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum = ?");
|
||||
schemaSql = sb.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanInstallmentChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
final Integer installmentNumber = rs.getInt("installmentNumber");
|
||||
final BigDecimal amountUnrecognized = rs.getBigDecimal("amountUnrecognized");
|
||||
LoanInstallmentChargeData installmentChargeData = this.installmentChargeDatas.get(installmentNumber);
|
||||
return LoanInstallmentChargeData.builder().installmentNumber(installmentChargeData.getInstallmentNumber())
|
||||
.dueDate(installmentChargeData.getDueDate()).amount(installmentChargeData.getAmount())
|
||||
.amountOutstanding(installmentChargeData.getAmountOutstanding()).amountWaived(installmentChargeData.getAmountWaived())
|
||||
.paid(installmentChargeData.isPaid()).waived(installmentChargeData.isWaived())
|
||||
.amountAccrued(installmentChargeData.getAmountAccrued()).amountUnrecognized(amountUnrecognized).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanChargePaidByData> retrieveLoanChargesPaidBy(Long chargeId, final LoanTransactionType transactionType,
|
||||
final Integer installmentNumber) {
|
||||
|
||||
+1
-4
@@ -838,14 +838,11 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT);
|
||||
loanChargeAdjustmentTransaction.getLoanTransactionRelations().add(loanTransactionRelation);
|
||||
|
||||
defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, loan);
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
||||
if (loan.isInterestBearing() && loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled()) {
|
||||
loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(),
|
||||
loan.retrieveListOfTransactionsForReprocessing(), loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||
loan.getActiveCharges());
|
||||
loan.reprocessTransactions();
|
||||
} else {
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction,
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
|
||||
-12
@@ -31,14 +31,12 @@ import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
|
||||
@@ -98,16 +96,10 @@ public interface LoanReadPlatformService {
|
||||
|
||||
DisbursementData retrieveLoanDisbursementDetail(Long loanId, Long disbursementId);
|
||||
|
||||
Collection<LoanScheduleAccrualData> retriveScheduleAccrualData();
|
||||
|
||||
LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId);
|
||||
|
||||
LoanTransactionData retrieveLoanWriteoffTemplate(Long loanId);
|
||||
|
||||
Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(LocalDate tillDate);
|
||||
|
||||
Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(LocalDate tillDate, Loan loan);
|
||||
|
||||
LoanTransactionData retrieveLoanChargeOffTemplate(Long loanId);
|
||||
|
||||
Collection<Long> fetchLoansForInterestRecalculation();
|
||||
@@ -116,10 +108,6 @@ public interface LoanReadPlatformService {
|
||||
|
||||
LoanTransactionData retrieveLoanPrePaymentTemplate(LoanTransactionType repaymentTransactionType, Long loanId, LocalDate onDate);
|
||||
|
||||
Collection<LoanTransactionData> retrieveWaiverLoanTransactions(Long loanId);
|
||||
|
||||
Collection<LoanSchedulePeriodData> fetchWaiverInterestRepaymentData(Long loanId);
|
||||
|
||||
boolean isGuaranteeRequired(Long loanId);
|
||||
|
||||
LocalDate retrieveMinimumDateOfRepaymentTransaction(Long loanId);
|
||||
|
||||
-400
@@ -29,18 +29,15 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.accounting.common.AccountingRuleType;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
@@ -77,7 +74,6 @@ import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.client.data.ClientData;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
|
||||
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.common.service.CommonEnumerations;
|
||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
|
||||
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
|
||||
@@ -96,7 +92,6 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
||||
@@ -1742,258 +1737,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
return this.jdbcTemplate.queryForObject(sql, rm, loanId, disbursementId); // NOSONAR
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanScheduleAccrualData> retriveScheduleAccrualData() {
|
||||
final String chargeAccrualDateCriteria = configurationDomainService.getAccrualDateConfigForCharge();
|
||||
if (chargeAccrualDateCriteria.equalsIgnoreCase(ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE)) {
|
||||
return retrieveScheduleAccrualDataForChargeSubmittedDateProcessing();
|
||||
}
|
||||
return retrieveScheduleAccrualDataForDefaultProcessing();
|
||||
}
|
||||
|
||||
private Collection<LoanScheduleAccrualData> retrieveScheduleAccrualDataForDefaultProcessing() {
|
||||
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
||||
LoanScheduleAccrualMapper mapper = new LoanScheduleAccrualMapper();
|
||||
Map<String, Object> paramMap = new HashMap<>(3);
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("select ").append(mapper.schema()).append(
|
||||
" where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ")
|
||||
.append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))")
|
||||
.append(" or ( ls.penalty_charges_amount <> COALESCE(ls.accrual_penalty_charges_derived, 0))")
|
||||
.append(" or ( ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0)))")
|
||||
.append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and loan.is_npa=false and loan.is_charged_off = false and ls.duedate <= :currentDate) ");
|
||||
|
||||
if (organisationStartDate != null) {
|
||||
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
|
||||
}
|
||||
sqlBuilder.append(" order by loan.id,ls.duedate ");
|
||||
|
||||
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
||||
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
|
||||
paramMap.put("organisationStartDate", (organisationStartDate == null) ? DateUtils.getBusinessLocalDate() : organisationStartDate);
|
||||
paramMap.put("currentDate", DateUtils.getBusinessLocalDate());
|
||||
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
|
||||
|
||||
}
|
||||
|
||||
private Collection<LoanScheduleAccrualData> retrieveScheduleAccrualDataForChargeSubmittedDateProcessing() {
|
||||
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
||||
LoanScheduleAccrualMapper mapper = new LoanScheduleAccrualMapper();
|
||||
Map<String, Object> paramMap = new HashMap<>(3);
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("select ").append(mapper.schema()).append(
|
||||
" where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ")
|
||||
.append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))")
|
||||
.append(" or ( ls.penalty_charges_amount <> COALESCE(ls.accrual_penalty_charges_derived, 0))")
|
||||
.append(" or ( ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0)))")
|
||||
.append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and loan.is_npa=false and loan.is_charged_off = false) ");
|
||||
|
||||
if (organisationStartDate != null) {
|
||||
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
|
||||
}
|
||||
sqlBuilder.append(" order by loan.id,ls.duedate ");
|
||||
|
||||
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
||||
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
|
||||
paramMap.put("organisationStartDate", (organisationStartDate == null) ? DateUtils.getBusinessLocalDate() : organisationStartDate);
|
||||
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(final LocalDate tillDate) {
|
||||
return retrievePeriodicAccrualData(tillDate, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(final LocalDate tillDate, final Loan loan) {
|
||||
final String chargeAccrualDateCriteria = configurationDomainService.getAccrualDateConfigForCharge();
|
||||
if (chargeAccrualDateCriteria.equalsIgnoreCase(ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE)) {
|
||||
return retrievePeriodicAccrualDataForChargeSubmittedDateProcessing(tillDate, loan);
|
||||
}
|
||||
return retrievePeriodicAccrualDataForDefaultProcessing(tillDate, loan);
|
||||
}
|
||||
|
||||
private Collection<LoanScheduleAccrualData> retrievePeriodicAccrualDataForDefaultProcessing(final LocalDate tillDate, final Loan loan) {
|
||||
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
|
||||
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("select ").append(mapper.schema()).append(
|
||||
" where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ")
|
||||
.append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))")
|
||||
.append(" or (ls.penalty_charges_amount <> COALESCE(ls.accrual_penalty_charges_derived, 0))")
|
||||
.append(" or (ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0)))")
|
||||
.append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and (loan.closedon_date <= :tillDate or loan.closedon_date is null)")
|
||||
.append(" and loan.is_npa=false and loan.is_charged_off = false and (ls.duedate <= :tillDate or (ls.duedate > :tillDate and ls.fromdate < :tillDate)")
|
||||
.append(" or (ls.installment = 1 and ls.fromdate = :tillDate))) ");
|
||||
Map<String, Object> paramMap = new HashMap<>(5);
|
||||
if (organisationStartDate != null) {
|
||||
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
|
||||
paramMap.put("organisationStartDate", organisationStartDate);
|
||||
}
|
||||
if (loan != null) {
|
||||
sqlBuilder.append(" and loan.id= :loanId ");
|
||||
paramMap.put("loanId", loan.getId());
|
||||
}
|
||||
sqlBuilder.append(" order by loan.id,ls.duedate ");
|
||||
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
||||
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
|
||||
paramMap.put("tillDate", tillDate);
|
||||
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
|
||||
}
|
||||
|
||||
private Collection<LoanScheduleAccrualData> retrievePeriodicAccrualDataForChargeSubmittedDateProcessing(final LocalDate tillDate,
|
||||
final Loan loan) {
|
||||
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
|
||||
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("select ").append(mapper.schema()).append(
|
||||
" where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ")
|
||||
.append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))")
|
||||
.append(" or (ls.penalty_charges_amount <> COALESCE(ls.accrual_penalty_charges_derived, 0))")
|
||||
.append(" or (ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0)))")
|
||||
.append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and (loan.closedon_date <= :tillDate or loan.closedon_date is null)")
|
||||
.append(" and loan.is_npa=false and loan.is_charged_off = false)");
|
||||
Map<String, Object> paramMap = new HashMap<>(5);
|
||||
if (organisationStartDate != null) {
|
||||
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
|
||||
paramMap.put("organisationStartDate", organisationStartDate);
|
||||
}
|
||||
if (loan != null) {
|
||||
sqlBuilder.append(" and loan.id= :loanId ");
|
||||
paramMap.put("loanId", loan.getId());
|
||||
}
|
||||
sqlBuilder.append(" order by loan.id,ls.duedate ");
|
||||
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
||||
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
|
||||
paramMap.put("tillDate", tillDate);
|
||||
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
|
||||
}
|
||||
|
||||
private static final class LoanSchedulePeriodicAccrualMapper implements RowMapper<LoanScheduleAccrualData> {
|
||||
|
||||
public String schema() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("loan.id as loanId , (CASE WHEN loan.client_id is null THEN mg.office_id ELSE mc.office_id END) as officeId,")
|
||||
.append("loan.accrued_till as accruedTill, loan.repayment_period_frequency_enum as frequencyEnum, ")
|
||||
.append("loan.interest_calculated_from_date as interestCalculatedFrom, ").append("loan.repay_every as repayEvery,")
|
||||
.append("ls.installment as installmentNumber, ")
|
||||
.append("ls.duedate as duedate,ls.fromdate as fromdate ,ls.id as scheduleId,loan.product_id as productId,")
|
||||
.append("ls.interest_amount as interest, ls.interest_waived_derived as interestWaived,")
|
||||
.append("ls.penalty_charges_amount as penalty, ").append("ls.fee_charges_amount as charges, ")
|
||||
.append("ls.credited_penalty as credited_penalty, ").append("ls.credited_fee as credited_fee, ")
|
||||
.append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,")
|
||||
.append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,")
|
||||
.append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode")
|
||||
.append(" from m_loan_repayment_schedule ls ").append(" left join m_loan loan on loan.id=ls.loan_id ")
|
||||
.append(" left join m_product_loan mpl on mpl.id = loan.product_id")
|
||||
.append(" left join m_client mc on mc.id = loan.client_id ").append(" left join m_group mg on mg.id = loan.group_id")
|
||||
.append(" left join m_currency curr on curr.code = loan.currency_code")
|
||||
.append(" left join m_loan_recalculation_details as recaldet on loan.id = recaldet.loan_id ");
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanScheduleAccrualData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
|
||||
final Long loanId = rs.getLong("loanId");
|
||||
final Long officeId = rs.getLong("officeId");
|
||||
final LocalDate accruedTill = JdbcSupport.getLocalDate(rs, "accruedTill");
|
||||
final LocalDate interestCalculatedFrom = JdbcSupport.getLocalDate(rs, "interestCalculatedFrom");
|
||||
final Integer installmentNumber = JdbcSupport.getInteger(rs, "installmentNumber");
|
||||
|
||||
final Integer frequencyEnum = JdbcSupport.getInteger(rs, "frequencyEnum");
|
||||
final Integer repayEvery = JdbcSupport.getInteger(rs, "repayEvery");
|
||||
final PeriodFrequencyType frequency = PeriodFrequencyType.fromInt(frequencyEnum);
|
||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "duedate");
|
||||
final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromdate");
|
||||
final Long repaymentScheduleId = rs.getLong("scheduleId");
|
||||
final Long loanProductId = rs.getLong("productId");
|
||||
final BigDecimal interestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interest");
|
||||
final BigDecimal feeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "charges");
|
||||
final BigDecimal penaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalty");
|
||||
final BigDecimal interestIncomeWaived = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interestWaived");
|
||||
final BigDecimal accruedInterestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accinterest");
|
||||
final BigDecimal accruedFeeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accfeecharege");
|
||||
final BigDecimal accruedPenaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accpenalty");
|
||||
final BigDecimal creditedFee = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "credited_fee");
|
||||
final BigDecimal creditedPenalty = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "credited_penalty");
|
||||
|
||||
final String currencyCode = rs.getString("currencyCode");
|
||||
final String currencyName = rs.getString("currencyName");
|
||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||
final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
|
||||
currencyDisplaySymbol, currencyNameCode);
|
||||
|
||||
return new LoanScheduleAccrualData(loanId, officeId, installmentNumber, accruedTill, frequency, repayEvery, dueDate, fromDate,
|
||||
repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome, accruedInterestIncome, accruedFeeIncome,
|
||||
accruedPenaltyIncome, currencyData, interestCalculatedFrom, interestIncomeWaived, creditedFee, creditedPenalty);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class LoanScheduleAccrualMapper implements RowMapper<LoanScheduleAccrualData> {
|
||||
|
||||
public String schema() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("loan.id as loanId, (CASE WHEN loan.client_id is null THEN mg.office_id ELSE mc.office_id END) as officeId,")
|
||||
.append("ls.duedate as duedate,ls.fromdate as fromdate,ls.id as scheduleId,loan.product_id as productId,")
|
||||
.append("ls.installment as installmentNumber, ")
|
||||
.append("ls.interest_amount as interest, ls.interest_waived_derived as interestWaived,")
|
||||
.append("ls.penalty_charges_amount as penalty, ").append("ls.fee_charges_amount as charges, ")
|
||||
.append("ls.credited_penalty as credited_penalty, ").append("ls.credited_fee as credited_fee, ")
|
||||
.append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,")
|
||||
.append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,")
|
||||
.append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode")
|
||||
.append(" from m_loan_repayment_schedule ls ").append(" left join m_loan loan on loan.id=ls.loan_id ")
|
||||
.append(" left join m_product_loan mpl on mpl.id = loan.product_id")
|
||||
.append(" left join m_client mc on mc.id = loan.client_id ").append(" left join m_group mg on mg.id = loan.group_id")
|
||||
.append(" left join m_currency curr on curr.code = loan.currency_code")
|
||||
.append(" left join m_loan_recalculation_details as recaldet on loan.id = recaldet.loan_id ");
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanScheduleAccrualData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
|
||||
final Long loanId = rs.getLong("loanId");
|
||||
final Long officeId = rs.getLong("officeId");
|
||||
final Integer installmentNumber = JdbcSupport.getInteger(rs, "installmentNumber");
|
||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "duedate");
|
||||
final LocalDate fromdate = JdbcSupport.getLocalDate(rs, "fromdate");
|
||||
final Long repaymentScheduleId = rs.getLong("scheduleId");
|
||||
final Long loanProductId = rs.getLong("productId");
|
||||
final BigDecimal interestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interest");
|
||||
final BigDecimal feeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "charges");
|
||||
final BigDecimal penaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalty");
|
||||
final BigDecimal creditedFee = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "credited_fee");
|
||||
final BigDecimal creditedPenalty = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "credited_penalty");
|
||||
|
||||
final BigDecimal interestIncomeWaived = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interestWaived");
|
||||
final BigDecimal accruedInterestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accinterest");
|
||||
final BigDecimal accruedFeeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accfeecharege");
|
||||
final BigDecimal accruedPenaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accpenalty");
|
||||
|
||||
final String currencyCode = rs.getString("currencyCode");
|
||||
final String currencyName = rs.getString("currencyName");
|
||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||
final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
|
||||
currencyDisplaySymbol, currencyNameCode);
|
||||
final LocalDate accruedTill = null;
|
||||
final PeriodFrequencyType frequency = null;
|
||||
final Integer repayEvery = null;
|
||||
final LocalDate interestCalculatedFrom = null;
|
||||
return new LoanScheduleAccrualData(loanId, officeId, installmentNumber, accruedTill, frequency, repayEvery, dueDate, fromdate,
|
||||
repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome, accruedInterestIncome, accruedFeeIncome,
|
||||
accruedPenaltyIncome, currencyData, interestCalculatedFrom, interestIncomeWaived, creditedFee, creditedPenalty);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId) {
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||
@@ -2146,155 +1889,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanTransactionData> retrieveWaiverLoanTransactions(final Long loanId) {
|
||||
try {
|
||||
|
||||
final LoanTransactionDerivedComponentMapper rm = new LoanTransactionDerivedComponentMapper(sqlGenerator);
|
||||
|
||||
final String sql = "select " + rm.schema()
|
||||
+ " where tr.loan_id = ? and tr.transaction_type_enum = ? and tr.is_reversed=false order by tr.transaction_date, tr.created_on_utc, tr.id ";
|
||||
return this.jdbcTemplate.query(sql, rm, loanId, LoanTransactionType.WAIVE_INTEREST.getValue()); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuaranteeRequired(final Long loanId) {
|
||||
final String sql = "select pl.hold_guarantee_funds from m_loan ml inner join m_product_loan pl on pl.id = ml.product_id where ml.id=?";
|
||||
return TRUE.equals(this.jdbcTemplate.queryForObject(sql, Boolean.class, loanId));
|
||||
}
|
||||
|
||||
private static final class LoanTransactionDerivedComponentMapper implements RowMapper<LoanTransactionData> {
|
||||
|
||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||
|
||||
LoanTransactionDerivedComponentMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
this.sqlGenerator = sqlGenerator;
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
|
||||
return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as " + sqlGenerator.escape("date")
|
||||
+ ", tr.amount as total, tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, "
|
||||
+ " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, "
|
||||
+ " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, "
|
||||
+ " tr.unrecognized_income_portion as unrecognizedIncome, tr.loan_id as loanId, l.external_id as externalLoanId, "
|
||||
+ " tr.external_id as externalId from m_loan_transaction tr " + " left join m_loan l on tr.loan_id = l.id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
final Long loanId = rs.getLong("loanId");
|
||||
final String externalLoanIdStr = rs.getString("externalLoanId");
|
||||
final ExternalId externalLoanId = ExternalIdFactory.produce(externalLoanIdStr);
|
||||
final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType");
|
||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(transactionTypeInt);
|
||||
|
||||
final LocalDate date = JdbcSupport.getLocalDate(rs, "date");
|
||||
final BigDecimal totalAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "total");
|
||||
final BigDecimal principalPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principal");
|
||||
final BigDecimal interestPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interest");
|
||||
final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees");
|
||||
final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties");
|
||||
final BigDecimal overPaymentPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "overpayment");
|
||||
final BigDecimal unrecognizedIncomePortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "unrecognizedIncome");
|
||||
final BigDecimal outstandingLoanBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "outstandingLoanBalance");
|
||||
final String externalIdStr = rs.getString("externalId");
|
||||
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
|
||||
|
||||
return new LoanTransactionData(id, transactionType, date, totalAmount, null, principalPortion, interestPortion,
|
||||
feeChargesPortion, penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, outstandingLoanBalance, false,
|
||||
externalId, loanId, externalLoanId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanSchedulePeriodData> fetchWaiverInterestRepaymentData(final Long loanId) {
|
||||
try {
|
||||
|
||||
final LoanRepaymentWaiverMapper rm = new LoanRepaymentWaiverMapper();
|
||||
|
||||
final String sql = "select " + rm.getSchema()
|
||||
+ " where lrs.loan_id = ? and lrs.interest_waived_derived is not null order by lrs.installment ASC ";
|
||||
return this.jdbcTemplate.query(sql, rm, loanId); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class LoanRepaymentWaiverMapper implements RowMapper<LoanSchedulePeriodData> {
|
||||
|
||||
private final String sqlSchema;
|
||||
|
||||
public String getSchema() {
|
||||
return this.sqlSchema;
|
||||
}
|
||||
|
||||
LoanRepaymentWaiverMapper() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("lrs.duedate as dueDate,lrs.interest_waived_derived interestWaived, lrs.installment as installment");
|
||||
sb.append(" from m_loan_repayment_schedule lrs ");
|
||||
sqlSchema = sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanSchedulePeriodData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
|
||||
final Integer period = JdbcSupport.getInteger(rs, "installment");
|
||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
||||
final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
|
||||
|
||||
final LocalDate fromDate = null;
|
||||
final LocalDate obligationsMetOnDate = null;
|
||||
final Boolean complete = false;
|
||||
final BigDecimal principalOriginalDue = null;
|
||||
final BigDecimal principalPaid = null;
|
||||
final BigDecimal principalWrittenOff = null;
|
||||
final BigDecimal principalOutstanding = null;
|
||||
final BigDecimal interestPaid = null;
|
||||
final BigDecimal interestWrittenOff = null;
|
||||
final BigDecimal interestOutstanding = null;
|
||||
final BigDecimal feeChargesDue = null;
|
||||
final BigDecimal feeChargesPaid = null;
|
||||
final BigDecimal feeChargesWaived = null;
|
||||
final BigDecimal feeChargesWrittenOff = null;
|
||||
final BigDecimal feeChargesOutstanding = null;
|
||||
final BigDecimal penaltyChargesDue = null;
|
||||
final BigDecimal penaltyChargesPaid = null;
|
||||
final BigDecimal penaltyChargesWaived = null;
|
||||
final BigDecimal penaltyChargesWrittenOff = null;
|
||||
final BigDecimal penaltyChargesOutstanding = null;
|
||||
|
||||
final BigDecimal totalDueForPeriod = null;
|
||||
final BigDecimal totalPaidInAdvanceForPeriod = null;
|
||||
final BigDecimal totalPaidLateForPeriod = null;
|
||||
final BigDecimal totalActualCostOfLoanForPeriod = null;
|
||||
final BigDecimal outstandingPrincipalBalanceOfLoan = null;
|
||||
final BigDecimal interestDueOnPrincipalOutstanding = null;
|
||||
Long loanId = null;
|
||||
final BigDecimal totalWaived = null;
|
||||
final BigDecimal totalWrittenOff = null;
|
||||
final BigDecimal totalOutstanding = null;
|
||||
final BigDecimal totalPaid = null;
|
||||
final BigDecimal totalInstallmentAmount = null;
|
||||
final BigDecimal totalCredits = null;
|
||||
final BigDecimal totalAccruedInterest = null;
|
||||
|
||||
return LoanSchedulePeriodData.periodWithPayments(loanId, period, fromDate, dueDate, obligationsMetOnDate, complete,
|
||||
principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
|
||||
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesDue,
|
||||
feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
|
||||
penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, totalPaid,
|
||||
totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, totalWaived, totalWrittenOff, totalOutstanding,
|
||||
totalActualCostOfLoanForPeriod, totalInstallmentAmount, totalCredits, false, totalAccruedInterest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate retrieveMinimumDateOfRepaymentTransaction(Long loanId) {
|
||||
return this.jdbcTemplate.queryForObject(
|
||||
|
||||
+13
-8
@@ -25,6 +25,7 @@ import org.apache.fineract.infrastructure.event.business.BusinessEventListener;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanStatusChangedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@@ -47,12 +48,15 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
|
||||
public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
|
||||
final Loan loan = event.get();
|
||||
log.debug("Loan Status change for loan {}", loan.getId());
|
||||
if (loan.getStatus().isClosedObligationsMet() || loan.getStatus().isOverpaid()) {
|
||||
log.debug("Loan Status {} for loan {}", loan.getStatus().getCode(), loan.getId());
|
||||
loanAccrualsProcessingService.processAccrualsForLoanClosure(loan);
|
||||
}
|
||||
if (loan.isOpen()) {
|
||||
LoanStatus oldStatus = event.getOldStatus();
|
||||
LoanStatus newStatus = loan.getStatus();
|
||||
if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
|
||||
log.debug("Loan Status {} for loan {}", newStatus.getCode(), loan.getId());
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan);
|
||||
} else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
|
||||
loan.handleMaturityDateActivate();
|
||||
loanAccrualsProcessingService.processAccrualsOnLoanReopen(loan);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,10 +68,11 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
|
||||
public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
|
||||
final Loan loan = event.get();
|
||||
if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
|
||||
if (loan.getStatus().isClosedObligationsMet() || loan.getStatus().isOverpaid()) {
|
||||
LoanStatus oldStatus = event.getOldStatus();
|
||||
LoanStatus newStatus = loan.getStatus();
|
||||
if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
|
||||
loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
|
||||
}
|
||||
if (loan.isOpen()) {
|
||||
} else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
|
||||
loanAccrualActivityProcessingService.processAccrualActivityForLoanReopen(loan);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-25
@@ -597,15 +597,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (loan.getLoanProduct().isMultiDisburseLoan()) {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = loan.retrieveListOfTransactionsForReprocessing();
|
||||
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = transactionProcessorFactory
|
||||
.determineProcessor(loan.getTransactionProcessingStrategyCode());
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(),
|
||||
allNonContraTransactionsPostDisbursement, loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||
loan.getActiveCharges());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
mapEntry.getValue().updateLoan(loan);
|
||||
}
|
||||
loan.getLoanTransactions().addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
changedTransactionDetail = loan.reprocessTransactions();
|
||||
}
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
}
|
||||
@@ -1023,7 +1015,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
// FIXME - kw - update account balance to negative amount.
|
||||
handleLoanOverpayment(loan, transactionDate, loanLifecycleStateMachine);
|
||||
statusChanged = true;
|
||||
} else if (loan.getSummary().isRepaidInFull(loan.loanCurrency())) {
|
||||
} else if (loan.getSummary().isRepaidInFull(loan.getCurrency())) {
|
||||
handleLoanRepaymentInFull(loan, transactionDate, loanLifecycleStateMachine);
|
||||
statusChanged = true;
|
||||
} else {
|
||||
@@ -1077,7 +1069,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final boolean isTransactionChronologicallyLatest = loan
|
||||
.isChronologicallyLatestRepaymentOrWaiver(newInterestPaymentWaiverTransaction);
|
||||
|
||||
if (newInterestPaymentWaiverTransaction.isNotZero(loan.getLoanRepaymentScheduleDetail().getCurrency())) {
|
||||
if (newInterestPaymentWaiverTransaction.isNotZero()) {
|
||||
loan.addLoanTransaction(newInterestPaymentWaiverTransaction);
|
||||
}
|
||||
|
||||
@@ -1133,19 +1125,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = loan.retrieveListOfTransactionsForReprocessing();
|
||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(
|
||||
loan.getDisbursementDate(), allNonContraTransactionsPostDisbursement, loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
mapEntry.getValue().updateLoan(loan);
|
||||
}
|
||||
/***
|
||||
* Commented since throwing exception if external id present for one of the transactions. for this need to save
|
||||
* the reversed transactions first and then new transactions.
|
||||
*/
|
||||
loan.getLoanTransactions().addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
return changedTransactionDetail;
|
||||
return loan.reprocessTransactions();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -1503,7 +1483,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
|
||||
boolean thereIsNewTransaction = newTransactionDetail.isGreaterThanZero(loan.getPrincipal().getCurrency());
|
||||
boolean thereIsNewTransaction = newTransactionDetail.isGreaterThanZero();
|
||||
if (thereIsNewTransaction) {
|
||||
if (paymentDetail != null) {
|
||||
this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
|
||||
|
||||
+3
-2
@@ -30,6 +30,7 @@ import java.util.stream.Stream;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
@@ -60,7 +61,7 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
||||
List<LoanTransaction> collect) {
|
||||
collect.add(new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), lt.getTypeOf().getValue(), lt.getDateOf(), lt.getAmount(),
|
||||
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
|
||||
if (lt.getTypeOf().isDisbursement() && refundFinal.get().compareTo(BigDecimal.ZERO) > 0) {
|
||||
if (lt.getTypeOf().isDisbursement() && MathUtil.isGreaterThanZero(refundFinal.get())) {
|
||||
if (lt.getAmount().compareTo(refundFinal.get()) <= 0) {
|
||||
collect.add(
|
||||
new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), REPAYMENT.getValue(), lt.getDateOf(), lt.getAmount(),
|
||||
@@ -97,7 +98,7 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
||||
}
|
||||
|
||||
private boolean isTransactionNeededForInterestRefundCalculations(LoanTransaction lt) {
|
||||
return lt.isNotReversed() && !lt.isAccrual() && !lt.isAccrualActivity() && !lt.isInterestRefund();
|
||||
return lt.isNotReversed() && !lt.isAccrualRelated() && !lt.isInterestRefund();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-12
@@ -24,7 +24,6 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
@@ -122,8 +121,7 @@ public class LoanReAgingServiceImpl {
|
||||
}
|
||||
reverseReAgeTransaction(reAgeTransaction, command);
|
||||
loanTransactionRepository.saveAndFlush(reAgeTransaction);
|
||||
|
||||
reProcessLoanTransactions(reAgeTransaction.getLoan());
|
||||
loan.reprocessTransactions();
|
||||
loan.updateLoanScheduleDependentDerivedFields();
|
||||
persistNote(loan, command, changes);
|
||||
|
||||
@@ -179,15 +177,6 @@ public class LoanReAgingServiceImpl {
|
||||
return new LoanReAgeParameter(reAgeTransaction, periodFrequencyType, periodFrequencyNumber, startDate, numberOfInstallments);
|
||||
}
|
||||
|
||||
private void reProcessLoanTransactions(Loan loan) {
|
||||
final List<LoanTransaction> filteredTransactions = loan.retrieveListOfTransactionsForReprocessing();
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(), filteredTransactions,
|
||||
loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
||||
}
|
||||
|
||||
private void persistNote(Loan loan, JsonCommand command, Map<String, Object> changes) {
|
||||
if (command.hasParameter("note")) {
|
||||
final String note = command.stringValueOfParameterNamed("note");
|
||||
|
||||
+1
-11
@@ -24,7 +24,6 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
@@ -129,16 +128,7 @@ public class LoanReAmortizationServiceImpl {
|
||||
LoanReAmortizationApiConstants.externalIdParameterName);
|
||||
reAmortizeTransaction.reverse(reversalExternalId);
|
||||
reAmortizeTransaction.manuallyAdjustedOrReversed();
|
||||
reProcessLoanTransactions(reAmortizeTransaction.getLoan());
|
||||
}
|
||||
|
||||
private void reProcessLoanTransactions(Loan loan) {
|
||||
final List<LoanTransaction> filteredTransactions = loan.retrieveListOfTransactionsForReprocessing();
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(), filteredTransactions,
|
||||
loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
||||
reAmortizeTransaction.getLoan().reprocessTransactions();
|
||||
}
|
||||
|
||||
private LoanTransaction findLatestNonReversedReAmortizeTransaction(Loan loan) {
|
||||
|
||||
+3
-2
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
|
||||
@@ -107,8 +108,8 @@ public class LoanAccountAutoStarter {
|
||||
@Bean
|
||||
@Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class)
|
||||
public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator,
|
||||
LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
@Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService) {
|
||||
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, progressiveLoanInterestRefundService);
|
||||
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -102,6 +102,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanApplicationWritePla
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanArrearsAgingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanArrearsAgingServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssemblerImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanCalculateRepaymentPastDueService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadService;
|
||||
@@ -217,7 +218,7 @@ public class LoanAccountConfiguration {
|
||||
LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||
LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler, LoanChargeMapper loanChargeMapper,
|
||||
LoanCollateralManagementMapper loanCollateralManagementMapper, LoanAccrualsProcessingService loanAccrualsProcessingService) {
|
||||
return new LoanAssembler(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
|
||||
return new LoanAssemblerImpl(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
|
||||
fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler,
|
||||
loanSummaryWrapper, loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService,
|
||||
workingDaysRepository, rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository,
|
||||
|
||||
+2
-2
@@ -105,7 +105,7 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"LoanReAmortizeBusinessEvent", "LoanUndoReAmortizeBusinessEvent", "LoanTransactionInterestPaymentWaiverPreBusinessEvent",
|
||||
"LoanTransactionInterestPaymentWaiverPostBusinessEvent", "LoanTransactionAccrualActivityPostBusinessEvent",
|
||||
"LoanTransactionAccrualActivityPreBusinessEvent", "LoanTransactionInterestRefundPostBusinessEvent",
|
||||
"LoanTransactionInterestRefundPreBusinessEvent");
|
||||
"LoanTransactionInterestRefundPreBusinessEvent", "LoanAccrualAdjustmentTransactionBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
@@ -191,7 +191,7 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"LoanReAmortizeBusinessEvent", "LoanUndoReAmortizeBusinessEvent", "LoanTransactionInterestPaymentWaiverPreBusinessEvent",
|
||||
"LoanTransactionInterestPaymentWaiverPostBusinessEvent", "LoanTransactionAccrualActivityPostBusinessEvent",
|
||||
"LoanTransactionAccrualActivityPreBusinessEvent", "LoanTransactionInterestRefundPostBusinessEvent",
|
||||
"LoanTransactionInterestRefundPreBusinessEvent");
|
||||
"LoanTransactionInterestRefundPreBusinessEvent", "LoanAccrualAdjustmentTransactionBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
|
||||
+10
-3
@@ -177,13 +177,20 @@ public class LoanTest {
|
||||
|
||||
final LoanTransaction loanTransaction = Mockito.mock(LoanTransaction.class);
|
||||
when(loanTransaction.isNotReversed()).thenReturn(Boolean.TRUE);
|
||||
when(loanTransaction.isAccrualTransaction()).thenReturn(Boolean.FALSE);
|
||||
when(loanTransaction.isAccrual()).thenReturn(Boolean.FALSE);
|
||||
when(loanTransaction.isAccrualAdjustment()).thenReturn(Boolean.FALSE);
|
||||
final LoanTransaction loanTransaction2 = Mockito.mock(LoanTransaction.class);
|
||||
when(loanTransaction2.isNotReversed()).thenReturn(Boolean.TRUE);
|
||||
when(loanTransaction2.isAccrualTransaction()).thenReturn(Boolean.FALSE);
|
||||
when(loanTransaction2.isAccrual()).thenReturn(Boolean.FALSE);
|
||||
when(loanTransaction2.isAccrualAdjustment()).thenReturn(Boolean.FALSE);
|
||||
final LoanTransaction loanTransaction3 = Mockito.mock(LoanTransaction.class);
|
||||
when(loanTransaction3.isNotReversed()).thenReturn(Boolean.TRUE);
|
||||
when(loanTransaction3.isAccrualTransaction()).thenReturn(Boolean.TRUE);
|
||||
when(loanTransaction3.isAccrual()).thenReturn(Boolean.TRUE);
|
||||
when(loanTransaction3.isAccrualAdjustment()).thenReturn(Boolean.FALSE);
|
||||
final LoanTransaction loanTransaction4 = Mockito.mock(LoanTransaction.class);
|
||||
when(loanTransaction4.isNotReversed()).thenReturn(Boolean.TRUE);
|
||||
when(loanTransaction4.isAccrual()).thenReturn(Boolean.FALSE);
|
||||
when(loanTransaction4.isAccrualAdjustment()).thenReturn(Boolean.TRUE);
|
||||
ReflectionTestUtils.setField(loan, "loanTransactions", List.of(loanTransaction, loanTransaction2, loanTransaction3));
|
||||
final LoanTransaction userTransaction = loan.getLastUserTransaction();
|
||||
assertNotNull(userTransaction);
|
||||
|
||||
Reference in New Issue
Block a user