FINERACT-2081: Refactoring for reprocess transactions and regenerate repayment schedule invocations
This commit is contained in:
committed by
Adam Saghy
parent
6bb68051ce
commit
b1384b9cce
+4
-16
@@ -40,15 +40,12 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@AllArgsConstructor
|
||||
@@ -58,9 +55,7 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanAccountDomainService loanAccountDomainService;
|
||||
private final AccountTransfersService accountTransfersService;
|
||||
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createInterestPause(final ExternalId loanExternalId, final String startDateString,
|
||||
@@ -151,16 +146,9 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
||||
loan);
|
||||
|
||||
final LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
|
||||
|
||||
loan.getLoanTermVariations().add(savedVariation);
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
|
||||
return new CommandProcessingResultBuilder().withEntityId(savedVariation.getId()).build();
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package org.apache.fineract.portfolio.interestpauses.service;
|
||||
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
|
||||
public interface AccountTransfersService {
|
||||
public interface LoanAccountTransfersService {
|
||||
|
||||
void updateLoanTransaction(Long loanTransactionId, LoanTransaction newLoanTransaction);
|
||||
|
||||
+27
-52
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.CUMULATIVE;
|
||||
import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
@@ -829,7 +830,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return lastRepaymentDate;
|
||||
}
|
||||
|
||||
public void removeLoanCharge(final LoanCharge loanCharge) {
|
||||
public Optional<ChangedTransactionDetail> removeLoanCharge(final LoanCharge loanCharge) {
|
||||
final boolean removed = loanCharge.isActive();
|
||||
if (removed) {
|
||||
loanCharge.setActive(false);
|
||||
@@ -848,10 +849,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
* Consider removing this block of code or logically completing it for the future by getting the list of
|
||||
* affected Transactions
|
||||
*/
|
||||
reprocessTransactions();
|
||||
return Optional.ofNullable(reprocessTransactions());
|
||||
}
|
||||
this.charges.remove(loanCharge);
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(final LoanCharge loanCharge) {
|
||||
@@ -900,37 +903,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
.anyMatch(loanChargePaidBy -> loanChargePaidBy.getLoanCharge().equals(loanCharge));
|
||||
}
|
||||
|
||||
public Map<String, Object> updateLoanCharge(final LoanCharge loanCharge, final JsonCommand command) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(3);
|
||||
|
||||
if (getActiveCharges().contains(loanCharge)) {
|
||||
final BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||
final Map<String, Object> loanChargeChanges = loanCharge.update(command, amount);
|
||||
actualChanges.putAll(loanChargeChanges);
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor();
|
||||
if (!loanCharge.isDueAtDisbursement()) {
|
||||
/*
|
||||
* TODO Vishwas Currently we do not allow waiving updating loan charge after a loan is approved (hence there
|
||||
* is no need to adjust any loan transactions).
|
||||
*
|
||||
* Consider removing this block of code or logically completing it for the future by getting the list of
|
||||
* affected Transactions
|
||||
*/
|
||||
reprocessTransactions();
|
||||
} else {
|
||||
// reprocess loan schedule based on charge been waived.
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
}
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public BigDecimal calculateAmountPercentageAppliedTo(final LoanCharge loanCharge) {
|
||||
if (loanCharge.isOverdueInstallmentCharge()) {
|
||||
return loanCharge.getAmountPercentageAppliedTo();
|
||||
@@ -1881,10 +1853,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return getStatus().isClosedWrittenOff();
|
||||
}
|
||||
|
||||
public boolean isClosedWithOutstandingAmountMarkedForReschedule() {
|
||||
return getStatus().isClosedWithOutsandingAmountMarkedForReschedule();
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return isRejected() || isWithdrawn();
|
||||
}
|
||||
@@ -2696,20 +2664,25 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
/*
|
||||
* Probably this is buggy when a chargeback transaction happens
|
||||
*/
|
||||
public void processPostDisbursementTransactions() {
|
||||
public Optional<ChangedTransactionDetail> processPostDisbursementTransactions() {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing();
|
||||
final List<LoanTransaction> copyTransactions = new ArrayList<>();
|
||||
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
||||
// TODO: Probably this is not needed and can be eliminated, make sure to double check it
|
||||
for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
|
||||
copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction));
|
||||
}
|
||||
loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(), copyTransactions, getCurrency(),
|
||||
getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
if (allNonContraTransactionsPostDisbursement.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// TODO: Probably this is not needed and can be eliminated, make sure to double check it
|
||||
for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
|
||||
copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction));
|
||||
}
|
||||
final ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(
|
||||
getDisbursementDate(), copyTransactions, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
return Optional.of(changedTransactionDetail);
|
||||
}
|
||||
|
||||
public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) {
|
||||
@@ -2826,7 +2799,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
Money totalInterest = Money.zero(getCurrency());
|
||||
Money feeCharges = Money.zero(getCurrency());
|
||||
Money penaltyCharges = Money.zero(getCurrency());
|
||||
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
|
||||
List<LoanRepaymentScheduleInstallment> repaymentSchedule = getRepaymentScheduleInstallments();
|
||||
for (final LoanRepaymentScheduleInstallment scheduledRepayment : repaymentSchedule) {
|
||||
totalPrincipal = totalPrincipal.plus(scheduledRepayment.getPrincipalOutstanding(getCurrency()));
|
||||
@@ -3106,11 +3078,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return this.createStandingInstructionAtDisbursement != null && this.createStandingInstructionAtDisbursement;
|
||||
}
|
||||
|
||||
public List<LoanCharge> getLoanChargesByDueDate(LocalDate dueDate) {
|
||||
return getLoanCharges(
|
||||
loanCharge -> loanCharge.getDueLocalDate() != null && DateUtils.isEqual(loanCharge.getDueLocalDate(), dueDate));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param predicate
|
||||
* filter of the charges
|
||||
@@ -3591,6 +3558,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return getLoanProductRelatedDetail().getLoanScheduleType() == PROGRESSIVE;
|
||||
}
|
||||
|
||||
public boolean isCumulativeSchedule() {
|
||||
return getLoanProductRelatedDetail().getLoanScheduleType() == CUMULATIVE;
|
||||
}
|
||||
|
||||
public boolean isChargeOffOnDate(final LocalDate onDate) {
|
||||
final LoanTransaction chargeOffTransaction = findChargedOffTransaction();
|
||||
return (chargeOffTransaction == null) ? false : (chargeOffTransaction.getDateOf().compareTo(onDate) <= 0);
|
||||
@@ -3609,4 +3580,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasChargeOffTransaction() {
|
||||
return getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff);
|
||||
}
|
||||
}
|
||||
|
||||
-6
@@ -74,12 +74,6 @@ public interface LoanAccountDomainService {
|
||||
boolean isRecoveryRepayment, String chargeRefundChargeType, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
|
||||
Boolean isHolidayValidationDone, boolean isLoanToLoanTransfer);
|
||||
|
||||
LoanTransaction saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction);
|
||||
|
||||
Loan saveAndFlushLoanWithDataIntegrityViolationChecks(Loan loan);
|
||||
|
||||
Loan saveLoanWithDataIntegrityViolationChecks(Loan loan);
|
||||
|
||||
LoanTransaction foreCloseLoan(Loan loan, LocalDate foreClourseDate, String noteText, ExternalId externalId,
|
||||
Map<String, Object> changes);
|
||||
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
public interface LoanAccountService {
|
||||
|
||||
LoanTransaction saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction);
|
||||
|
||||
Loan saveAndFlushLoanWithDataIntegrityViolationChecks(Loan loan);
|
||||
|
||||
Loan saveLoanWithDataIntegrityViolationChecks(Loan loan);
|
||||
|
||||
}
|
||||
+1
-2
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
@@ -30,7 +29,7 @@ public interface LoanDownPaymentHandlerService {
|
||||
LoanTransaction handleDownPayment(ScheduleGeneratorDTO scheduleGeneratorDTO, JsonCommand command,
|
||||
LoanTransaction disbursementTransaction, Loan loan);
|
||||
|
||||
ChangedTransactionDetail handleRepaymentOrRecoveryOrWaiverTransaction(Loan loan, LoanTransaction newTransactionDetail,
|
||||
void handleRepaymentOrRecoveryOrWaiverTransaction(Loan loan, LoanTransaction newTransactionDetail,
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine, LoanTransaction transactionForAdjustment,
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
+5
-13
@@ -36,7 +36,6 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
@@ -48,7 +47,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Mon
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanRefundValidator;
|
||||
|
||||
@@ -62,6 +60,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
private final LoanRefundService loanRefundService;
|
||||
private final LoanRefundValidator loanRefundValidator;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
@Override
|
||||
public LoanTransaction handleDownPayment(ScheduleGeneratorDTO scheduleGeneratorDTO, JsonCommand command,
|
||||
@@ -77,11 +76,9 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChangedTransactionDetail handleRepaymentOrRecoveryOrWaiverTransaction(final Loan loan, final LoanTransaction loanTransaction,
|
||||
public void handleRepaymentOrRecoveryOrWaiverTransaction(final Loan loan, final LoanTransaction loanTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
|
||||
if (loanTransaction.isRecoveryRepayment()) {
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, loan);
|
||||
}
|
||||
@@ -155,15 +152,12 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
||||
}
|
||||
}
|
||||
if (reprocess) {
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& !loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
changedTransactionDetail = loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
@@ -185,8 +179,6 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
||||
throw new InvalidLoanStateTransitionException("transaction", "amount.exceeds.threshold", errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
private LoanTransaction handleDownPayment(final Loan loan, final LoanTransaction disbursementTransaction, final JsonCommand command,
|
||||
|
||||
+5
-11
@@ -65,7 +65,7 @@ public class LoanRefundService {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
handleRefundTransaction(loan, loanTransaction, loanLifecycleStateMachine, null);
|
||||
handleRefundTransaction(loan, loanTransaction, loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
public void creditBalanceRefund(final Loan loan, final LoanTransaction newCreditBalanceRefundTransaction,
|
||||
@@ -88,7 +88,7 @@ public class LoanRefundService {
|
||||
}
|
||||
|
||||
private void handleRefundTransaction(final Loan loan, final LoanTransaction loanTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction) {
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REFUND, loan);
|
||||
|
||||
loanTransaction.updateLoan(loan);
|
||||
@@ -104,18 +104,12 @@ public class LoanRefundService {
|
||||
final LocalDate loanTransactionDate = extractTransactionDate(loan, loanTransaction);
|
||||
|
||||
loanRefundValidator.validateTransactionDateNotInFuture(loanTransactionDate);
|
||||
loanRefundValidator.validateTransactionAmountThreshold(loan, adjustedTransaction);
|
||||
loanRefundValidator.validateTransactionAmountThreshold(loan, null);
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor();
|
||||
|
||||
// If it's a refund
|
||||
if (adjustedTransaction == null) {
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction,
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
} else {
|
||||
loan.reprocessTransactions();
|
||||
}
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, new TransactionCtx(loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
loan.doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
|
||||
+7
-8
@@ -24,7 +24,6 @@ import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
@@ -35,6 +34,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul
|
||||
public class LoanScheduleService {
|
||||
|
||||
private final LoanChargeService loanChargeService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
/**
|
||||
* Ability to regenerate the repayment schedule based on the loans current details/state.
|
||||
@@ -59,10 +59,10 @@ public class LoanScheduleService {
|
||||
} else {
|
||||
regenerateRepaymentSchedule(loan, generatorDTO);
|
||||
}
|
||||
loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail recalculateScheduleFromLastTransaction(final Loan loan, final ScheduleGeneratorDTO generatorDTO,
|
||||
public void recalculateScheduleFromLastTransaction(final Loan loan, final ScheduleGeneratorDTO generatorDTO,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
@@ -73,7 +73,8 @@ public class LoanScheduleService {
|
||||
regenerateRepaymentSchedule(loan, generatorDTO);
|
||||
}
|
||||
}
|
||||
return loan.reprocessTransactions();
|
||||
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
|
||||
public void regenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
||||
@@ -106,13 +107,11 @@ public class LoanScheduleService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loan.processPostDisbursementTransactions();
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail handleRegenerateRepaymentScheduleWithInterestRecalculation(final Loan loan,
|
||||
final ScheduleGeneratorDTO generatorDTO) {
|
||||
public void handleRegenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
||||
return loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.time.LocalDate;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
|
||||
public interface ReprocessLoanTransactionsService {
|
||||
|
||||
void reprocessTransactions(Loan loan);
|
||||
|
||||
void reprocessTransactionsWithPostTransactionChecks(Loan loan, LocalDate transactionDate);
|
||||
|
||||
void processPostDisbursementTransactions(Loan loan);
|
||||
|
||||
void removeLoanCharge(Loan loan, LoanCharge loanCharge);
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.time.LocalDate;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.LoanAccountTransfersService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransactionsService {
|
||||
|
||||
private final LoanAccountService loanAccountService;
|
||||
private final LoanAccountTransfersService loanAccountTransfersService;
|
||||
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
||||
|
||||
@Override
|
||||
public void reprocessTransactions(final Loan loan) {
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
if (changedTransactionDetail != null) {
|
||||
handleChangedDetail(changedTransactionDetail);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reprocessTransactionsWithPostTransactionChecks(final Loan loan, final LocalDate transactionDate) {
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactionsWithPostTransactionChecks(transactionDate);
|
||||
if (changedTransactionDetail != null) {
|
||||
handleChangedDetail(changedTransactionDetail);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processPostDisbursementTransactions(final Loan loan) {
|
||||
loan.processPostDisbursementTransactions().ifPresent(this::handleChangedDetail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLoanCharge(final Loan loan, final LoanCharge loanCharge) {
|
||||
loan.removeLoanCharge(loanCharge).ifPresent(this::handleChangedDetail);
|
||||
}
|
||||
|
||||
private void handleChangedDetail(final ChangedTransactionDetail changedTransactionDetail) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
loanAccountTransfersService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-2
@@ -24,9 +24,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.AccountTransfersService;
|
||||
|
||||
public interface AccountTransfersWritePlatformService extends AccountTransfersService {
|
||||
public interface AccountTransfersWritePlatformService {
|
||||
|
||||
CommandProcessingResult create(JsonCommand command);
|
||||
|
||||
|
||||
-10
@@ -502,16 +502,6 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
return accountTransferDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
||||
final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId);
|
||||
if (transferTransaction != null) {
|
||||
transferTransaction.updateToLoanTransaction(newLoanTransaction);
|
||||
this.accountTransferRepository.save(transferTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount();
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.account.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferTransaction;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.LoanAccountTransfersService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class LoanAccountTransfersServiceImpl implements LoanAccountTransfersService {
|
||||
|
||||
private final AccountTransferRepository accountTransferRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
||||
final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId);
|
||||
if (transferTransaction != null) {
|
||||
transferTransaction.updateToLoanTransaction(newLoanTransaction);
|
||||
this.accountTransferRepository.save(transferTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+29
-131
@@ -32,13 +32,10 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
@@ -77,9 +74,7 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferTransaction;
|
||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
@@ -99,7 +94,6 @@ import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanForeclosureValidator;
|
||||
@@ -114,7 +108,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerS
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanRefundService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||
@@ -122,8 +116,6 @@ import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.data.PostDatedChecksStatus;
|
||||
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks;
|
||||
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecksRepository;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -141,7 +133,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||
private final NoteRepository noteRepository;
|
||||
private final AccountTransferRepository accountTransferRepository;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final StandingInstructionRepository standingInstructionRepository;
|
||||
@@ -150,7 +141,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
||||
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
||||
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
|
||||
private final DelinquencyReadPlatformService delinquencyReadPlatformService;
|
||||
@@ -165,6 +155,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
private final LoanDownPaymentHandlerService loanDownPaymentHandlerService;
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanRefundService loanRefundService;
|
||||
private final LoanAccountService loanAccountService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -264,30 +256,16 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanDownPaymentTransactionValidator.validateRepaymentTypeAccountStatus(loan, newRepaymentTransaction, event);
|
||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, event,
|
||||
newRepaymentTransaction.getTransactionDate());
|
||||
final ChangedTransactionDetail changedTransactionDetail = makeRepayment(loan, newRepaymentTransaction,
|
||||
defaultLoanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
makeRepayment(loan, newRepaymentTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newRepaymentTransaction);
|
||||
|
||||
/***
|
||||
* TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping and saving for the
|
||||
* time being, not a major issue for now as this loop is entered only in edge cases (when a payment is made
|
||||
* before the latest payment recorded against the loan)
|
||||
***/
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
updateLoanTransferTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newRepaymentTransaction);
|
||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
final Note note = Note.loanTransactionNote(loan, newRepaymentTransaction, noteText);
|
||||
@@ -395,50 +373,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
return repaymentEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransaction saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) {
|
||||
try {
|
||||
return this.loanTransactionRepository.saveAndFlush(newRepaymentTransaction);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loan saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
try {
|
||||
return this.loanRepositoryWrapper.saveAndFlush(loan);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loan saveLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
try {
|
||||
return this.loanRepositoryWrapper.save(loan);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void raiseValidationExceptionForUniqueConstraintViolation(Exception e) {
|
||||
final Throwable realCause = e.getCause();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
if (realCause.getMessage().toLowerCase().contains("external_id_unique") || realCause.getMessage()
|
||||
.contains("duplicate key value violates unique constraint \"m_loan_transaction_external_id_key\"")) {
|
||||
baseDataValidator.reset().parameter("externalId").failWithCode("value.must.be.unique");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public LoanTransaction makeChargePayment(final Loan loan, final Long chargeId, final LocalDate transactionDate,
|
||||
@@ -487,8 +421,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanChargeService.makeChargePayment(loan, chargeId, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds, newPaymentTransaction, installmentNumber);
|
||||
}
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
||||
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
final Note note = Note.loanTransactionNote(loan, newPaymentTransaction, noteText);
|
||||
@@ -579,7 +513,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanRefundService.makeRefund(loan, newRefundTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newRefundTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newRefundTransaction);
|
||||
this.loanRepositoryWrapper.saveAndFlush(loan);
|
||||
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
@@ -628,8 +562,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
disbursementTransaction.updateLoan(loan);
|
||||
loan.addLoanTransaction(disbursementTransaction);
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(disbursementTransaction);
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(disbursementTransaction);
|
||||
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
final Note note = Note.loanTransactionNote(loan, disbursementTransaction, noteText);
|
||||
@@ -651,7 +585,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction, "reversed");
|
||||
loanTransaction.reverse();
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(loanTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(loanTransaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -686,14 +620,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLoanTransferTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
||||
final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId);
|
||||
if (transferTransaction != null) {
|
||||
transferTransaction.updateToLoanTransaction(newLoanTransaction);
|
||||
this.accountTransferRepository.save(transferTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransaction creditBalanceRefund(final Loan loan, final LocalDate transactionDate, final BigDecimal transactionAmount,
|
||||
final String noteText, final ExternalId externalId, PaymentDetail paymentDetail) {
|
||||
@@ -841,36 +767,21 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanForeclosureValidator.validateForForeclosure(loan, payment.getTransactionDate());
|
||||
}
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_FORECLOSURE);
|
||||
final ChangedTransactionDetail changedTransactionDetail = handleForeClosureTransactions(loan, payment,
|
||||
defaultLoanLifecycleStateMachine, scheduleGeneratorDTO);
|
||||
handleForeClosureTransactions(loan, payment, defaultLoanLifecycleStateMachine, scheduleGeneratorDTO);
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
|
||||
/***
|
||||
* TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping and saving for the
|
||||
* time being, not a major issue for now as this loop is entered only in edge cases (when a payment is made
|
||||
* before the latest payment recorded against the loan)
|
||||
***/
|
||||
|
||||
for (LoanTransaction newTransaction : newTransactions) {
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newTransaction);
|
||||
transactionIds.add(newTransaction.getId());
|
||||
}
|
||||
changes.put("transactions", transactionIds);
|
||||
changes.put("eventAmount", payPrincipal.getAmount().negate());
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
updateLoanTransferTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
changes.put("note", noteText);
|
||||
@@ -978,36 +889,23 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
}
|
||||
} else {
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.getLoanTransactions().add(refundTransaction);
|
||||
if (interestRefundTransaction != null) {
|
||||
loan.addLoanTransaction(interestRefundTransaction);
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
|
||||
// Store and flush newly created transaction to generate PK
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(refundTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(refundTransaction);
|
||||
if (interestRefundTransaction != null) {
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(interestRefundTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(interestRefundTransaction);
|
||||
}
|
||||
/*
|
||||
* TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping and saving for
|
||||
* the time being, not a major issue for now as this loop is entered only in edge cases (when a payment is
|
||||
* made before the latest payment recorded against the loan)
|
||||
*/
|
||||
// Apply reprocessed transactions (if applicable)
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
updateLoanTransferTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
@@ -1076,7 +974,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanRefundRequest.getPrincipal(), loanRefundRequest.getInterest(), loanRefundRequest.getFeeCharges(),
|
||||
loanRefundRequest.getPenaltyCharges(), paymentDetail, transactionDate, externalIdFactory.create());
|
||||
interestRefundTransaction.updateLoan(loan);
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(interestRefundTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(interestRefundTransaction);
|
||||
loan.addLoanTransaction(interestRefundTransaction);
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
|
||||
|
||||
@@ -1154,21 +1052,21 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private ChangedTransactionDetail makeRepayment(final Loan loan, final LoanTransaction repaymentTransaction,
|
||||
private void makeRepayment(final Loan loan, final LoanTransaction repaymentTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loan, repaymentTransaction, "created");
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
return loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction,
|
||||
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, loanLifecycleStateMachine,
|
||||
null, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail handleForeClosureTransactions(final Loan loan, final LoanTransaction repaymentTransaction,
|
||||
private void handleForeClosureTransactions(final Loan loan, final LoanTransaction repaymentTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
loan.setLoanSubStatus(LoanSubStatus.FORECLOSED.getValue());
|
||||
return loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction,
|
||||
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, loanLifecycleStateMachine,
|
||||
null, scheduleGeneratorDTO);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -33,7 +33,7 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
@@ -47,13 +47,13 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@RequiredArgsConstructor
|
||||
public class LoanScheduleWritePlatformServiceImpl implements LoanScheduleWritePlatformService {
|
||||
|
||||
private final LoanAccountDomainService loanAccountDomainService;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanScheduleAssembler loanScheduleAssembler;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
private final LoanAccountService loanAccountService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult addLoanScheduleVariations(final Long loanId, final JsonCommand command) {
|
||||
@@ -64,7 +64,7 @@ public class LoanScheduleWritePlatformServiceImpl implements LoanScheduleWritePl
|
||||
}
|
||||
loanScheduleAssembler.assempleVariableScheduleFrom(loan, command.json());
|
||||
|
||||
loanAccountDomainService.saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
loanAccountService.saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
final Map<String, Object> changes = new HashMap<>();
|
||||
List<LoanTermVariationsData> newVariationsData = new ArrayList<>();
|
||||
List<LoanTermVariations> modifiedVariations = loan.getLoanTermVariations();
|
||||
@@ -102,7 +102,7 @@ public class LoanScheduleWritePlatformServiceImpl implements LoanScheduleWritePl
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loanAccountDomainService.saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
loanAccountService.saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsDeletedBusinessEvent(loan));
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withLoanId(loanId) //
|
||||
|
||||
+4
-23
@@ -47,12 +47,9 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
@@ -62,7 +59,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToT
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
@@ -82,7 +78,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingS
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -98,6 +94,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
|
||||
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
|
||||
|
||||
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
@@ -112,15 +109,12 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
||||
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
|
||||
private final LoanAccountDomainService loanAccountDomainService;
|
||||
private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository;
|
||||
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
||||
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
private final LoanChargeService loanChargeService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
/**
|
||||
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object and persist
|
||||
@@ -440,7 +434,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
}
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loanChargeService.recalculateAllCharges(loan);
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
|
||||
this.loanRepaymentScheduleHistoryRepository.saveAll(loanRepaymentScheduleHistoryList);
|
||||
|
||||
@@ -450,19 +444,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
// update the status of the request
|
||||
loanRescheduleRequest.approve(appUser, approvedOnDate);
|
||||
|
||||
/***
|
||||
* TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping and saving for
|
||||
* the time being, not a major issue for now as this loop is entered only in edge cases (when a adjustment
|
||||
* is made before the latest payment recorded against the loan)
|
||||
***/
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoanAccountServiceImpl implements LoanAccountService {
|
||||
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
|
||||
@Override
|
||||
public LoanTransaction saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) {
|
||||
try {
|
||||
return this.loanTransactionRepository.saveAndFlush(newRepaymentTransaction);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loan saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
try {
|
||||
return this.loanRepositoryWrapper.saveAndFlush(loan);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loan saveLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
try {
|
||||
return this.loanRepositoryWrapper.save(loan);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void raiseValidationExceptionForUniqueConstraintViolation(Exception e) {
|
||||
final Throwable realCause = e.getCause();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
if (realCause.getMessage().toLowerCase().contains("external_id_unique") || realCause.getMessage()
|
||||
.contains("duplicate key value violates unique constraint \"m_loan_transaction_external_id_key\"")) {
|
||||
baseDataValidator.reset().parameter("externalId").failWithCode("value.must.be.unique");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -34,7 +34,7 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPreBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
@@ -54,7 +54,7 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanTransactionAssembler loanTransactionAssembler;
|
||||
private final LoanAccountDomainService loanAccountDomainService;
|
||||
private final LoanAccountService loanAccountService;
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@@ -207,7 +207,7 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi
|
||||
newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
|
||||
newLoanTransaction.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(newLoanTransaction,
|
||||
loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED));
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newLoanTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newLoanTransaction);
|
||||
loan.addLoanTransaction(newLoanTransaction);
|
||||
|
||||
LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
|
||||
@@ -242,7 +242,7 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi
|
||||
|
||||
private LoanTransaction makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanTransaction newAccrualActivityTransaction) {
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionAccrualActivityPreBusinessEvent(loan));
|
||||
newAccrualActivityTransaction = loanAccountDomainService
|
||||
newAccrualActivityTransaction = loanAccountService
|
||||
.saveLoanTransactionWithDataIntegrityViolationChecks(newAccrualActivityTransaction);
|
||||
|
||||
loan.addLoanTransaction(newAccrualActivityTransaction);
|
||||
|
||||
+41
-67
@@ -92,9 +92,9 @@ import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
@@ -165,7 +165,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final AccountTransferDetailRepository accountTransferDetailRepository;
|
||||
private final LoanChargeAssembler loanChargeAssembler;
|
||||
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
||||
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
|
||||
private final NoteRepository noteRepository;
|
||||
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
||||
@@ -173,6 +172,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
private final LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator;
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanAccountService loanAccountService;
|
||||
|
||||
private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) {
|
||||
return DateUtils.isAfter(loanCharge.getDueDate(), e.getFromDate()) && !DateUtils.isAfter(loanCharge.getDueDate(), e.getDueDate());
|
||||
@@ -287,25 +288,18 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
}
|
||||
|
||||
if (reprocessRequired) {
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = overpaidReprocess
|
||||
? loan.reprocessTransactionsWithPostTransactionChecks(transactionDate)
|
||||
: loan.reprocessTransactions();
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
if (overpaidReprocess) {
|
||||
reprocessLoanTransactionsService.reprocessTransactionsWithPostTransactionChecks(loan, transactionDate);
|
||||
} else {
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
}
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && isAppliedOnBackDate
|
||||
@@ -469,7 +463,23 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanUpdateChargeBusinessEvent(loanCharge));
|
||||
|
||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||
final Map<String, Object> changes = loan.updateLoanCharge(loanCharge, command);
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<>(3);
|
||||
if (loan.getActiveCharges().contains(loanCharge)) {
|
||||
final BigDecimal amount = loan.calculateAmountPercentageAppliedTo(loanCharge);
|
||||
final Map<String, Object> loanChargeChanges = loanCharge.update(command, amount);
|
||||
changes.putAll(loanChargeChanges);
|
||||
loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
if (!loanCharge.isDueAtDisbursement()) {
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
} else {
|
||||
// reprocess loan schedule based on charge been waived.
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(loan.getCurrency(), loan.getDisbursementDate(), loan.getRepaymentScheduleInstallments(),
|
||||
loan.getActiveCharges());
|
||||
}
|
||||
|
||||
this.loanRepositoryWrapper.save(loan);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanUpdateChargeBusinessEvent(loanCharge));
|
||||
@@ -606,7 +616,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, loanCharge);
|
||||
loan.removeLoanCharge(loanCharge);
|
||||
reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge);
|
||||
this.loanRepositoryWrapper.save(loan);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDeleteChargeBusinessEvent(loanCharge));
|
||||
return new CommandProcessingResultBuilder() //
|
||||
@@ -820,23 +830,12 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
if (reprocessRequired) {
|
||||
addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate);
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings()
|
||||
.entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
}
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && runInterestRecalculation
|
||||
@@ -866,22 +865,20 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
} else {
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction,
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
}
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(loanChargeAdjustmentTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(loanChargeAdjustmentTransaction);
|
||||
loan.updateLoanSummaryAndStatus();
|
||||
|
||||
loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(loan.getCurrency().getCode(),
|
||||
existingTransactionIds, existingReversedTransactionIds, false);
|
||||
@@ -1176,20 +1173,10 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isChargedOff()) {
|
||||
final List<Long> existingTransactionIds = loan.findExistingTransactionIds();
|
||||
ScheduleGeneratorDTO generatorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
ChangedTransactionDetail changedTransactionDetail = loanScheduleService
|
||||
.handleRegenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
||||
loanScheduleService.handleRegenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
}
|
||||
return loan;
|
||||
@@ -1456,29 +1443,16 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
waiveLoanChargeTransaction.getAmount(loan.getCurrency()).getAmount(), loanInstallmentNumber);
|
||||
waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy);
|
||||
loan.addLoanTransaction(waiveLoanChargeTransaction);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
// Waive of charges whose due date falls after latest 'repayment' transaction don't require entire loan schedule
|
||||
// to be reprocessed.
|
||||
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loan.getCurrency())) {
|
||||
/*
|
||||
* TODO Vishwas Currently we do not allow waiving fully paid loan charge and waiving partially paid loan
|
||||
* charges only waives the remaining amount.
|
||||
*
|
||||
* Consider removing this block of code or logically completing it for the future by getting the list of
|
||||
* affected Transactions
|
||||
*/
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
} else {
|
||||
// reprocess loan schedule based on charge been waived.
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
|
||||
+4
-3
@@ -58,6 +58,7 @@ public class LoanDisbursementService {
|
||||
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanDisbursementValidator loanDisbursementValidator;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
public void updateDisbursementDetails(final Loan loan, final JsonCommand jsonCommand, final Map<String, Object> actualChanges) {
|
||||
final List<Long> disbursementList = loan.fetchDisbursementIds();
|
||||
@@ -262,7 +263,7 @@ public class LoanDisbursementService {
|
||||
for (LoanCharge loanCharge : tempCharges) {
|
||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, loanCharge);
|
||||
loan.removeLoanCharge(loanCharge);
|
||||
reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge);
|
||||
}
|
||||
loan.getTrancheCharges().clear();
|
||||
} else {
|
||||
@@ -272,7 +273,7 @@ public class LoanDisbursementService {
|
||||
if (loan.getCharges().contains(deleteCharge)) {
|
||||
loanChargeValidator.validateLoanIsNotClosed(loan, deleteCharge);
|
||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, deleteCharge);
|
||||
loan.removeLoanCharge(deleteCharge);
|
||||
reprocessLoanTransactionsService.removeLoanCharge(loan, deleteCharge);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,6 +296,6 @@ public class LoanDisbursementService {
|
||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, charge); //
|
||||
return true; //
|
||||
}) //
|
||||
.forEach(loan::removeLoanCharge);
|
||||
.forEach(loanCharge -> reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge));
|
||||
}
|
||||
}
|
||||
|
||||
+132
-295
@@ -123,9 +123,7 @@ import org.apache.fineract.portfolio.account.domain.AccountAssociationsRepositor
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferTransaction;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority;
|
||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
||||
@@ -153,11 +151,11 @@ import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
||||
@@ -267,11 +265,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
private final LoanAccountLockService loanAccountLockService;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
||||
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
||||
private final ErrorHandler errorHandler;
|
||||
private final LoanDownPaymentHandlerService loanDownPaymentHandlerService;
|
||||
private final AccountTransferRepository accountTransferRepository;
|
||||
private final LoanTransactionAssembler loanTransactionAssembler;
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
private final LoanOfficerValidator loanOfficerValidator;
|
||||
@@ -280,6 +276,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanOfficerService loanOfficerService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanAccountService loanAccountService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -429,8 +427,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() || downPaymentEnabled) {
|
||||
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = disburseLoan(command, isPaymentTypeApplicableForDisbursementCharge,
|
||||
paymentDetail, loan, currentUser, changes, scheduleGeneratorDTO);
|
||||
disburseLoan(command, isPaymentTypeApplicableForDisbursementCharge, paymentDetail, loan, currentUser, changes,
|
||||
scheduleGeneratorDTO);
|
||||
loan.adjustNetDisbursalAmount(amountToDisburse.getAmount());
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
@@ -444,14 +442,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (disbursementTransaction != null) {
|
||||
loanTransactionRepository.saveAndFlush(disbursementTransaction);
|
||||
}
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
|
||||
if (loan.isAutoRepaymentForDownPaymentEnabled() && !isWithoutAutoPayment) {
|
||||
// updating linked savings account for auto down payment transaction for disbursement to savings account
|
||||
if (isAccountTransfer && loan.shouldCreateStandingInstructionAtDisbursement()) {
|
||||
@@ -569,9 +560,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail disburseLoan(JsonCommand command, boolean isPaymentTypeApplicableForDisbursementCharge,
|
||||
PaymentDetail paymentDetail, Loan loan, AppUser currentUser, Map<String, Object> changes,
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
private void disburseLoan(JsonCommand command, boolean isPaymentTypeApplicableForDisbursementCharge, PaymentDetail paymentDetail,
|
||||
Loan loan, AppUser currentUser, Map<String, Object> changes, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final PaymentDetail paymentDetail1 = isPaymentTypeApplicableForDisbursementCharge ? paymentDetail : null;
|
||||
final LocalDate actualDisbursementDate1 = command.localDateValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE);
|
||||
|
||||
@@ -584,9 +574,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
boolean disbursementMissedParam = loan.isDisbursementMissed();
|
||||
LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate();
|
||||
if ((loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
if ((loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
|
||||
@@ -612,18 +604,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.addLoanTransaction(interestAppliedTransaction);
|
||||
}
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
if (loan.getLoanProduct().isMultiDisburseLoan()) {
|
||||
if (loan.getLoanProduct().isMultiDisburseLoan() || loan.isProgressiveSchedule()) {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = loan.retrieveListOfTransactionsForReprocessing();
|
||||
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
||||
changedTransactionDetail = loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
}
|
||||
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, loan);
|
||||
changes.put(PARAM_STATUS, LoanEnumerations.status(loan.getLoanStatus()));
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
private void updatePostDatedChecks(Set<PostDatedChecks> postDatedChecks) {
|
||||
@@ -784,7 +774,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_DISBURSED);
|
||||
updateLoanCounters(loan, actualDisbursementDate);
|
||||
boolean canDisburse = loan.canDisburse();
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
if (canDisburse) {
|
||||
Money amountBeforeAdjust = loan.getPrincipal();
|
||||
Money disburseAmount = loanDisbursementService.adjustDisburseAmount(loan, command, actualDisbursementDate);
|
||||
@@ -813,8 +802,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() || downPaymentEnabled) {
|
||||
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
changedTransactionDetail = disburseLoan(command, configurationDomainService.isPaymentTypeApplicableForDisbursementCharge(),
|
||||
paymentDetail, loan, currentUser, changes, scheduleGeneratorDTO);
|
||||
disburseLoan(command, configurationDomainService.isPaymentTypeApplicableForDisbursementCharge(), paymentDetail, loan,
|
||||
currentUser, changes, scheduleGeneratorDTO);
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
|
||||
@@ -825,17 +814,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
if (!changes.isEmpty()) {
|
||||
|
||||
createNote(loan, command, changes);
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings()
|
||||
.entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
@@ -1049,14 +1028,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
return statusChanged;
|
||||
}
|
||||
|
||||
private void updateLoanTransferTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
||||
final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId);
|
||||
if (transferTransaction != null) {
|
||||
transferTransaction.updateToLoanTransaction(newLoanTransaction);
|
||||
this.accountTransferRepository.save(transferTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO check
|
||||
public boolean isChronologicallyTheLatestTransaction(final LoanTransaction loanTransaction,
|
||||
final List<LoanTransaction> loanTransactions) {
|
||||
@@ -1077,8 +1048,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
return isChronologicallyLatestRepaymentOrWaiver;
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail recalculateLoanWithInterestPaymentWaiverTxn(Loan loan,
|
||||
LoanTransaction newInterestPaymentWaiverTransaction) {
|
||||
private void recalculateLoanWithInterestPaymentWaiverTxn(Loan loan, LoanTransaction newInterestPaymentWaiverTransaction) {
|
||||
LocalDate recalculateFrom = null;
|
||||
LocalDate transactionDate = newInterestPaymentWaiverTransaction.getTransactionDate();
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
@@ -1086,7 +1056,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom, null);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
newInterestPaymentWaiverTransaction.updateLoan(loan);
|
||||
|
||||
final boolean isTransactionChronologicallyLatest = loan
|
||||
@@ -1126,37 +1095,27 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
if (reprocess) {
|
||||
changedTransactionDetail = reprocessChangedLoanTransactions(loan, loanRepaymentScheduleTransactionProcessor,
|
||||
scheduleGeneratorDTO);
|
||||
reprocessChangedLoanTransactions(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
|
||||
doPostLoanTransactionChecks(loan, newInterestPaymentWaiverTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail reprocessChangedLoanTransactions(Loan loan,
|
||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
private void reprocessChangedLoanTransactions(Loan loan, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
|
||||
} else if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -1179,14 +1138,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
LoanTransaction newInterestPaymentWaiverTransaction = loanTransactionAssembler.assembleTransactionAndCalculateChanges(loan, command,
|
||||
changes);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = recalculateLoanWithInterestPaymentWaiverTxn(loan,
|
||||
newInterestPaymentWaiverTransaction);
|
||||
recalculateLoanWithInterestPaymentWaiverTxn(loan, newInterestPaymentWaiverTransaction);
|
||||
|
||||
loanTransactionValidator.validateLoanTransactionInterestPaymentWaiverAfterRecalculation(loan);
|
||||
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newInterestPaymentWaiverTransaction);
|
||||
|
||||
saveChangedTransactionDetail(changedTransactionDetail);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newInterestPaymentWaiverTransaction);
|
||||
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
@@ -1230,17 +1186,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
private void saveChangedTransactionDetail(ChangedTransactionDetail changedTransactionDetail) {
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
updateLoanTransferTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(final LoanTransactionType repaymentTransactionType,
|
||||
@@ -1451,9 +1396,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(),
|
||||
holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = adjustExistingTransaction(loan, newTransactionDetail,
|
||||
loanLifecycleStateMachine, transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
||||
scheduleGeneratorDTO, reversalTxnExternalId);
|
||||
adjustExistingTransaction(loan, newTransactionDetail, loanLifecycleStateMachine, transactionToAdjust, existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO, reversalTxnExternalId);
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
@@ -1468,19 +1412,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.loanTransactionRepository.saveAndFlush(newTransactionDetail);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping and saving for the
|
||||
* time being, not a major issue for now as this loop is entered only in edge cases (when a adjustment is made
|
||||
* before the latest payment recorded against the loan)
|
||||
*/
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
final String noteText = command.stringValueOfParameterNamed("note");
|
||||
@@ -1545,13 +1476,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.with(changes).build();
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail adjustExistingTransaction(final Loan loan, final LoanTransaction newTransactionDetail,
|
||||
public void adjustExistingTransaction(final Loan loan, final LoanTransaction newTransactionDetail,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final ExternalId reversalExternalId) {
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
@@ -1598,11 +1526,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.updateLoanSummaryAndStatus();
|
||||
|
||||
if (newTransactionDetail.isRepaymentLikeType() || newTransactionDetail.isInterestWaiver()) {
|
||||
changedTransactionDetail = loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan,
|
||||
newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment, scheduleGeneratorDTO);
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, newTransactionDetail,
|
||||
loanLifecycleStateMachine, transactionForAdjustment, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -1763,8 +1689,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
||||
waiveInterestTransaction.getTransactionDate());
|
||||
final ChangedTransactionDetail changedTransactionDetail = waiveInterest(loan, waiveInterestTransaction, loanLifecycleStateMachine,
|
||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
waiveInterest(loan, waiveInterestTransaction, loanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds,
|
||||
scheduleGeneratorDTO);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
@@ -1772,21 +1698,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(waiveInterestTransaction);
|
||||
|
||||
/***
|
||||
* TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping and saving for the
|
||||
* time being, not a major issue for now as this loop is entered only in edge cases (when a waiver is made
|
||||
* before the latest payment recorded against the loan)
|
||||
***/
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
final String noteText = command.stringValueOfParameterNamed("note");
|
||||
@@ -1874,45 +1785,41 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.withLoanId(loanId) //
|
||||
.with(changes);
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = closeAsWrittenOff(loan, command, loanLifecycleStateMachine, changes,
|
||||
final Optional<LoanTransaction> loanTransactionOptional = closeAsWrittenOff(loan, command, loanLifecycleStateMachine, changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
if (loanTransactionOptional.isPresent()) {
|
||||
final LoanTransaction loanTransaction = loanTransactionOptional.get();
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
LoanTransaction writeOff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
||||
this.loanTransactionRepository.saveAndFlush(writeOff);
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
this.loanTransactionRepository.saveAndFlush(loanTransaction);
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
final String noteText = command.stringValueOfParameterNamed("note");
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
changes.put("note", noteText);
|
||||
final Note note = Note.loanTransactionNote(loan, writeOff, noteText);
|
||||
final Note note = Note.loanTransactionNote(loan, loanTransaction, noteText);
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
|
||||
loan.isInterestBearingAndInterestRecalculationEnabled(), false);
|
||||
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(writeOff));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(loanTransaction));
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
|
||||
builder.withEntityId(writeOff.getId()).withEntityExternalId(writeOff.getExternalId());
|
||||
builder.withEntityId(loanTransaction.getId()).withEntityExternalId(loanTransaction.getExternalId());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult closeLoan(final Long loanId, final JsonCommand command) {
|
||||
|
||||
this.loanTransactionValidator.validateTransactionWithNoAmount(command.json());
|
||||
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
@@ -1946,22 +1853,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final LocalDate closureDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE);
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CLOSED);
|
||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.REPAID_IN_FULL, closureDate);
|
||||
ChangedTransactionDetail changedTransactionDetail = close(loan, command, loanLifecycleStateMachine, changes, existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
final Optional<LoanTransaction> loanTransactionOptional = close(loan, command, loanLifecycleStateMachine, changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
|
||||
final LoanTransaction possibleClosingTransaction = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
||||
if (possibleClosingTransaction != null) {
|
||||
this.loanTransactionRepository.saveAndFlush(possibleClosingTransaction);
|
||||
}
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
loanTransactionOptional.ifPresent(this.loanTransactionRepository::saveAndFlush);
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
final String noteText = command.stringValueOfParameterNamed("note");
|
||||
@@ -1989,12 +1889,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
|
||||
CommandProcessingResult result;
|
||||
if (possibleClosingTransaction != null) {
|
||||
|
||||
if (loanTransactionOptional.isPresent()) {
|
||||
result = new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(possibleClosingTransaction.getId()) //
|
||||
.withEntityExternalId(possibleClosingTransaction.getExternalId()) //
|
||||
.withEntityId(loanTransactionOptional.get().getId()) //
|
||||
.withEntityExternalId(loanTransactionOptional.get().getExternalId()) //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
.withClientId(loan.getClientId()) //
|
||||
.withGroupId(loan.getGroupId()) //
|
||||
@@ -2589,20 +2488,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
|
||||
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
|
||||
ChangedTransactionDetail changedTransactionDetail = undoWrittenOff(loan, loanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
undoWrittenOff(loan, loanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
@@ -2730,20 +2620,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
LocalDate recalculateFrom = null;
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
|
||||
if (command.entityId() != null) {
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE);
|
||||
changedTransactionDetail = updateDisbursementDateAndAmountForTranche(loan, loanDisbursementDetails, command, changes,
|
||||
scheduleGeneratorDTO);
|
||||
updateDisbursementDateAndAmountForTranche(loan, loanDisbursementDetails, command, changes, scheduleGeneratorDTO);
|
||||
} else {
|
||||
loan.repaymentScheduleDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else {
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
loan.processPostDisbursementTransactions();
|
||||
reprocessLoanTransactionsService.processPostDisbursementTransactions(loan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2752,14 +2639,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
|
||||
if (command.entityId() != null && changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
createLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||
@@ -2817,22 +2696,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
ScheduleGeneratorDTO generatorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = loanScheduleService.recalculateScheduleFromLastTransaction(loan, generatorDTO,
|
||||
existingTransactionIds, existingReversedTransactionIds);
|
||||
loanScheduleService.recalculateScheduleFromLastTransaction(loan, generatorDTO, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
}
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
false);
|
||||
@@ -2909,9 +2780,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
if (loan.isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged
|
||||
|| rescheduledRepaymentDate != null) {
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else {
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
}
|
||||
@@ -3168,7 +3039,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult chargeOff(JsonCommand command) {
|
||||
|
||||
loanTransactionValidator.validateChargeOffTransaction(command.json());
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||
@@ -3225,18 +3095,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
loan.addLoanTransaction(chargeOffTransaction);
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
// Trigger transaction replayed event
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
if (loan.isCumulativeSchedule()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.addLoanTransaction(chargeOffTransaction);
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
} else {
|
||||
loan.addLoanTransaction(chargeOffTransaction);
|
||||
loan.getTransactionProcessor().processLatestTransaction(chargeOffTransaction,
|
||||
@@ -3307,30 +3171,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
loan.liftChargeOff();
|
||||
loanTransactionRepository.saveAndFlush(chargedOffTransaction);
|
||||
if (loan.isProgressiveSchedule()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
|
||||
}
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoChargeOffBusinessEvent(chargedOffTransaction));
|
||||
|
||||
loan.reprocessTransactions();
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
.withClientId(loan.getClientId()) //
|
||||
@@ -3577,12 +3431,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.getLoanTransactions().retainAll(retainTransactions);
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail closeAsWrittenOff(final Loan loan, final JsonCommand command,
|
||||
private Optional<LoanTransaction> closeAsWrittenOff(final Loan loan, final JsonCommand command,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final AppUser currentUser,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor();
|
||||
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loan, scheduleGeneratorDTO);
|
||||
closeDisbursements(loan, scheduleGeneratorDTO);
|
||||
|
||||
final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE);
|
||||
loan.setClosedOnDate(writtenOffOnLocalDate);
|
||||
@@ -3590,82 +3444,74 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.setClosedBy(currentUser);
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.WRITE_OFF_OUTSTANDING, loan);
|
||||
|
||||
LoanTransaction loanTransaction = null;
|
||||
if (!statusEnum.hasStateOf(loan.getStatus())) {
|
||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, loan);
|
||||
changes.put(PARAM_STATUS, LoanEnumerations.status(loan.getLoanStatus()));
|
||||
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull(EXTERNAL_ID);
|
||||
|
||||
ExternalId externalId = ExternalIdFactory.produce(txnExternalId);
|
||||
|
||||
if (externalId.isEmpty() && TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
|
||||
changes.put(CLOSED_ON_DATE, command.stringValueOfParameterNamed(TRANSACTION_DATE));
|
||||
changes.put(WRITTEN_OFF_ON_DATE, command.stringValueOfParameterNamed(TRANSACTION_DATE));
|
||||
changes.put("externalId", externalId);
|
||||
|
||||
if (DateUtils.isBefore(writtenOffOnLocalDate, loan.getDisbursementDate())) {
|
||||
final String errorMessage = "The date on which a loan is written off cannot be before the loan disbursement date: "
|
||||
+ loan.getDisbursementDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.before.submittal.date", errorMessage,
|
||||
writtenOffOnLocalDate, loan.getDisbursementDate());
|
||||
}
|
||||
|
||||
if (DateUtils.isDateInTheFuture(writtenOffOnLocalDate)) {
|
||||
final String errorMessage = "The date on which a loan is written off cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, writtenOffOnLocalDate);
|
||||
}
|
||||
|
||||
loanTransaction = LoanTransaction.writeoff(loan, loan.getOffice(), writtenOffOnLocalDate, externalId);
|
||||
LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
|
||||
if (DateUtils.isAfter(lastTransactionDate, writtenOffOnLocalDate)) {
|
||||
final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions.";
|
||||
throw new InvalidLoanStateTransitionException("writeoff", "must.occur.on.or.after.other.transaction.dates", errorMessage,
|
||||
writtenOffOnLocalDate);
|
||||
}
|
||||
|
||||
loan.addLoanTransaction(loanTransaction);
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction,
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
if (statusEnum.hasStateOf(loan.getStatus())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
if (changedTransactionDetail == null) {
|
||||
changedTransactionDetail = new ChangedTransactionDetail();
|
||||
|
||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, loan);
|
||||
changes.put(PARAM_STATUS, LoanEnumerations.status(loan.getLoanStatus()));
|
||||
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull(EXTERNAL_ID);
|
||||
|
||||
ExternalId externalId = ExternalIdFactory.produce(txnExternalId);
|
||||
|
||||
if (externalId.isEmpty() && TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction);
|
||||
return changedTransactionDetail;
|
||||
|
||||
changes.put(CLOSED_ON_DATE, command.stringValueOfParameterNamed(TRANSACTION_DATE));
|
||||
changes.put(WRITTEN_OFF_ON_DATE, command.stringValueOfParameterNamed(TRANSACTION_DATE));
|
||||
changes.put("externalId", externalId);
|
||||
|
||||
if (DateUtils.isBefore(writtenOffOnLocalDate, loan.getDisbursementDate())) {
|
||||
final String errorMessage = "The date on which a loan is written off cannot be before the loan disbursement date: "
|
||||
+ loan.getDisbursementDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.before.submittal.date", errorMessage,
|
||||
writtenOffOnLocalDate, loan.getDisbursementDate());
|
||||
}
|
||||
|
||||
if (DateUtils.isDateInTheFuture(writtenOffOnLocalDate)) {
|
||||
final String errorMessage = "The date on which a loan is written off cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, writtenOffOnLocalDate);
|
||||
}
|
||||
|
||||
final LoanTransaction loanTransaction = LoanTransaction.writeoff(loan, loan.getOffice(), writtenOffOnLocalDate, externalId);
|
||||
LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
|
||||
if (DateUtils.isAfter(lastTransactionDate, writtenOffOnLocalDate)) {
|
||||
final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions.";
|
||||
throw new InvalidLoanStateTransitionException("writeoff", "must.occur.on.or.after.other.transaction.dates", errorMessage,
|
||||
writtenOffOnLocalDate);
|
||||
}
|
||||
|
||||
loan.addLoanTransaction(loanTransaction);
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, new TransactionCtx(loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
|
||||
return Optional.of(loanTransaction);
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail closeDisbursements(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
private void closeDisbursements(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
if (loan.isDisbursementAllowed() && loan.atLeastOnceDisbursed()) {
|
||||
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getDisbursedAmount());
|
||||
loan.removeDisbursementDetail();
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
changedTransactionDetail = loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
LocalDate lastLoanTransactionDate = loan.getLatestTransactionDate();
|
||||
loan.doPostLoanTransactionChecks(lastLoanTransactionDate, loanLifecycleStateMachine);
|
||||
}
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail close(final Loan loan, final JsonCommand command,
|
||||
private Optional<LoanTransaction> close(final Loan loan, final JsonCommand command,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
@@ -3695,7 +3541,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
throw new InvalidLoanStateTransitionException("close", "cannot.be.a.future.date", errorMessage, closureDate);
|
||||
}
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor();
|
||||
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loan, scheduleGeneratorDTO);
|
||||
closeDisbursements(loan, scheduleGeneratorDTO);
|
||||
|
||||
LoanTransaction loanTransaction = null;
|
||||
if (loan.isOpen()) {
|
||||
@@ -3747,16 +3593,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
if (changedTransactionDetail == null) {
|
||||
changedTransactionDetail = new ChangedTransactionDetail();
|
||||
}
|
||||
changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction);
|
||||
return changedTransactionDetail;
|
||||
return Optional.ofNullable(loanTransaction);
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail updateDisbursementDateAndAmountForTranche(final Loan loan,
|
||||
final LoanDisbursementDetails disbursementDetails, final JsonCommand command, final Map<String, Object> actualChanges,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
private void updateDisbursementDateAndAmountForTranche(final Loan loan, final LoanDisbursementDetails disbursementDetails,
|
||||
final JsonCommand command, final Map<String, Object> actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final Locale locale = command.extractLocale();
|
||||
final BigDecimal principal = command.bigDecimalValueOfParameterNamed(LoanApiConstants.updatedDisbursementPrincipalParameterName,
|
||||
locale);
|
||||
@@ -3772,15 +3613,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && !(loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff))) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else {
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
return loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
|
||||
private Map<String, Object> undoLastDisbursal(final ScheduleGeneratorDTO scheduleGeneratorDTO, final List<Long> existingTransactionIds,
|
||||
@@ -3826,17 +3665,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail waiveInterest(final Loan loan, final LoanTransaction waiveInterestTransaction,
|
||||
private void waiveInterest(final Loan loan, final LoanTransaction waiveInterestTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
return loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, waiveInterestTransaction,
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, waiveInterestTransaction,
|
||||
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail undoWrittenOff(final Loan loan, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
private void undoWrittenOff(final Loan loan, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
@@ -3845,13 +3684,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
"reversed");
|
||||
writeOffTransaction.reverse();
|
||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, loan);
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
return loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-4
@@ -41,7 +41,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.starter.AdvancedPaymentScheduleTransactionProcessorCondition;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||
@@ -85,9 +84,7 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
||||
List<LoanRepaymentScheduleInstallment> installmentsToReprocess = new ArrayList<>(
|
||||
loan.getRepaymentScheduleInstallments().stream().filter(i -> !i.isReAged() && !i.isAdditional()).toList());
|
||||
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
+4
-7
@@ -47,16 +47,15 @@ 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.domain.reaging.LoanReAgeParameter;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgingParameterRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -72,12 +71,12 @@ public class LoanReAgingServiceImpl {
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final LoanReAgingParameterRepository reAgingParameterRepository;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final NoteRepository noteRepository;
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
public CommandProcessingResult reAge(Long loanId, JsonCommand command) {
|
||||
Loan loan = loanAssembler.assembleFrom(loanId);
|
||||
@@ -129,13 +128,11 @@ public class LoanReAgingServiceImpl {
|
||||
}
|
||||
reverseReAgeTransaction(reAgeTransaction, command);
|
||||
loanTransactionRepository.saveAndFlush(reAgeTransaction);
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
loan.updateLoanScheduleDependentDerivedFields();
|
||||
persistNote(loan, command, changes);
|
||||
|
||||
|
||||
+3
-1
@@ -49,6 +49,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Mon
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -64,6 +65,7 @@ public class LoanReAmortizationServiceImpl {
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
public CommandProcessingResult reAmortize(Long loanId, JsonCommand command) {
|
||||
Loan loan = loanAssembler.assembleFrom(loanId);
|
||||
@@ -132,7 +134,7 @@ public class LoanReAmortizationServiceImpl {
|
||||
"reversed");
|
||||
reAmortizeTransaction.reverse(reversalExternalId);
|
||||
reAmortizeTransaction.manuallyAdjustedOrReversed();
|
||||
reAmortizeTransaction.getLoan().reprocessTransactions();
|
||||
reprocessLoanTransactionsService.reprocessTransactions(reAmortizeTransaction.getLoan());
|
||||
}
|
||||
|
||||
private LoanTransaction findLatestNonReversedReAmortizeTransaction(Loan loan) {
|
||||
|
||||
+42
-32
@@ -42,7 +42,6 @@ import org.apache.fineract.organisation.teller.data.CashierTransactionDataValida
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountAssociationsRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
|
||||
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
@@ -64,13 +63,13 @@ import org.apache.fineract.portfolio.fund.domain.FundRepository;
|
||||
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
|
||||
import org.apache.fineract.portfolio.group.domain.GroupRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.AccountTransfersService;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseReadPlatformService;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePlatformService;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePlatformServiceImpl;
|
||||
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.LoanChargeRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
@@ -104,6 +103,7 @@ import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlat
|
||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccountServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualActivityProcessingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService;
|
||||
@@ -141,6 +141,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServic
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServiceJpaRepositoryImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
||||
@@ -192,13 +193,12 @@ public class LoanAccountConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanApplicationWritePlatformService.class)
|
||||
public LoanApplicationWritePlatformService loanApplicationWritePlatformService(PlatformSecurityContext context,
|
||||
FromJsonHelper fromJsonHelper, LoanApplicationTransitionValidator loanApplicationTransitionValidator,
|
||||
LoanApplicationValidator loanApplicationValidator, LoanRepositoryWrapper loanRepositoryWrapper, NoteRepository noteRepository,
|
||||
LoanAssembler loanAssembler, LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
LoanApplicationTransitionValidator loanApplicationTransitionValidator, LoanApplicationValidator loanApplicationValidator,
|
||||
LoanRepositoryWrapper loanRepositoryWrapper, NoteRepository noteRepository, LoanAssembler loanAssembler,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
CalendarRepository calendarRepository, CalendarInstanceRepository calendarInstanceRepository,
|
||||
SavingsAccountRepositoryWrapper savingsAccountRepository, AccountAssociationsRepository accountAssociationsRepository,
|
||||
LoanReadPlatformService loanReadPlatformService, BusinessEventNotifierService businessEventNotifierService,
|
||||
ConfigurationDomainService configurationDomainService, LoanScheduleAssembler loanScheduleAssembler,
|
||||
BusinessEventNotifierService businessEventNotifierService, LoanScheduleAssembler loanScheduleAssembler,
|
||||
LoanUtilService loanUtilService, CalendarReadPlatformService calendarReadPlatformService,
|
||||
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, GLIMAccountInfoRepository glimRepository,
|
||||
LoanRepository loanRepository, GSIMReadPlatformService gsimReadPlatformService,
|
||||
@@ -291,20 +291,20 @@ public class LoanAccountConfiguration {
|
||||
ConfigurationDomainService configurationDomainService,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
ExternalIdFactory externalIdFactory, AccountTransferDetailRepository accountTransferDetailRepository,
|
||||
LoanChargeAssembler loanChargeAssembler, ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService,
|
||||
PaymentDetailWritePlatformService paymentDetailWritePlatformService, NoteRepository noteRepository,
|
||||
LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService,
|
||||
LoanChargeAssembler loanChargeAssembler, PaymentDetailWritePlatformService paymentDetailWritePlatformService,
|
||||
NoteRepository noteRepository, LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService,
|
||||
LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanChargeValidator loanChargeValidator,
|
||||
LoanScheduleService loanScheduleService) {
|
||||
LoanScheduleService loanScheduleService, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||
LoanAccountService loanAccountService) {
|
||||
return new LoanChargeWritePlatformServiceImpl(loanChargeApiJsonValidator, loanAssembler, chargeRepository,
|
||||
businessEventNotifierService, loanTransactionRepository, accountTransfersWritePlatformService, loanRepositoryWrapper,
|
||||
journalEntryWritePlatformService, loanAccountDomainService, loanChargeRepository, loanWritePlatformService, loanUtilService,
|
||||
loanChargeReadPlatformService, defaultLoanLifecycleStateMachine, accountAssociationsReadPlatformService, fromApiJsonHelper,
|
||||
configurationDomainService, loanRepaymentScheduleTransactionProcessorFactory, externalIdFactory,
|
||||
accountTransferDetailRepository, loanChargeAssembler, replayedTransactionBusinessEventService,
|
||||
paymentDetailWritePlatformService, noteRepository, loanAccrualTransactionBusinessEventService,
|
||||
loanAccrualsProcessingService, loanDownPaymentTransactionValidator, loanChargeValidator, loanScheduleService);
|
||||
accountTransferDetailRepository, loanChargeAssembler, paymentDetailWritePlatformService, noteRepository,
|
||||
loanAccrualTransactionBusinessEventService, loanAccrualsProcessingService, loanDownPaymentTransactionValidator,
|
||||
loanChargeValidator, loanScheduleService, reprocessLoanTransactionsService, loanAccountService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -386,13 +386,13 @@ public class LoanAccountConfiguration {
|
||||
PostDatedChecksRepository postDatedChecksRepository,
|
||||
LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, LoanAccountLockService loanAccountLockService,
|
||||
ExternalIdFactory externalIdFactory, ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService,
|
||||
LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService, ErrorHandler errorHandler,
|
||||
LoanDownPaymentHandlerService loanDownPaymentHandlerService, AccountTransferRepository accountTransferRepository,
|
||||
ExternalIdFactory externalIdFactory, LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService,
|
||||
ErrorHandler errorHandler, LoanDownPaymentHandlerService loanDownPaymentHandlerService,
|
||||
LoanTransactionAssembler loanTransactionAssembler, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||
LoanOfficerValidator loanOfficerValidator, LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator,
|
||||
LoanDisbursementService loanDisbursementService, LoanScheduleService loanScheduleService,
|
||||
LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService) {
|
||||
LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanAccountService loanAccountService) {
|
||||
return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer,
|
||||
loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository,
|
||||
loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService, calendarInstanceRepository,
|
||||
@@ -403,10 +403,10 @@ public class LoanAccountConfiguration {
|
||||
guarantorDomainService, loanUtilService, entityDatatableChecksWritePlatformService, transactionProcessingStrategy,
|
||||
codeValueRepository, cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
|
||||
postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
|
||||
loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
|
||||
loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, accountTransferRepository,
|
||||
loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator,
|
||||
loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService);
|
||||
loanAccountLockService, externalIdFactory, loanAccrualTransactionBusinessEventService, errorHandler,
|
||||
loanDownPaymentHandlerService, loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator,
|
||||
loanDownPaymentTransactionValidator, loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService,
|
||||
reprocessLoanTransactionsService, loanAccountService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -421,9 +421,11 @@ public class LoanAccountConfiguration {
|
||||
public LoanDownPaymentHandlerService loanDownPaymentHandlerService(LoanTransactionRepository loanTransactionRepository,
|
||||
BusinessEventNotifierService businessEventNotifierService,
|
||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanScheduleService loanScheduleService,
|
||||
LoanRefundService loanRefundService, LoanRefundValidator loanRefundValidator) {
|
||||
LoanRefundService loanRefundService, LoanRefundValidator loanRefundValidator,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService) {
|
||||
return new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator);
|
||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
|
||||
reprocessLoanTransactionsService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -435,8 +437,8 @@ public class LoanAccountConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanDisbursementService.class)
|
||||
public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator,
|
||||
LoanDisbursementValidator loanDisbursementValidator) {
|
||||
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator);
|
||||
LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService) {
|
||||
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -447,8 +449,9 @@ public class LoanAccountConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanScheduleService.class)
|
||||
public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService) {
|
||||
return new LoanScheduleService(loanChargeService);
|
||||
public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService) {
|
||||
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -472,10 +475,17 @@ public class LoanAccountConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
|
||||
public InterestPauseWritePlatformService interestPauseWritePlatformService(LoanTermVariationsRepository loanTermVariationsRepository,
|
||||
LoanRepositoryWrapper loanRepositoryWrapper, LoanAssembler loanAssembler, LoanAccountDomainService loanAccountDomainService,
|
||||
AccountTransfersService accountTransfersService,
|
||||
ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService) {
|
||||
LoanRepositoryWrapper loanRepositoryWrapper, LoanAssembler loanAssembler,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService) {
|
||||
return new InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, loanRepositoryWrapper, loanAssembler,
|
||||
loanAccountDomainService, accountTransfersService, replayedTransactionBusinessEventService);
|
||||
reprocessLoanTransactionsService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanAccountService.class)
|
||||
public LoanAccountService loanAccountService(LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
LoanTransactionRepository loanTransactionRepository) {
|
||||
return new LoanAccountServiceImpl(loanRepositoryWrapper, loanTransactionRepository);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-1
@@ -40,6 +40,7 @@ import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
@@ -131,6 +132,12 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
@Mock
|
||||
private LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
||||
|
||||
@Mock
|
||||
private ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
@Mock
|
||||
private LoanAccountService loanAccountService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
||||
@@ -143,7 +150,7 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
||||
when(loanChargeRepository.saveAndFlush(any(LoanCharge.class))).thenReturn(loanCharge);
|
||||
when(loan.getCurrency()).thenReturn(monetaryCurrency);
|
||||
when(loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(any())).thenReturn(loan);
|
||||
when(loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(any())).thenReturn(loan);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
|
||||
+5
-1
@@ -101,12 +101,16 @@ public class LoanDownPaymentHandlerServiceImplTest {
|
||||
@Mock
|
||||
private LoanRefundValidator loanRefundValidator;
|
||||
|
||||
@Mock
|
||||
private ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
private LoanDownPaymentHandlerServiceImpl underTest;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator);
|
||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
|
||||
reprocessLoanTransactionsService);
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
tempConfigServiceMock.when(TemporaryConfigurationServiceContainer::isExternalIdAutoGenerationEnabled).thenReturn(true);
|
||||
|
||||
Reference in New Issue
Block a user