FINERACT-2279: Contract Termination - Schedule Handing & Reverse-replay
This commit is contained in:
+4
-1
@@ -92,6 +92,7 @@ public class LoanTransactionsApiResource {
|
||||
public static final String UNDO_REAMORTIZE = "undoReAmortize";
|
||||
public static final String CAPITALIZED_INCOME = "capitalizedIncome";
|
||||
public static final String CAPITALIZED_INCOME_ADJUSTMENT = "capitalizedIncomeAdjustment";
|
||||
public static final String CONTRACT_TERMINATION = "contractTermination";
|
||||
private final Set<String> responseDataParameters = new HashSet<>(Arrays.asList("id", "type", "date", "currency", "amount", "externalId",
|
||||
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME, LoanApiConstants.REVERSED_ON_DATE_PARAMNAME));
|
||||
|
||||
@@ -534,6 +535,7 @@ public class LoanTransactionsApiResource {
|
||||
case capitalizedIncome -> LoanTransactionType.CAPITALIZED_INCOME;
|
||||
case capitalizedIncomeAmortization -> LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION;
|
||||
case capitalizedIncomeAdjustment -> LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT;
|
||||
case contractTermination -> LoanTransactionType.CONTRACT_TERMINATION;
|
||||
default ->
|
||||
throw new InvalidLoanTransactionTypeException("transaction", transactionTypeParam.name(), "Unknown transaction type");
|
||||
};
|
||||
@@ -647,7 +649,8 @@ public class LoanTransactionsApiResource {
|
||||
transactionData = this.loanReadPlatformService.retrieveDisbursalTemplate(resolvedLoanId, false);
|
||||
} else if (CommandParameterUtil.is(commandParam, "recoverypayment")) {
|
||||
transactionData = this.loanReadPlatformService.retrieveRecoveryPaymentTemplate(resolvedLoanId);
|
||||
} else if (CommandParameterUtil.is(commandParam, "prepayLoan")) {
|
||||
} else if (CommandParameterUtil.is(commandParam, "prepayLoan")
|
||||
|| CommandParameterUtil.is(commandParam, LoanApiConstants.CONTRACT_TERMINATION_COMMAND)) {
|
||||
LocalDate transactionDate;
|
||||
if (transactionDateParam == null) {
|
||||
transactionDate = DateUtils.getBusinessLocalDate();
|
||||
|
||||
+2
@@ -1232,6 +1232,8 @@ public class LoansApiResource {
|
||||
commandRequest = new CommandWrapperBuilder().recoverFromGuarantor(resolvedLoanId).build();
|
||||
} else if (CommandParameterUtil.is(commandParam, "assigndelinquency")) {
|
||||
commandRequest = builder.assignDelinquency(resolvedLoanId).build();
|
||||
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.CONTRACT_TERMINATION_COMMAND)) {
|
||||
commandRequest = builder.applyContractTermination(resolvedLoanId).build();
|
||||
}
|
||||
|
||||
if (commandRequest == null) {
|
||||
|
||||
+2
-1
@@ -913,7 +913,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
} else {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.getLoanTransactions().add(refundTransaction);
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.handler.loan.contracttermination;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.contracttermination.LoanContractTerminationServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@CommandType(entity = "LOAN", action = "CONTRACT_TERMINATION")
|
||||
public class LoanContractTerminationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanContractTerminationServiceImpl loanContractTerminationService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand command) {
|
||||
return loanContractTerminationService.applyContractTermination(command);
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -296,7 +296,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
}
|
||||
|
||||
if (reprocessRequired) {
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
@@ -881,7 +882,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
if (reprocessRequired) {
|
||||
addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate);
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
@@ -916,7 +918,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
@@ -1500,7 +1503,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
// Waive of charges whose due date falls after latest 'repayment' transaction don't require entire loan schedule
|
||||
|
||||
+30
@@ -21,9 +21,11 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
|
||||
@@ -41,17 +43,23 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException;
|
||||
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoanUtilService {
|
||||
@@ -64,6 +72,7 @@ public class LoanUtilService {
|
||||
private final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
private final CalendarReadPlatformService calendarReadPlatformService;
|
||||
private final NoteRepository noteRepository;
|
||||
|
||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom) {
|
||||
final HolidayDetailDTO holidayDetailDTO = null;
|
||||
@@ -298,4 +307,25 @@ public class LoanUtilService {
|
||||
}
|
||||
}
|
||||
|
||||
public void checkClientOrGroupActive(final Loan loan) {
|
||||
final Client client = loan.client();
|
||||
if (client != null && client.isNotActive()) {
|
||||
throw new ClientNotActiveException(client.getId());
|
||||
}
|
||||
final Group group = loan.group();
|
||||
if (group != null && group.isNotActive()) {
|
||||
throw new GroupNotActiveException(group.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void persistNote(Loan loan, JsonCommand command, Map<String, Object> changes) {
|
||||
if (command.hasParameter(LoanApiConstants.noteParameterName)) {
|
||||
final String note = command.stringValueOfParameterNamed(LoanApiConstants.noteParameterName);
|
||||
final Note newNote = Note.loanNote(loan, note);
|
||||
changes.put(LoanApiConstants.noteParameterName, note);
|
||||
|
||||
noteRepository.save(newNote);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-6
@@ -1019,7 +1019,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private void reprocessChangedLoanTransactions(Loan loan, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
@@ -2343,7 +2344,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
reprocessLoanTransactionsService.processPostDisbursementTransactions(loan);
|
||||
}
|
||||
@@ -2502,7 +2504,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|| rescheduledRepaymentDate != null) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
}
|
||||
@@ -3233,7 +3236,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
@@ -3345,7 +3349,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
@@ -3410,7 +3415,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
"reversed");
|
||||
writeOffTransaction.reverse();
|
||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, loan);
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
+2
-1
@@ -82,7 +82,8 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
||||
List<LoanRepaymentScheduleInstallment> installmentsToReprocess = new ArrayList<>(
|
||||
loan.getRepaymentScheduleInstallments().stream().filter(i -> !i.isReAged() && !i.isAdditional()).toList());
|
||||
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro
|
||||
Optional<LoanRepaymentScheduleInstallment> currentRepaymentPeriod = getRelatedRepaymentScheduleInstallment(loan, businessDate);
|
||||
|
||||
if (currentRepaymentPeriod.isPresent()) {
|
||||
if (loan.isChargedOff()) {
|
||||
if (loan.isChargedOff() || loan.hasContractTerminationTransaction()) {
|
||||
return MathUtil.subtractToZero(currentRepaymentPeriod.get().getInterestOutstanding(loan.getCurrency()).getAmount(),
|
||||
totalUnpaidPayableDueInterest);
|
||||
} else {
|
||||
|
||||
+3
-2
@@ -70,8 +70,9 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
||||
}
|
||||
|
||||
private Predicate<LoanTransaction> loanTransactionForReprocessingPredicate() {
|
||||
return transaction -> transaction.isNotReversed() && (transaction.isChargeOff() || transaction.isReAge()
|
||||
|| transaction.isAccrualActivity() || transaction.isReAmortize() || !transaction.isNonMonetaryTransaction());
|
||||
return transaction -> transaction.isNotReversed()
|
||||
&& (transaction.isChargeOff() || transaction.isReAge() || transaction.isAccrualActivity() || transaction.isReAmortize()
|
||||
|| !transaction.isNonMonetaryTransaction() || transaction.isContractTermination());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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.contracttermination;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
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.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionContractTerminationPostBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
|
||||
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;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class LoanContractTerminationServiceImpl {
|
||||
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanRepository loanRepository;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final NoteRepository noteRepository;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanTransactionService loanTransactionService;
|
||||
|
||||
public CommandProcessingResult applyContractTermination(final JsonCommand command) {
|
||||
final Loan loan = loanAssembler.assembleFrom(command.getLoanId());
|
||||
// validate client or group is active
|
||||
loanUtilService.checkClientOrGroupActive(loan);
|
||||
|
||||
// validate Contract Termination
|
||||
validateContractTermination(loan);
|
||||
|
||||
final ExternalId externalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
|
||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||
|
||||
final LoanTransaction contractTermination = LoanTransaction.contractTermination(loan, DateUtils.getBusinessLocalDate(), externalId);
|
||||
loanTransactionRepository.save(contractTermination);
|
||||
|
||||
final String noteText = command.stringValueOfParameterNamed("note");
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
changes.put("note", noteText);
|
||||
final Note note = Note.loanTransactionNote(loan, contractTermination, noteText);
|
||||
noteRepository.save(note);
|
||||
}
|
||||
|
||||
// Mark Contract Termination, Update Loan SubStatus
|
||||
loan.setLoanSubStatus(LoanSubStatus.CONTRACT_TERMINATION);
|
||||
loanRepository.save(loan);
|
||||
changes.put(LoanApiConstants.subStatusAttributeName, loan.getLoanSubStatus().getCode());
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
final List<LoanTransaction> loanTransactions = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan);
|
||||
loanTransactions.add(contractTermination);
|
||||
reprocessLoanTransactionsService.reprocessParticularTransactions(loan, loanTransactions);
|
||||
loan.addLoanTransaction(contractTermination);
|
||||
} else {
|
||||
reprocessLoanTransactionsService.processLatestTransaction(contractTermination, loan);
|
||||
loan.addLoanTransaction(contractTermination);
|
||||
}
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanTransactionContractTerminationPostBusinessEvent(contractTermination));
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(contractTermination.getId()) //
|
||||
.withEntityExternalId(contractTermination.getExternalId()) //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
.withClientId(loan.getClientId()) //
|
||||
.withGroupId(loan.getGroupId()) //
|
||||
.withLoanId(command.getLoanId()) //
|
||||
.with(changes).build();
|
||||
}
|
||||
|
||||
public void validateContractTermination(final Loan loan) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
|
||||
if (!loan.isOpen()) {
|
||||
final String defaultUserMessage = "Contract termination can not be applied, Loan Account is not Active.";
|
||||
final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.account.is.not.active.state",
|
||||
defaultUserMessage);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (!loan.getLoanProduct().getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)) {
|
||||
final String defaultUserMessage = "Contract termination can not be applied, Loan product schedule type is not Progressive.";
|
||||
final ApiParameterError error = ApiParameterError.generalError(
|
||||
"error.msg.loan.contract.termination.is.only.supported.for.progressive.loan.schedule.type", defaultUserMessage);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (loan.isChargedOff()) {
|
||||
final String defaultUserMessage = "Contract termination can not be applied, Loan Account is Charge-Off.";
|
||||
final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.account.is.charge-off", defaultUserMessage);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (loan.isContractTermination()) {
|
||||
final String defaultUserMessage = "Contract termination can not be applied, Loan Account is already terminated.";
|
||||
final ApiParameterError error = ApiParameterError
|
||||
.generalError("error.msg.loan.account.is.already.contract.termination.substate", defaultUserMessage);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -128,7 +128,8 @@ public class LoanReAgingServiceImpl {
|
||||
}
|
||||
reverseReAgeTransaction(reAgeTransaction, command);
|
||||
loanTransactionRepository.saveAndFlush(reAgeTransaction);
|
||||
if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction())) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
+2
-2
@@ -367,9 +367,9 @@ public class LoanAccountConfiguration {
|
||||
CalendarInstanceRepository calendarInstanceRepository, ConfigurationDomainService configurationDomainService,
|
||||
HolidayRepository holidayRepository, WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
LoanScheduleGeneratorFactory loanScheduleFactory, FloatingRatesReadPlatformService floatingRatesReadPlatformService,
|
||||
CalendarReadPlatformService calendarReadPlatformService) {
|
||||
CalendarReadPlatformService calendarReadPlatformService, NoteRepository noteRepository) {
|
||||
return new LoanUtilService(applicationCurrencyRepository, calendarInstanceRepository, configurationDomainService, holidayRepository,
|
||||
workingDaysRepository, loanScheduleFactory, floatingRatesReadPlatformService, calendarReadPlatformService);
|
||||
workingDaysRepository, loanScheduleFactory, floatingRatesReadPlatformService, calendarReadPlatformService, noteRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+2
-2
@@ -107,7 +107,7 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"LoanTransactionAccrualActivityPreBusinessEvent", "LoanTransactionInterestRefundPostBusinessEvent",
|
||||
"LoanTransactionInterestRefundPreBusinessEvent", "LoanAccrualAdjustmentTransactionBusinessEvent",
|
||||
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent");
|
||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
@@ -195,7 +195,7 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"LoanTransactionAccrualActivityPreBusinessEvent", "LoanTransactionInterestRefundPostBusinessEvent",
|
||||
"LoanTransactionInterestRefundPreBusinessEvent", "LoanAccrualAdjustmentTransactionBusinessEvent",
|
||||
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent");
|
||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
|
||||
Reference in New Issue
Block a user