FINERACT-2348: Calculate and store Capitalized income / Buydown fee amortization allocation schedule
This commit is contained in:
committed by
Adam Saghy
parent
9790f59e4a
commit
cd0ecfcda0
+124
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationMappingDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData.AmortizationMappingData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeeBalanceRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoanAmortizationAllocationServiceImpl implements LoanAmortizationAllocationService {
|
||||
|
||||
private final LoanAmortizationAllocationMappingRepository loanAmortizationAllocationMappingRepository;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository;
|
||||
private final LoanBuyDownFeeBalanceRepository buyDownFeeBalanceRepository;
|
||||
|
||||
@Override
|
||||
public LoanAmortizationAllocationData retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(final Long loanTransactionId,
|
||||
final Long loanId) {
|
||||
final LoanTransaction loanTransaction = this.loanTransactionRepository.findByIdAndLoanId(loanTransactionId, loanId)
|
||||
.orElseThrow(() -> new LoanTransactionNotFoundException(loanTransactionId));
|
||||
if (!LoanTransactionType.BUY_DOWN_FEE.equals(loanTransaction.getTypeOf())) {
|
||||
throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.buydown.fee.transaction",
|
||||
"Transaction with ID " + loanTransactionId + " is not a Buy Down Fee transaction");
|
||||
}
|
||||
return retrieveLoanAmortizationAllocationData(loanTransaction, loanId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanAmortizationAllocationData retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(final Long loanTransactionId,
|
||||
final Long loanId) {
|
||||
final LoanTransaction loanTransaction = this.loanTransactionRepository.findByIdAndLoanId(loanTransactionId, loanId)
|
||||
.orElseThrow(() -> new LoanTransactionNotFoundException(loanTransactionId));
|
||||
if (!LoanTransactionType.CAPITALIZED_INCOME.equals(loanTransaction.getTypeOf())) {
|
||||
throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.capitalized.income.transaction",
|
||||
"Transaction with ID " + loanTransactionId + " is not a Capitalized Income transaction");
|
||||
}
|
||||
return retrieveLoanAmortizationAllocationData(loanTransaction, loanId);
|
||||
}
|
||||
|
||||
private LoanAmortizationAllocationData retrieveLoanAmortizationAllocationData(final LoanTransaction loanTransaction,
|
||||
final Long loanId) {
|
||||
try {
|
||||
final Long loanTransactionId = loanTransaction.getId();
|
||||
final AmortizationAllocationBaseTransactionDTO baseTransactionInfo = getBaseTransactionInfo(loanTransaction, loanId);
|
||||
if (baseTransactionInfo == null) {
|
||||
throw new LoanTransactionNotFoundException(loanTransactionId);
|
||||
}
|
||||
|
||||
final List<AmortizationAllocationMappingDTO> amortizationMappings = loanAmortizationAllocationMappingRepository
|
||||
.findAmortizationMappingsByBaseTransactionAndLoan(loanTransactionId, loanId);
|
||||
|
||||
final List<AmortizationMappingData> mappings = amortizationMappings.stream()
|
||||
.map(dto -> AmortizationMappingData.builder().amortizationLoanTransactionId(dto.getAmortizationLoanTransactionId())
|
||||
.amortizationLoanTransactionExternalId(dto.getAmortizationLoanTransactionExternalId())
|
||||
.date(dto.getAmortizationDate()).type(dto.getAmortizationType()).amount(dto.getAmount()).build())
|
||||
.toList();
|
||||
|
||||
return new LoanAmortizationAllocationData(baseTransactionInfo.getLoanId(), baseTransactionInfo.getLoanExternalId(),
|
||||
baseTransactionInfo.getBaseLoanTransactionId(), baseTransactionInfo.getBaseLoanTransactionDate(),
|
||||
baseTransactionInfo.getBaseLoanTransactionAmount(), baseTransactionInfo.getUnrecognizedAmount(),
|
||||
baseTransactionInfo.getChargedOffAmount(), baseTransactionInfo.getAdjustmentAmount(), mappings);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new LoanTransactionNotFoundException(loanTransaction.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private AmortizationAllocationBaseTransactionDTO getBaseTransactionInfo(final LoanTransaction loanTransaction, final Long loanId) {
|
||||
if (loanTransaction.isBuyDownFee()) {
|
||||
return buyDownFeeBalanceRepository.findBaseTransactionInfo(loanTransaction.getId(), loanId);
|
||||
} else {
|
||||
return capitalizedIncomeBalanceRepository.findBaseTransactionInfo(loanTransaction.getId(), loanId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanAmortizationAllocationMapping createAmortizationAllocationMappingWithBaseLoanTransaction(
|
||||
final LoanTransaction loanTransaction, final BigDecimal amount, final AmortizationType amortizationType) {
|
||||
return new LoanAmortizationAllocationMapping(loanTransaction.getLoan().getId(), loanTransaction.getId(), null, null,
|
||||
amortizationType, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(
|
||||
final LoanAmortizationAllocationMapping amortizationAllocationMapping, final LoanTransaction amortizationTransaction) {
|
||||
final LoanAmortizationAllocationMapping updatedMapping = new LoanAmortizationAllocationMapping(
|
||||
amortizationAllocationMapping.getLoanId(), amortizationAllocationMapping.getBaseLoanTransactionId(),
|
||||
amortizationTransaction.getTransactionDate(), amortizationTransaction.getId(),
|
||||
amortizationAllocationMapping.getAmortizationType(), amortizationAllocationMapping.getAmount());
|
||||
loanAmortizationAllocationMappingRepository.save(updatedMapping);
|
||||
}
|
||||
|
||||
}
|
||||
+138
-29
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -33,7 +34,10 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||
@@ -54,34 +58,83 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanJournalEntryPoster journalEntryPoster;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final LoanAmortizationAllocationService loanAmortizationAllocationService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void processBuyDownFeeAmortizationTillDate(@NonNull Loan loan, @NonNull LocalDate tillDate, boolean addJournal) {
|
||||
List<LoanBuyDownFeeBalance> balances = loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId());
|
||||
final List<LoanBuyDownFeeBalance> balances = loanBuyDownFeeBalanceRepository.findAllByLoanIdAndClosedFalse(loan.getId());
|
||||
|
||||
LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate()
|
||||
final LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate()
|
||||
: getFinalBuyDownFeeAmortizationTransactionDate(loan);
|
||||
LocalDate tillDatePlusOne = tillDate.plusDays(1);
|
||||
if (tillDatePlusOne.isAfter(maturityDate)) {
|
||||
tillDatePlusOne = maturityDate;
|
||||
}
|
||||
|
||||
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
|
||||
Money totalAmortization = Money.zero(loan.getCurrency());
|
||||
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
|
||||
BigDecimal totalAmortizedAccumulator = totalAmortized;
|
||||
for (LoanBuyDownFeeBalance balance : balances) {
|
||||
List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments, maturityDate,
|
||||
loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), tillDatePlusOne, loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate);
|
||||
|
||||
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(amortizationTillDate.getAmount()));
|
||||
BigDecimal amortizationAmount;
|
||||
AmortizationType amortizationType;
|
||||
if (!balance.isDeleted()) {
|
||||
final List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
final Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), tillDatePlusOne, loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate);
|
||||
if (!adjustments.isEmpty()) {
|
||||
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
|
||||
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(balance.getLoanTransaction().getId(),
|
||||
loan.getId());
|
||||
BigDecimal amortizedAmount = BigDecimal.ZERO;
|
||||
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
|
||||
.getAmortizationMappings()) {
|
||||
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
|
||||
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
|
||||
}
|
||||
}
|
||||
amortizationAmount = amortizedAmount;
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
|
||||
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else {
|
||||
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
} else {
|
||||
amortizationAmount = totalAmortization.getAmount().subtract(totalAmortizedAccumulator);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(amortizationTillDate.getAmount()));
|
||||
} else {
|
||||
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
balance.setClosed(true);
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
}
|
||||
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
|
||||
.createAmortizationAllocationMappingWithBaseLoanTransaction(balance.getLoanTransaction(), amortizationAmount,
|
||||
amortizationType);
|
||||
loanAmortizationAllocationMappings.add(loanAmortizationAllocationMapping);
|
||||
}
|
||||
}
|
||||
|
||||
loanBuyDownFeeBalanceRepository.saveAll(balances);
|
||||
|
||||
BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
|
||||
BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized);
|
||||
final BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized);
|
||||
|
||||
if (!MathUtil.isZero(totalAmortizationAmount)) {
|
||||
LoanTransaction transaction = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
@@ -91,14 +144,17 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
Money.of(loan.getCurrency(), MathUtil.negate(totalAmortizationAmount)), tillDate, externalIdFactory.create());
|
||||
loan.addLoanTransaction(transaction);
|
||||
|
||||
transaction = loanTransactionRepository.save(transaction);
|
||||
loanTransactionRepository.flush();
|
||||
transaction = loanTransactionRepository.saveAndFlush(transaction);
|
||||
final LoanTransaction finalTransaction = transaction;
|
||||
loanAmortizationAllocationMappings.forEach(loanAmortizationAllocationMapping -> loanAmortizationAllocationService
|
||||
.setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(loanAmortizationAllocationMapping,
|
||||
finalTransaction));
|
||||
|
||||
if (addJournal) {
|
||||
journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false);
|
||||
}
|
||||
|
||||
BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
final BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
? new LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent(transaction)
|
||||
: new LoanBuyDownFeeAmortizationAdjustmentTransactionCreatedBusinessEvent(transaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(event);
|
||||
@@ -165,7 +221,7 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
|
||||
});
|
||||
|
||||
for (LoanBuyDownFeeBalance balance : loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId())) {
|
||||
for (LoanBuyDownFeeBalance balance : loanBuyDownFeeBalanceRepository.findAllByLoanIdAndDeletedFalseAndClosedFalse(loan.getId())) {
|
||||
balance.setUnrecognizedAmount(balance.getChargedOffAmount());
|
||||
balance.setChargedOffAmount(BigDecimal.ZERO);
|
||||
}
|
||||
@@ -173,25 +229,75 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
|
||||
private Optional<LoanTransaction> createBuyDownFeeAmortizationTransaction(final Loan loan, final LocalDate transactionDate,
|
||||
final boolean isChargeOff, final LoanTransaction chargeOffTransaction) {
|
||||
ExternalId externalId = externalIdFactory.create();
|
||||
final ExternalId externalId = externalIdFactory.create();
|
||||
|
||||
final List<LoanBuyDownFeeBalance> balances = loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId());
|
||||
final List<LoanBuyDownFeeBalance> balances = loanBuyDownFeeBalanceRepository.findAllByLoanIdAndClosedFalse(loan.getId());
|
||||
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
|
||||
|
||||
BigDecimal totalAmortizationAmount = BigDecimal.ZERO;
|
||||
BigDecimal totalAmortization = BigDecimal.ZERO;
|
||||
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
|
||||
BigDecimal totalAmortizedAccumulator = totalAmortized;
|
||||
for (LoanBuyDownFeeBalance balance : balances) {
|
||||
List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : transactionDate;
|
||||
final Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), maturityDate, loan.getCurrency());
|
||||
totalAmortizationAmount = totalAmortizationAmount.add(amortizationTillDate.getAmount());
|
||||
if (isChargeOff) {
|
||||
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
|
||||
BigDecimal amortizationAmount;
|
||||
AmortizationType amortizationType;
|
||||
if (!balance.isDeleted()) {
|
||||
final List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
final LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : transactionDate;
|
||||
final Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), maturityDate, loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate.getAmount());
|
||||
if (!adjustments.isEmpty()) {
|
||||
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
|
||||
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(balance.getLoanTransaction().getId(),
|
||||
loan.getId());
|
||||
BigDecimal amortizedAmount = BigDecimal.ZERO;
|
||||
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
|
||||
.getAmortizationMappings()) {
|
||||
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
|
||||
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
|
||||
}
|
||||
}
|
||||
amortizationAmount = amortizedAmount;
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
|
||||
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else {
|
||||
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
} else {
|
||||
amortizationAmount = totalAmortization.subtract(totalAmortizedAccumulator);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
if (isChargeOff) {
|
||||
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
|
||||
}
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
} else {
|
||||
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
balance.setClosed(true);
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
}
|
||||
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
|
||||
.createAmortizationAllocationMappingWithBaseLoanTransaction(balance.getLoanTransaction(), amortizationAmount,
|
||||
amortizationType);
|
||||
loanAmortizationAllocationMappings.add(loanAmortizationAllocationMapping);
|
||||
}
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
BigDecimal amortizedAmount = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
|
||||
BigDecimal totalUnrecognizedAmount = totalAmortizationAmount.subtract(amortizedAmount);
|
||||
final BigDecimal totalUnrecognizedAmount = totalAmortization.subtract(totalAmortized);
|
||||
if (MathUtil.isZero(totalUnrecognizedAmount)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -207,6 +313,9 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
|
||||
loan.addLoanTransaction(amortizationTransaction);
|
||||
loanTransactionRepository.saveAndFlush(amortizationTransaction);
|
||||
loanAmortizationAllocationMappings.forEach(loanAmortizationAllocationMapping -> loanAmortizationAllocationService
|
||||
.setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(loanAmortizationAllocationMapping,
|
||||
amortizationTransaction));
|
||||
|
||||
return Optional.of(amortizationTransaction);
|
||||
}
|
||||
|
||||
+142
-28
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -34,7 +35,10 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||
@@ -56,6 +60,7 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanJournalEntryPoster journalEntryPoster;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final LoanAmortizationAllocationService loanAmortizationAllocationService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@@ -108,23 +113,75 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
|
||||
final List<LoanCapitalizedIncomeBalance> balances = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loan.getId());
|
||||
final List<LoanCapitalizedIncomeBalance> balances = loanCapitalizedIncomeBalanceRepository
|
||||
.findAllByLoanIdAndClosedFalse(loan.getId());
|
||||
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
|
||||
|
||||
BigDecimal totalAmortizationAmount = BigDecimal.ZERO;
|
||||
BigDecimal totalAmortization = BigDecimal.ZERO;
|
||||
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
|
||||
BigDecimal totalAmortizedAccumulator = totalAmortized;
|
||||
for (LoanCapitalizedIncomeBalance balance : balances) {
|
||||
List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : transactionDate;
|
||||
final Money amortizationTillDate = CapitalizedIncomeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), maturityDate, loan.getCurrency());
|
||||
totalAmortizationAmount = totalAmortizationAmount.add(amortizationTillDate.getAmount());
|
||||
if (isChargeOff) {
|
||||
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
|
||||
BigDecimal amortizationAmount;
|
||||
AmortizationType amortizationType;
|
||||
if (!balance.isDeleted()) {
|
||||
final List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
final LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : transactionDate;
|
||||
final Money amortizationTillDate = CapitalizedIncomeAmortizationUtil.calculateTotalAmortizationTillDate(balance,
|
||||
adjustments, maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), maturityDate,
|
||||
loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate.getAmount());
|
||||
if (!adjustments.isEmpty()) {
|
||||
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
|
||||
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(balance.getLoanTransaction().getId(),
|
||||
loan.getId());
|
||||
BigDecimal amortizedAmount = BigDecimal.ZERO;
|
||||
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
|
||||
.getAmortizationMappings()) {
|
||||
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
|
||||
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
|
||||
}
|
||||
}
|
||||
amortizationAmount = amortizedAmount;
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
|
||||
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else {
|
||||
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
} else {
|
||||
amortizationAmount = totalAmortization.subtract(totalAmortizedAccumulator);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
if (isChargeOff) {
|
||||
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
|
||||
}
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
} else {
|
||||
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
balance.setClosed(true);
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
}
|
||||
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
|
||||
.createAmortizationAllocationMappingWithBaseLoanTransaction(balance.getLoanTransaction(), amortizationAmount,
|
||||
amortizationType);
|
||||
loanAmortizationAllocationMappings.add(loanAmortizationAllocationMapping);
|
||||
}
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
BigDecimal amortizedAmount = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
|
||||
BigDecimal totalUnrecognizedAmount = totalAmortizationAmount.subtract(amortizedAmount);
|
||||
final BigDecimal totalUnrecognizedAmount = totalAmortization.subtract(totalAmortized);
|
||||
if (MathUtil.isZero(totalUnrecognizedAmount)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -141,6 +198,9 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
|
||||
loan.addLoanTransaction(amortizationTransaction);
|
||||
loanTransactionRepository.saveAndFlush(amortizationTransaction);
|
||||
loanAmortizationAllocationMappings.forEach(loanAmortizationAllocationMapping -> loanAmortizationAllocationService
|
||||
.setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(loanAmortizationAllocationMapping,
|
||||
amortizationTransaction));
|
||||
|
||||
return Optional.of(amortizationTransaction);
|
||||
}
|
||||
@@ -162,7 +222,8 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false);
|
||||
});
|
||||
|
||||
for (LoanCapitalizedIncomeBalance balance : loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loan.getId())) {
|
||||
for (LoanCapitalizedIncomeBalance balance : loanCapitalizedIncomeBalanceRepository
|
||||
.findAllByLoanIdAndDeletedFalseAndClosedFalse(loan.getId())) {
|
||||
balance.setUnrecognizedAmount(balance.getChargedOffAmount());
|
||||
balance.setChargedOffAmount(BigDecimal.ZERO);
|
||||
}
|
||||
@@ -172,30 +233,80 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
@Transactional
|
||||
public void processCapitalizedIncomeAmortizationTillDate(@NonNull final Loan loan, @NonNull final LocalDate tillDate,
|
||||
final boolean addJournal) {
|
||||
List<LoanCapitalizedIncomeBalance> balances = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loan.getId());
|
||||
final List<LoanCapitalizedIncomeBalance> balances = loanCapitalizedIncomeBalanceRepository
|
||||
.findAllByLoanIdAndClosedFalse(loan.getId());
|
||||
|
||||
LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate()
|
||||
final LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate()
|
||||
: getFinalCapitalizedIncomeAmortizationTransactionDate(loan);
|
||||
LocalDate tillDatePlusOne = tillDate.plusDays(1);
|
||||
if (tillDatePlusOne.isAfter(maturityDate)) {
|
||||
tillDatePlusOne = maturityDate;
|
||||
}
|
||||
|
||||
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
|
||||
Money totalAmortization = Money.zero(loan.getCurrency());
|
||||
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
|
||||
BigDecimal totalAmortizedAccumulator = totalAmortized;
|
||||
for (LoanCapitalizedIncomeBalance balance : balances) {
|
||||
List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
Money amortizationTillDate = CapitalizedIncomeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), tillDatePlusOne, loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate);
|
||||
|
||||
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(amortizationTillDate.getAmount()));
|
||||
BigDecimal amortizationAmount;
|
||||
AmortizationType amortizationType;
|
||||
if (!balance.isDeleted()) {
|
||||
final List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
final Money amortizationTillDate = CapitalizedIncomeAmortizationUtil.calculateTotalAmortizationTillDate(balance,
|
||||
adjustments, maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), tillDatePlusOne,
|
||||
loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate);
|
||||
if (!adjustments.isEmpty()) {
|
||||
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
|
||||
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(balance.getLoanTransaction().getId(),
|
||||
loan.getId());
|
||||
BigDecimal amortizedAmount = BigDecimal.ZERO;
|
||||
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
|
||||
.getAmortizationMappings()) {
|
||||
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
|
||||
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
|
||||
}
|
||||
}
|
||||
amortizationAmount = amortizedAmount;
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
|
||||
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else {
|
||||
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
} else {
|
||||
amortizationAmount = totalAmortization.getAmount().subtract(totalAmortizedAccumulator);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(amortizationTillDate.getAmount()));
|
||||
} else {
|
||||
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
balance.setClosed(true);
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
}
|
||||
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
|
||||
.createAmortizationAllocationMappingWithBaseLoanTransaction(balance.getLoanTransaction(), amortizationAmount,
|
||||
amortizationType);
|
||||
loanAmortizationAllocationMappings.add(loanAmortizationAllocationMapping);
|
||||
}
|
||||
}
|
||||
|
||||
loanCapitalizedIncomeBalanceRepository.saveAll(balances);
|
||||
|
||||
BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
|
||||
BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized);
|
||||
final BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized);
|
||||
|
||||
if (!MathUtil.isZero(totalAmortizationAmount)) {
|
||||
LoanTransaction transaction = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
@@ -205,14 +316,17 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
Money.of(loan.getCurrency(), MathUtil.negate(totalAmortizationAmount)), tillDate, externalIdFactory.create());
|
||||
loan.addLoanTransaction(transaction);
|
||||
|
||||
transaction = loanTransactionRepository.save(transaction);
|
||||
loanTransactionRepository.flush();
|
||||
transaction = loanTransactionRepository.saveAndFlush(transaction);
|
||||
final LoanTransaction finalTransaction = transaction;
|
||||
loanAmortizationAllocationMappings.forEach(loanAmortizationAllocationMapping -> loanAmortizationAllocationService
|
||||
.setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(loanAmortizationAllocationMapping,
|
||||
finalTransaction));
|
||||
|
||||
if (addJournal) {
|
||||
journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false);
|
||||
}
|
||||
|
||||
BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
final BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
? new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(transaction)
|
||||
: new LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent(transaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(event);
|
||||
|
||||
+18
-5
@@ -493,8 +493,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
final Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
||||
BigDecimal capitalizedIncomeBalance = BigDecimal.ZERO;
|
||||
if (loan.getLoanProduct().getLoanProductRelatedDetail().isEnableIncomeCapitalization()) {
|
||||
capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loanId).stream()
|
||||
.map(LoanCapitalizedIncomeBalance::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository.findAllByLoanIdAndDeletedFalseAndClosedFalse(loanId)
|
||||
.stream().map(LoanCapitalizedIncomeBalance::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
transactionAmount = loan.getLoanProduct().isAllowApprovedDisbursedAmountsOverApplied()
|
||||
? loanMaximumAmountCalculator.getOverAppliedMax(loan)
|
||||
@@ -517,7 +517,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
break;
|
||||
case CAPITALIZED_INCOME_ADJUSTMENT:
|
||||
final LoanCapitalizedIncomeBalance loanCapitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionId(loanId, transactionId);
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loanId, transactionId);
|
||||
|
||||
transactionAmount = (loanCapitalizedIncomeBalance == null) ? BigDecimal.ZERO
|
||||
: loanCapitalizedIncomeBalance.getAmount()
|
||||
@@ -527,8 +527,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
paymentOptions, retriveLoanCurrencyData(loanId), classificationOptions);
|
||||
break;
|
||||
case BUY_DOWN_FEE_ADJUSTMENT:
|
||||
final LoanBuyDownFeeBalance loanBuyDownFeeBalance = loanBuyDownFeeBalanceRepository.findByLoanIdAndLoanTransactionId(loanId,
|
||||
transactionId);
|
||||
final LoanBuyDownFeeBalance loanBuyDownFeeBalance = loanBuyDownFeeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loanId, transactionId);
|
||||
|
||||
transactionAmount = (loanBuyDownFeeBalance == null) ? BigDecimal.ZERO
|
||||
: loanBuyDownFeeBalance.getAmount().subtract(MathUtil.nullToZero(loanBuyDownFeeBalance.getAmountAdjustment()));
|
||||
@@ -2522,6 +2522,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
return resolvedLoanId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getResolvedLoanTransactionId(final Long transactionId, final ExternalId externalTransactionId) {
|
||||
Long resolvedLoanTransactionId = transactionId;
|
||||
if (resolvedLoanTransactionId == null) {
|
||||
externalTransactionId.throwExceptionIfEmpty();
|
||||
resolvedLoanTransactionId = retrieveLoanTransactionIdByExternalId(externalTransactionId);
|
||||
if (resolvedLoanTransactionId == null) {
|
||||
throw new LoanTransactionNotFoundException(externalTransactionId);
|
||||
}
|
||||
}
|
||||
return resolvedLoanTransactionId;
|
||||
}
|
||||
|
||||
private LoanTransaction deriveDefaultInterestWaiverTransaction(final Loan loan) {
|
||||
final Money totalInterestOutstanding = loan.getTotalInterestOutstandingOnLoan();
|
||||
Money possibleInterestToWaive = totalInterestOutstanding.copy();
|
||||
|
||||
+7
-5
@@ -147,12 +147,13 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
}
|
||||
|
||||
LoanCapitalizedIncomeBalance capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionId(loan.getId(), transactionToAdjust.getId());
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loan.getId(), transactionToAdjust.getId());
|
||||
if (MathUtil.isGreaterThanZero(capitalizedIncomeBalance.getAmountAdjustment())) {
|
||||
throw new InvalidLoanTransactionTypeException("transaction", "capitalizedIncome.cannot.be.reversed.when.adjusted",
|
||||
"Capitalized income transaction cannot be reversed when non-reversed adjustment exists for it.");
|
||||
}
|
||||
loanCapitalizedIncomeBalanceRepository.delete(capitalizedIncomeBalance);
|
||||
capitalizedIncomeBalance.setDeleted(true);
|
||||
loanCapitalizedIncomeBalanceRepository.saveAndFlush(capitalizedIncomeBalance);
|
||||
}
|
||||
if (transactionToAdjust.isCapitalizedIncomeAdjustment()) {
|
||||
if (newTransactionDetail.isNotZero()) {
|
||||
@@ -174,14 +175,15 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
"Buy down fee transaction cannot be adjusted");
|
||||
}
|
||||
|
||||
LoanBuyDownFeeBalance buyDownFeeBalance = loanBuyDownFeeBalanceRepository.findByLoanIdAndLoanTransactionId(loan.getId(),
|
||||
transactionToAdjust.getId());
|
||||
LoanBuyDownFeeBalance buyDownFeeBalance = loanBuyDownFeeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loan.getId(), transactionToAdjust.getId());
|
||||
|
||||
if (MathUtil.isGreaterThanZero(buyDownFeeBalance.getAmountAdjustment())) {
|
||||
throw new InvalidLoanTransactionTypeException("transaction", "buy.down.fee.cannot.be.reversed.when.adjusted",
|
||||
"Buy down fee transaction cannot be reversed when non-reversed adjustment exists for it.");
|
||||
}
|
||||
loanBuyDownFeeBalanceRepository.delete(buyDownFeeBalance);
|
||||
buyDownFeeBalance.setDeleted(true);
|
||||
loanBuyDownFeeBalanceRepository.saveAndFlush(buyDownFeeBalance);
|
||||
}
|
||||
if (transactionToAdjust.isBuyDownFeeAdjustment()) {
|
||||
if (newTransactionDetail.isNotZero()) {
|
||||
|
||||
+19
-4
@@ -70,6 +70,7 @@ import org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePl
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
@@ -118,6 +119,8 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualEventService
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAmortizationAllocationService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAmortizationAllocationServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanApplicationWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanApplicationWritePlatformServiceJpaRepositoryImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanArrearsAgingService;
|
||||
@@ -566,9 +569,10 @@ public class LoanAccountConfiguration {
|
||||
final ConfigurationDomainService configurationDomainService, final LoanTransactionRepository loanTransactionRepository,
|
||||
final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository,
|
||||
final BusinessEventNotifierService businessEventNotifierService, final LoanJournalEntryPoster journalEntryPoster,
|
||||
final ExternalIdFactory externalIdFactory) {
|
||||
final ExternalIdFactory externalIdFactory, final LoanAmortizationAllocationService loanAmortizationAllocationService) {
|
||||
return new LoanCapitalizedIncomeAmortizationProcessingServiceImpl(configurationDomainService, loanTransactionRepository,
|
||||
loanCapitalizedIncomeBalanceRepository, businessEventNotifierService, journalEntryPoster, externalIdFactory);
|
||||
loanCapitalizedIncomeBalanceRepository, businessEventNotifierService, journalEntryPoster, externalIdFactory,
|
||||
loanAmortizationAllocationService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -577,9 +581,9 @@ public class LoanAccountConfiguration {
|
||||
final LoanTransactionRepository loanTransactionRepository,
|
||||
final LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository,
|
||||
final BusinessEventNotifierService businessEventNotifierService, final LoanJournalEntryPoster journalEntryPoster,
|
||||
final ExternalIdFactory externalIdFactory) {
|
||||
final ExternalIdFactory externalIdFactory, final LoanAmortizationAllocationService loanAmortizationAllocationService) {
|
||||
return new LoanBuyDownFeeAmortizationProcessingServiceImpl(loanTransactionRepository, loanBuyDownFeeBalanceRepository,
|
||||
businessEventNotifierService, journalEntryPoster, externalIdFactory);
|
||||
businessEventNotifierService, journalEntryPoster, externalIdFactory, loanAmortizationAllocationService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -589,4 +593,15 @@ public class LoanAccountConfiguration {
|
||||
LoanBuyDownFeeAmortizationProcessingService loanBuyDownFeeAmortizationProcessingService) {
|
||||
return new LoanBuyDownFeeAmortizationEventService(businessEventNotifierService, loanBuyDownFeeAmortizationProcessingService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanAmortizationAllocationService.class)
|
||||
public LoanAmortizationAllocationService loanAmortizationAllocationService(
|
||||
final LoanAmortizationAllocationMappingRepository loanAmortizationAllocationMappingRepository,
|
||||
final LoanTransactionRepository loanTransactionRepository,
|
||||
final LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
|
||||
final LoanBuyDownFeeBalanceRepository buyDownFeeBalanceRepository) {
|
||||
return new LoanAmortizationAllocationServiceImpl(loanAmortizationAllocationMappingRepository, loanTransactionRepository,
|
||||
capitalizedIncomeBalanceRepository, buyDownFeeBalanceRepository);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,4 +213,7 @@
|
||||
<include file="parts/0192_create_loan_approved_amount_history.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0193_add_column_ip.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0194_fix_missing_permission.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0195_create_loan_amortization_allocation_mapping.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0196_add_deleted_and_closed_to_buy_down_fee_balance.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0197_add_deleted_and_closed_to_capitalized_income_balance.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||
<changeSet author="fineract" id="1" context="postgresql">
|
||||
<createTable tableName="m_loan_amortization_allocation_mapping">
|
||||
<column autoIncrement="true" name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_m_loan_amortization_allocation_mapping"/>
|
||||
</column>
|
||||
|
||||
<column name="loan_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="base_loan_transaction_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amortization_loan_transaction_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amortization_type" type="VARCHAR(20)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amount" type="DECIMAL(19, 6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="timestamp with time zone"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="timestamp with time zone"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
<changeSet author="fineract" id="2" context="mysql">
|
||||
<createTable tableName="m_loan_amortization_allocation_mapping">
|
||||
<column autoIncrement="true" name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_m_loan_amortization_allocation_mapping"/>
|
||||
</column>
|
||||
|
||||
<column name="loan_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="base_loan_transaction_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amortization_loan_transaction_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amortization_type" type="VARCHAR(20)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amount" type="DECIMAL(19, 6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="DATETIME"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="DATETIME"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
<changeSet author="fineract" id="3">
|
||||
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_created_by" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_appuser" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_last_modified_by" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_appuser" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="loan_id" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_loan_id" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_loan" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="base_loan_transaction_id" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_base_transaction_id" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_loan_transaction" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="amortization_loan_transaction_id" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_amortization_transaction_id" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_loan_transaction" validate="true"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addColumn tableName="m_loan_buy_down_fee_balance">
|
||||
<column defaultValueBoolean="false" name="is_deleted" type="boolean">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column defaultValueBoolean="false" name="is_closed" type="boolean">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addColumn tableName="m_loan_capitalized_income_balance">
|
||||
<column defaultValueBoolean="false" name="is_deleted" type="boolean">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column defaultValueBoolean="false" name="is_closed" type="boolean">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user