FINERACT-2152: Interest pause during one period and between two periods

This commit is contained in:
Oleksii Novikov
2025-01-24 09:58:02 +01:00
committed by Adam Saghy
parent a9d76ef3dd
commit af37e07f31
18 changed files with 414 additions and 79 deletions
@@ -22,6 +22,7 @@ import static org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.ACTIVE
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType.INTEREST_PAUSE;
import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -43,6 +44,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.springframework.transaction.annotation.Transactional;
@AllArgsConstructor
@@ -51,24 +53,26 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
private final LoanTermVariationsRepository loanTermVariationsRepository;
private final LoanRepositoryWrapper loanRepositoryWrapper;
private final LoanAssembler loanAssembler;
@Override
public CommandProcessingResult createInterestPause(ExternalId loanExternalId, String startDateString, String endDateString,
String dateFormat, String locale) {
public CommandProcessingResult createInterestPause(final ExternalId loanExternalId, final String startDateString,
final String endDateString, final String dateFormat, final String locale) {
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
final Loan loan = loanAssembler.assembleFrom(loanExternalId, false);
return processInterestPause(loanRepositoryWrapper.findOneWithNotFoundDetection(loanExternalId), startDate, endDate, dateFormat,
locale);
return processInterestPause(loan, startDate, endDate, dateFormat, locale);
}
@Override
public CommandProcessingResult createInterestPause(Long loanId, String startDateString, String endDateString, String dateFormat,
String locale) {
public CommandProcessingResult createInterestPause(final Long loanId, final String startDateString, final String endDateString,
final String dateFormat, final String locale) {
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
final Loan loan = loanAssembler.assembleFrom(loanId, false);
return processInterestPause(loanRepositoryWrapper.findOneWithNotFoundDetection(loanId), startDate, endDate, dateFormat, locale);
return processInterestPause(loan, startDate, endDate, dateFormat, locale);
}
@Override
@@ -131,14 +135,18 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
.with(Map.of("startDate", startDate.toString(), "endDate", endDate.toString())).build();
}
private CommandProcessingResult processInterestPause(Loan loan, LocalDate startDate, LocalDate endDate, String dateFormat,
String locale) {
private CommandProcessingResult processInterestPause(final Loan loan, final LocalDate startDate, final LocalDate endDate,
String dateFormat, String locale) {
validateActiveLoan(loan);
validateInterestPauseDates(loan, startDate, endDate, dateFormat, locale, null);
LoanTermVariations variation = new LoanTermVariations(INTEREST_PAUSE.getValue(), startDate, null, endDate, false, loan);
final LoanTermVariations variation = new LoanTermVariations(INTEREST_PAUSE.getValue(), startDate, BigDecimal.ZERO, endDate, false,
loan);
LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
final LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
loan.getLoanTermVariations().add(savedVariation);
loan.reprocessTransactions();
return new CommandProcessingResultBuilder().withEntityId(savedVariation.getId()).build();
}
@@ -23,16 +23,23 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.service.DateUtils;
public class LoanTermVariationsDataWrapper {
@Getter
private final List<LoanTermVariationsData> exceptionData;
private ListIterator<LoanTermVariationsData> iterator;
@Getter
private final List<LoanTermVariationsData> interestRateChanges;
@Getter
private final List<LoanTermVariationsData> interestRateFromInstallment;
@Getter
private final List<LoanTermVariationsData> dueDateVariation;
private ListIterator<LoanTermVariationsData> dueDateIterator;
@Getter
private final List<LoanTermVariationsData> interestPauseVariations;
public LoanTermVariationsDataWrapper(final List<LoanTermVariationsData> exceptionData) {
if (exceptionData == null) {
@@ -43,6 +50,7 @@ public class LoanTermVariationsDataWrapper {
this.interestRateChanges = new ArrayList<>();
this.dueDateVariation = new ArrayList<>();
this.interestRateFromInstallment = new ArrayList<>();
this.interestPauseVariations = new ArrayList<>();
deriveLoanTermVariations();
}
@@ -80,18 +88,6 @@ public class LoanTermVariationsDataWrapper {
return this.dueDateIterator.previous();
}
public List<LoanTermVariationsData> getInterestRateChanges() {
return this.interestRateChanges;
}
public List<LoanTermVariationsData> getDueDateVariation() {
return this.dueDateVariation;
}
public List<LoanTermVariationsData> getExceptionData() {
return this.exceptionData;
}
public void setExceptionData(final List<LoanTermVariationsData> exceptionData) {
clearTerms();
this.exceptionData.addAll(exceptionData);
@@ -103,10 +99,7 @@ public class LoanTermVariationsDataWrapper {
this.interestRateChanges.clear();
this.dueDateVariation.clear();
this.interestRateFromInstallment.clear();
}
public List<LoanTermVariationsData> getInterestRateFromInstallment() {
return this.interestRateFromInstallment;
this.interestPauseVariations.clear();
}
public int adjustNumberOfRepayments() {
@@ -133,12 +126,11 @@ public class LoanTermVariationsDataWrapper {
}
public boolean hasExceptionVariation(final LocalDate date, ListIterator<LoanTermVariationsData> exceptionDataListIterator) {
ListIterator<LoanTermVariationsData> iterator = exceptionDataListIterator;
return hasNext(date, iterator);
return hasNext(date, exceptionDataListIterator);
}
public void updateLoanTermVariationsData(final List<LoanTermVariationsData> exceptionData) {
if (this.exceptionData != null && exceptionData != null && exceptionData.size() > 0) {
if (this.exceptionData != null && exceptionData != null && !exceptionData.isEmpty()) {
this.exceptionData.addAll(exceptionData);
deriveLoanTermVariations();
}
@@ -153,12 +145,15 @@ public class LoanTermVariationsDataWrapper {
this.dueDateVariation.add(loanTermVariationsData);
} else if (loanTermVariationsData.getTermVariationType().isInterestRateFromInstallment()) {
this.interestRateFromInstallment.add(loanTermVariationsData);
} else if (loanTermVariationsData.getTermVariationType().isInterestPauseVariation()) {
this.interestPauseVariations.add(loanTermVariationsData);
}
}
Collections.sort(this.dueDateVariation);
this.exceptionData.removeAll(this.interestRateChanges);
this.exceptionData.removeAll(this.dueDateVariation);
this.exceptionData.removeAll(this.interestRateFromInstallment);
this.exceptionData.removeAll(this.interestPauseVariations);
this.iterator = this.exceptionData.listIterator();
this.dueDateIterator = this.dueDateVariation.listIterator();
}
@@ -98,6 +98,10 @@ public enum LoanTermVariationType {
return this.value.equals(LoanTermVariationType.INTEREST_RATE.getValue());
}
public boolean isInterestPauseVariation() {
return this.value.equals(LoanTermVariationType.INTEREST_PAUSE.getValue());
}
public boolean isPrincipalAmountVariation() {
return this.value.equals(LoanTermVariationType.PRINCIPAL_AMOUNT.getValue());
}
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
import java.util.Map;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.portfolio.fund.domain.Fund;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -30,6 +31,12 @@ public interface LoanAssembler {
Loan assembleFrom(Long accountId);
Loan assembleFrom(Long accountId, boolean loadLazyCollections);
Loan assembleFrom(ExternalId externalId);
Loan assembleFrom(ExternalId externalId, boolean loadLazyCollections);
Loan assembleFrom(JsonCommand command);
void setHelpers(Loan loanAccount);