Skip to content

Commit 73d8659

Browse files
budaidevadamsaghy
authored andcommitted
FINERACT-2354: add reaging interest template data
1 parent 25fd822 commit 73d8659

File tree

5 files changed

+334
-7
lines changed

5 files changed

+334
-7
lines changed

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ public class LoanTransactionData implements Serializable {
124124
private Collection<CodeValueData> reAmortizationReasonOptions = null;
125125
private Collection<StringEnumOptionData> reAmortizationInterestHandlingOptions = null;
126126

127+
private Integer numberOfFutureInstallments;
128+
private Integer numberOfPastInstallments;
129+
private LocalDate nextInstallmentDueDate;
130+
private LocalDate calculatedStartDate;
131+
127132
public static LoanTransactionData importInstance(BigDecimal repaymentAmount, LocalDate lastRepaymentDate, Long repaymentTypeId,
128133
Integer rowIndex, String locale, String dateFormat) {
129134
return LoanTransactionData.builder().transactionAmount(repaymentAmount).transactionDate(lastRepaymentDate)

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTransactionMapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public interface LoanTransactionMapper {
4040
@Mapping(target = "reAmortizationInterestHandlingOptions", ignore = true)
4141
@Mapping(target = "classificationOptions", ignore = true)
4242
@Mapping(target = "paymentTypeOptions", ignore = true)
43+
@Mapping(target = "numberOfFutureInstallments", ignore = true)
44+
@Mapping(target = "numberOfPastInstallments", ignore = true)
45+
@Mapping(target = "nextInstallmentDueDate", ignore = true)
46+
@Mapping(target = "calculatedStartDate", ignore = true)
4347
@Mapping(target = "overpaymentPortion", ignore = true)
4448
@Mapping(target = "transfer", ignore = true)
4549
@Mapping(target = "fixedEmiAmount", ignore = true)

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ public class LoanTransactionsApiResource {
9898
public static final String CAPITALIZED_INCOME = "capitalizedIncome";
9999
public static final String INTEREST_REFUND_COMMAND_VALUE = "interest-refund";
100100
private final Set<String> responseDataParameters = new HashSet<>(Arrays.asList("id", "type", "date", "currency", "amount", "externalId",
101-
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME, LoanApiConstants.REVERSED_ON_DATE_PARAMNAME, "classification"));
101+
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME, LoanApiConstants.REVERSED_ON_DATE_PARAMNAME, "classification",
102+
"numberOfFutureInstallments", "numberOfPastInstallments", "nextInstallmentDueDate", "calculatedStartDate"));
102103

103104
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
104105

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
8383
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
8484
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
85+
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
8586
import org.apache.fineract.portfolio.common.service.CommonEnumerations;
8687
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
8788
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
@@ -1728,16 +1729,57 @@ public LoanTransactionData retrieveLoanWriteoffTemplate(final Long loanId) {
17281729

17291730
@Override
17301731
public LoanTransactionData retrieveLoanReAgeTemplate(final Long loanId) {
1731-
final LoanAccountData loan = this.retrieveOne(loanId);
1732+
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
17321733
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REAGE);
17331734
final BigDecimal totalOutstanding = loan.getSummary() != null ? loan.getSummary().getTotalOutstanding() : null;
17341735
final List<CodeValueData> reAgeReasonOptions = new ArrayList<>(
17351736
codeValueReadPlatformService.retrieveCodeValuesByCode(LoanApiConstants.REAGE_REASONS));
1736-
return LoanTransactionData.builder().type(transactionType).currency(loan.getCurrency()).date(DateUtils.getBusinessLocalDate())
1737-
.amount(totalOutstanding).netDisbursalAmount(loan.getNetDisbursalAmount()).loanId(loanId)
1738-
.externalLoanId(loan.getExternalId()).periodFrequencyOptions(CommonEnumerations.BASIC_PERIOD_FREQUENCY_TYPES)
1739-
.reAgeReasonOptions(reAgeReasonOptions)
1740-
.reAgeInterestHandlingOptions(LoanReAgeInterestHandlingType.getValuesAsEnumOptionDataList()).build();
1737+
1738+
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
1739+
final List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
1740+
1741+
int futureInstallmentCount = 0;
1742+
int pastInstallmentCount = 0;
1743+
LocalDate nextInstallmentDueDate = null;
1744+
1745+
for (LoanRepaymentScheduleInstallment installment : installments) {
1746+
LocalDate dueDate = installment.getDueDate();
1747+
if (DateUtils.isAfter(dueDate, businessDate)) {
1748+
futureInstallmentCount++;
1749+
if (nextInstallmentDueDate == null || DateUtils.isBefore(dueDate, nextInstallmentDueDate)) {
1750+
nextInstallmentDueDate = dueDate;
1751+
}
1752+
} else {
1753+
pastInstallmentCount++;
1754+
}
1755+
}
1756+
1757+
final PeriodFrequencyType frequencyType = loan.getLoanRepaymentScheduleDetail().getRepaymentPeriodFrequencyType();
1758+
final LocalDate calculatedStartDate = calculateReAgeStartDate(businessDate, frequencyType);
1759+
1760+
final CurrencyData currencyData = new CurrencyData(loan.getCurrencyCode(), null, loan.getCurrency().getDigitsAfterDecimal(),
1761+
loan.getCurrency().getInMultiplesOf(), null, null);
1762+
1763+
return LoanTransactionData.builder().type(transactionType).currency(currencyData).date(businessDate).amount(totalOutstanding)
1764+
.netDisbursalAmount(loan.getNetDisbursalAmount()).loanId(loanId).externalLoanId(loan.getExternalId())
1765+
.periodFrequencyOptions(CommonEnumerations.BASIC_PERIOD_FREQUENCY_TYPES).reAgeReasonOptions(reAgeReasonOptions)
1766+
.reAgeInterestHandlingOptions(LoanReAgeInterestHandlingType.getValuesAsEnumOptionDataList())
1767+
.numberOfFutureInstallments(futureInstallmentCount).numberOfPastInstallments(pastInstallmentCount)
1768+
.nextInstallmentDueDate(nextInstallmentDueDate).calculatedStartDate(calculatedStartDate).build();
1769+
}
1770+
1771+
private LocalDate calculateReAgeStartDate(LocalDate businessDate, PeriodFrequencyType frequencyType) {
1772+
if (frequencyType == null) {
1773+
return null;
1774+
}
1775+
// Per PS-2795: calculated start date = current date + 1 unit of original frequency type
1776+
return switch (frequencyType) {
1777+
case DAYS -> businessDate.plusDays(1);
1778+
case WEEKS -> businessDate.plusWeeks(1);
1779+
case MONTHS -> businessDate.plusMonths(1);
1780+
case YEARS -> businessDate.plusYears(1);
1781+
case WHOLE_TERM, INVALID -> null;
1782+
};
17411783
}
17421784

17431785
@Override

0 commit comments

Comments
 (0)