diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java index cd73614f3..ebb16e657 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java @@ -35,7 +35,9 @@ public enum TransactionType { INTEREST_PAYMENT_WAIVER("interestPaymentWaiver"), // REPAYMENT_AT_DISBURSEMENT("repaymentAtDisbursement"), // CAPITALIZED_INCOME("capitalizedIncome"), // - CAPITALIZED_INCOME_AMORTIZATION("capitalizedIncomeAmortization"); // + CAPITALIZED_INCOME_AMORTIZATION("capitalizedIncomeAmortization"), // + CAPITALIZED_INCOME_ADJUSTMENT("capitalizedIncomeAdjustment"), // + ; public final String value; diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanRepaymentStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanRepaymentStepDef.java index e0e0e9370..6530aede2 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanRepaymentStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanRepaymentStepDef.java @@ -579,7 +579,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef { Response loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); long loanId1 = loanResponse.body().getLoanId(); Response response = loanTransactionsApi - .retrieveTransactionTemplate(loanId1, "prepayLoan", DATE_FORMAT, transactionDate, DEFAULT_LOCALE).execute(); + .retrieveTransactionTemplate(loanId1, "prepayLoan", DATE_FORMAT, transactionDate, DEFAULT_LOCALE, null).execute(); Double transactionAmount = response.body().getAmount(); log.debug("%n--- Loan Pay-off with amount: {} ---", transactionAmount); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java index 62dfbd146..e6db7ed52 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java @@ -175,6 +175,7 @@ public interface LoanApiConstants { String CHARGEBACK_TRANSACTION_COMMAND = "chargeback"; String MARK_AS_FRAUD_COMMAND = "markAsFraud"; String CAPITALIZED_INCOME_TRANSACTION_COMMAND = "capitalizedIncome"; + String CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND = "capitalizedIncomeAdjustment"; // Data Validator names String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud"; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionApiConstants.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionApiConstants.java index 70de65491..23a426ec7 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionApiConstants.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionApiConstants.java @@ -54,5 +54,6 @@ public interface LoanTransactionApiConstants { accrualAdjustment, // capitalizedIncome, // capitalizedIncomeAmortization, // + capitalizedIncomeAdjustment, // } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java index c9a9ced86..abcf28516 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java @@ -313,6 +313,36 @@ public class LoanTransactionData implements Serializable { null, null, outstandingLoanBalance, null, manuallyReversed, ExternalId.empty(), null, loanId); } + public static LoanTransactionData loanTransactionDataForCreditTemplate(final LoanTransactionEnumData transactionType, + final LocalDate transactionDate, final BigDecimal transactionAmount, final Collection paymentOptions, + final CurrencyData currency) { + final Long id = null; + final Long loanId = null; + final ExternalId externalLoanId = ExternalId.empty(); + final ExternalId externalId = ExternalId.empty(); + final Long officeId = null; + final String officeName = null; + final PaymentDetailData paymentDetailData = null; + final BigDecimal unrecognizedIncomePortion = null; + final BigDecimal principalPortion = null; + final BigDecimal interestPortion = null; + final BigDecimal feeChargesPortion = null; + final BigDecimal penaltyChargesPortion = null; + final BigDecimal overpaymentPortion = null; + final BigDecimal netDisbursalAmount = null; + final BigDecimal fixedEmiAmount = null; + final BigDecimal outstandingLoanBalance = null; + final AccountTransferData transfer = null; + final LocalDate submittedOnDate = null; + final LocalDate possibleNextRepaymentDate = null; + final boolean manuallyReversed = false; + return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currency, transactionDate, + transactionAmount, netDisbursalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, + overpaymentPortion, unrecognizedIncomePortion, paymentOptions, transfer, externalId, fixedEmiAmount, outstandingLoanBalance, + submittedOnDate, manuallyReversed, possibleNextRepaymentDate, loanId, externalLoanId); + + } + public static LoanTransactionData loanTransactionDataForDisbursalTemplate(final LoanTransactionEnumData transactionType, final LocalDate expectedDisbursedOnLocalDateForTemplate, final BigDecimal disburseAmountForTemplate, final BigDecimal netDisbursalAmount, final Collection paymentOptions, final BigDecimal retriveLastEmiAmount, diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java index 55a29f5e7..47f4ef530 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java @@ -650,6 +650,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom LoanTransactionType.ACCRUAL_ADJUSTMENT; case 35 -> LoanTransactionType.CAPITALIZED_INCOME; case 36 -> LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION; + case 37 -> LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT; default -> LoanTransactionType.INVALID; }; } @@ -241,4 +243,8 @@ public enum LoanTransactionType { public boolean isCapitalizedIncome() { return this == LoanTransactionType.CAPITALIZED_INCOME; } + + public boolean isCapitalizedIncomeAdjustment() { + return this == LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT; + } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java index c81a5d73f..cdff9421b 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java @@ -63,7 +63,7 @@ public interface LoanReadPlatformService { LoanTransactionData retrieveLoanTransactionTemplate(Long loanId); - LoanTransactionData retrieveLoanTransactionTemplate(Long loanId, LoanTransactionType transactionType); + LoanTransactionData retrieveLoanTransactionTemplate(Long loanId, LoanTransactionType transactionType, Long transactionId); LoanTransactionData retrieveWaiveInterestDetails(Long loanId); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java index f3c77f572..807def936 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java @@ -330,6 +330,9 @@ public final class LoanEnumerations { case CAPITALIZED_INCOME_AMORTIZATION -> new LoanTransactionEnumData(LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION.getValue().longValue(), LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION.getCode(), "Capitalized Income Amortization"); + case CAPITALIZED_INCOME_ADJUSTMENT -> + new LoanTransactionEnumData(LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.getValue().longValue(), + LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.getCode(), "Capitalized Income Adjustment"); }; } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java index af4d6d344..6c22ce20a 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java @@ -93,7 +93,10 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc // Post journal entries journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - return new CommandProcessingResultBuilder().withEntityId(loan.getId()).withEntityExternalId(loan.getExternalId()).build(); + return new CommandProcessingResultBuilder() // + .withEntityId(capitalizedIncomeTransaction.getId()) // + .withEntityExternalId(capitalizedIncomeTransaction.getExternalId()) // + .build(); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java index 68dbc254b..89a1c5898 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java @@ -91,6 +91,7 @@ public class LoanTransactionsApiResource { public static final String REAMORTIZE = "reAmortize"; public static final String UNDO_REAMORTIZE = "undoReAmortize"; public static final String CAPITALIZED_INCOME = "capitalizedIncome"; + public static final String CAPITALIZED_INCOME_ADJUSTMENT = "capitalizedIncomeAdjustment"; private final Set responseDataParameters = new HashSet<>(Arrays.asList("id", "type", "date", "currency", "amount", "externalId", LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME, LoanApiConstants.REVERSED_ON_DATE_PARAMNAME)); @@ -126,11 +127,12 @@ public class LoanTransactionsApiResource { @QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo, @QueryParam("dateFormat") @Parameter(description = "dateFormat") final String rawDateFormat, @QueryParam("transactionDate") @Parameter(description = "transactionDate") final DateParam transactionDateParam, - @QueryParam("locale") @Parameter(description = "locale") final String locale) { + @QueryParam("locale") @Parameter(description = "locale") final String locale, + @QueryParam("transactionId") @Parameter(description = "transactionId") final Long transactionId) { final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ? null : new DateFormat(rawDateFormat); - return retrieveTransactionTemplate(loanId, null, commandParam, uriInfo, dateFormat, transactionDateParam, locale); + return retrieveTransactionTemplate(loanId, null, commandParam, uriInfo, dateFormat, transactionDateParam, locale, transactionId); } @GET @@ -156,11 +158,13 @@ public class LoanTransactionsApiResource { @QueryParam("command") @Parameter(description = "command") final String commandParam, @Context final UriInfo uriInfo, @QueryParam("dateFormat") @Parameter(description = "dateFormat") final String rawDateFormat, @QueryParam("transactionDate") @Parameter(description = "transactionDate") final DateParam transactionDateParam, - @QueryParam("locale") @Parameter(description = "locale") final String locale) { + @QueryParam("locale") @Parameter(description = "locale") final String locale, + @QueryParam("transactionId") @Parameter(description = "transactionId") final Long transactionId) { final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ? null : new DateFormat(rawDateFormat); - return retrieveTransactionTemplate(null, loanExternalId, commandParam, uriInfo, dateFormat, transactionDateParam, locale); + return retrieveTransactionTemplate(null, loanExternalId, commandParam, uriInfo, dateFormat, transactionDateParam, locale, + transactionId); } @GET @@ -529,6 +533,7 @@ public class LoanTransactionsApiResource { case accrualAdjustment -> LoanTransactionType.ACCRUAL_ADJUSTMENT; case capitalizedIncome -> LoanTransactionType.CAPITALIZED_INCOME; case capitalizedIncomeAmortization -> LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION; + case capitalizedIncomeAdjustment -> LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT; default -> throw new InvalidLoanTransactionTypeException("transaction", transactionTypeParam.name(), "Unknown transaction type"); }; @@ -598,7 +603,7 @@ public class LoanTransactionsApiResource { } private String retrieveTransactionTemplate(Long loanId, String loanExternalIdStr, String commandParam, UriInfo uriInfo, - DateFormat dateFormat, DateParam transactionDateParam, String locale) { + DateFormat dateFormat, DateParam transactionDateParam, String locale, Long transactionId) { this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr); @@ -671,7 +676,10 @@ public class LoanTransactionsApiResource { transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId); } else if (CommandParameterUtil.is(commandParam, LoanApiConstants.CAPITALIZED_INCOME_TRANSACTION_COMMAND)) { transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId, - LoanTransactionType.CAPITALIZED_INCOME); + LoanTransactionType.CAPITALIZED_INCOME, transactionId); + } else if (CommandParameterUtil.is(commandParam, LoanApiConstants.CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND)) { + transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId, + LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT, transactionId); } else { throw new UnrecognizedQueryParamException("command", commandParam); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index c7b4a45ea..ca2154bda 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -50,6 +50,7 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; +import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.SearchParameters; @@ -107,6 +108,7 @@ import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData; import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance; import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType; import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy; import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType; @@ -126,6 +128,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanSc import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionMapper; +import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository; import org.apache.fineract.portfolio.loanaccount.serialization.LoanForeclosureValidator; import org.apache.fineract.portfolio.loanproduct.data.LoanProductData; import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData; @@ -180,6 +183,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa private final LoanTransactionMapper loanTransactionMapper; private final LoanTransactionProcessingService loadTransactionProcessingService; private final LoanBalanceService loanBalanceService; + private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository; @Override public LoanAccountData retrieveOne(final Long loanId) { @@ -451,10 +455,52 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa return loanDetails; } + private CurrencyData retriveLoanCurrencyData(final Long loanId) { + final LoanCurrencyDataMapper loanCurrencyMapper = new LoanCurrencyDataMapper(sqlGenerator); + final String sql = "select " + loanCurrencyMapper.schema() + " where l.id = ?"; + + return this.jdbcTemplate.queryForObject(sql, loanCurrencyMapper, loanId); + } + @Override - public LoanTransactionData retrieveLoanTransactionTemplate(final Long loanId, LoanTransactionType transactionType) { - return LoanTransactionData.templateOnTop(retrieveLoanTransactionTemplate(loanId), - LoanEnumerations.transactionType(transactionType)); + public LoanTransactionData retrieveLoanTransactionTemplate(final Long loanId, final LoanTransactionType transactionType, + final Long transactionId) { + + LoanTransactionData loanTransactionData = null; + Collection paymentOptions = null; + BigDecimal transactionAmount = BigDecimal.ZERO; + switch (transactionType) { + case CAPITALIZED_INCOME: + final Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId); + if (loan.getLoanProduct().getLoanProductRelatedDetail().isEnableIncomeCapitalization()) { + final BigDecimal capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loanId).stream() + .map(LoanCapitalizedIncomeBalance::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add); + transactionAmount = loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()).subtract(capitalizedIncomeBalance); + } + paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes(); + loanTransactionData = LoanTransactionData.loanTransactionDataForCreditTemplate( + LoanEnumerations.transactionType(transactionType), DateUtils.getBusinessLocalDate(), transactionAmount, + paymentOptions, retriveLoanCurrencyData(loanId)); + + break; + case CAPITALIZED_INCOME_ADJUSTMENT: + final LoanCapitalizedIncomeBalance loanCapitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository + .findByLoanIdAndLoanTransactionId(loanId, transactionId); + + transactionAmount = (loanCapitalizedIncomeBalance == null) ? BigDecimal.ZERO + : loanCapitalizedIncomeBalance.getAmount() + .subtract(MathUtil.nullToZero(loanCapitalizedIncomeBalance.getAmountAdjustment())); + loanTransactionData = LoanTransactionData.loanTransactionDataForCreditTemplate( + LoanEnumerations.transactionType(transactionType), DateUtils.getBusinessLocalDate(), transactionAmount, + paymentOptions, retriveLoanCurrencyData(loanId)); + break; + default: + loanTransactionData = LoanTransactionData.templateOnTop(retrieveLoanTransactionTemplate(loanId), + LoanEnumerations.transactionType(transactionType)); + break; + } + + return loanTransactionData; } @Override @@ -646,6 +692,33 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa return loanId; } + private static final class LoanCurrencyDataMapper implements RowMapper { + + private final DatabaseSpecificSQLGenerator sqlGenerator; + + LoanCurrencyDataMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + + public String schema() { + return " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc." + + sqlGenerator.escape("name") + + " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode from m_loan l join m_currency rc on rc." + + sqlGenerator.escape("code") + " = l.currency_code "; + } + + @Override + public CurrencyData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + return new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); + } + } + private static final class LoanMapper implements RowMapper { private final DatabaseSpecificSQLGenerator sqlGenerator; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java index 47f02d53e..3dfa3bcf0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java @@ -88,6 +88,7 @@ import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionMapper; +import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository; import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationTransitionValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator; @@ -333,14 +334,16 @@ public class LoanAccountConfiguration { DelinquencyReadPlatformService delinquencyReadPlatformService, LoanTransactionRepository loanTransactionRepository, LoanChargePaidByReadService loanChargePaidByReadService, LoanTransactionRelationReadService loanTransactionRelationReadService, LoanForeclosureValidator loanForeclosureValidator, LoanTransactionMapper loanTransactionMapper, - LoanTransactionProcessingService loanTransactionProcessingService, LoanBalanceService loanBalanceService) { + LoanTransactionProcessingService loanTransactionProcessingService, LoanBalanceService loanBalanceService, + LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository) { return new LoanReadPlatformServiceImpl(jdbcTemplate, context, loanRepositoryWrapper, applicationCurrencyRepository, loanProductReadPlatformService, clientReadPlatformService, groupReadPlatformService, loanDropdownReadPlatformService, fundReadPlatformService, chargeReadPlatformService, codeValueReadPlatformService, calendarReadPlatformService, staffReadPlatformService, paginationHelper, paymentTypeReadPlatformService, floatingRatesReadPlatformService, loanUtilService, configurationDomainService, accountDetailsReadPlatformService, columnValidator, sqlGenerator, delinquencyReadPlatformService, loanTransactionRepository, loanChargePaidByReadService, loanTransactionRelationReadService, - loanForeclosureValidator, loanTransactionMapper, loanTransactionProcessingService, loanBalanceService); + loanForeclosureValidator, loanTransactionMapper, loanTransactionProcessingService, loanBalanceService, + loanCapitalizedIncomeBalanceRepository); } @Bean diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java index 40030968b..2721be937 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java @@ -276,7 +276,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest { } protected GetLoansLoanIdTransactionsTemplateResponse getPrepayAmount(Long loanId, String date) { - return ok(fineractClient().loanTransactions.retrieveTransactionTemplate(loanId, "prepayLoan", DATETIME_PATTERN, date, "en")); + return ok(fineractClient().loanTransactions.retrieveTransactionTemplate(loanId, "prepayLoan", DATETIME_PATTERN, date, "en", null)); } protected Long verifyPrepayAmountByRepayment(Long loanId, String date) { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java index 63b0c6cb6..e159f2b84 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java @@ -25,17 +25,25 @@ import java.math.BigDecimal; import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; +import lombok.extern.slf4j.Slf4j; import org.apache.fineract.client.models.GetLoansLoanIdTransactionsResponse; import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse; import org.apache.fineract.client.models.PostClientsResponse; +import org.apache.fineract.client.models.PostLoanProductsRequest; import org.apache.fineract.client.models.PostLoanProductsResponse; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.client.models.TransactionType; import org.apache.fineract.integrationtests.common.ClientHelper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +@Slf4j public class LoanTransactionTest extends BaseLoanIntegrationTest { + private final String capitalizedIncomeCommand = "capitalizedIncome"; + private final String capitalizedIncomeAdjustmentCommand = "capitalizedIncomeAdjustment"; + @Test public void testGetLoanTransactionsFiltering() { final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()); @@ -94,24 +102,68 @@ public class LoanTransactionTest extends BaseLoanIntegrationTest { public void testGetLoanTransactionTemplateForCapitalizedIncome() { final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()); - final PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive()); + final PostLoanProductsResponse loanProductsResponse = loanProductHelper + .createLoanProduct(create4IProgressive().enableIncomeCapitalization(true) + .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT) + .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION) + .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue()) + .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()) + .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)); final String loanExternalIdStr = UUID.randomUUID().toString(); - final String command = "capitalizedIncome"; runAt("20 December 2024", () -> { Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024", 430.0, 7.0, 6, (request) -> request.externalId(loanExternalIdStr)); - disburseLoan(loanId, BigDecimal.valueOf(430), "20 December 2024"); + disburseLoan(loanId, BigDecimal.valueOf(230), "20 December 2024"); final GetLoansLoanIdTransactionsTemplateResponse transactionTemplate = loanTransactionHelper.retrieveTransactionTemplate(loanId, - command, null, null, null); + capitalizedIncomeCommand, null, null, null); assertNotNull(transactionTemplate); - assertEquals("loanTransactionType." + command, transactionTemplate.getType().getCode()); + assertEquals("loanTransactionType." + capitalizedIncomeCommand, transactionTemplate.getType().getCode()); + assertEquals(transactionTemplate.getAmount(), 200); assertThat(transactionTemplate.getPaymentTypeOptions().size() > 0); }); } + @Test + public void testGetLoanTransactionTemplateForCapitalizedIncomeAdjustment() { + final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()); + + final PostLoanProductsResponse loanProductsResponse = loanProductHelper + .createLoanProduct(create4IProgressive().enableIncomeCapitalization(true) + .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT) + .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION) + .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue()) + .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()) + .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)); + + final String loanExternalIdStr = UUID.randomUUID().toString(); + + runAt("20 December 2024", () -> { + final Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), + "20 December 2024", 430.0, 7.0, 6, (request) -> request.externalId(loanExternalIdStr)); + + disburseLoan(loanId, BigDecimal.valueOf(230), "20 December 2024"); + + PostLoansLoanIdTransactionsResponse loanTransactionResponse = loanTransactionHelper.executeLoanTransaction(loanId, + new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("20 December 2024").locale("en") + .transactionAmount(150.0), + capitalizedIncomeCommand); + assertNotNull(loanTransactionResponse); + final Long transactionId = loanTransactionResponse.getResourceId(); + assertNotNull(transactionId); + log.info("Loan Id {} with transaction id {}", loanId, transactionId); + + final GetLoansLoanIdTransactionsTemplateResponse transactionTemplate = loanTransactionHelper.retrieveTransactionTemplate(loanId, + capitalizedIncomeAdjustmentCommand, null, null, null, transactionId); + + assertNotNull(transactionTemplate); + assertEquals("loanTransactionType." + capitalizedIncomeAdjustmentCommand, transactionTemplate.getType().getCode()); + assertEquals(transactionTemplate.getAmount(), 150); + }); + } + } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java index 8918a5a14..5de3e0244 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java @@ -902,6 +902,11 @@ public class LoanTransactionHelper { return postLoanTransaction(createLoanTransactionURL(MAKE_REPAYMENT_COMMAND, loanID), getRepaymentBodyAsJSON(date, amountToBePaid)); } + public PostLoansLoanIdTransactionsResponse executeLoanTransaction(final Long loanId, final PostLoansLoanIdTransactionsRequest request, + final String command) { + return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.executeLoanTransaction(loanId, request, command)); + } + public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final Long loanId, final PostLoansLoanIdTransactionsRequest request) { return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.executeLoanTransaction(loanId, request, "repayment")); } @@ -2332,7 +2337,7 @@ public class LoanTransactionHelper { public GetLoansLoanIdTransactionsTemplateResponse getPrepaymentAmount(final Long loanId, final String transactionDate, String dateformat) { return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.retrieveTransactionTemplate(loanId, "prepayLoan", - dateformat, transactionDate, "en")); + dateformat, transactionDate, "en", null)); } // TODO: Rewrite to use fineract-client instead! @@ -2806,16 +2811,22 @@ public class LoanTransactionHelper { return chargebackPayload; } + public GetLoansLoanIdTransactionsTemplateResponse retrieveTransactionTemplate(Long loanId, String command, String dateFormat, + String transactionDate, String locale, Long transactionId) { + return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.retrieveTransactionTemplate(loanId, command, dateFormat, + transactionDate, locale, transactionId)); + } + public GetLoansLoanIdTransactionsTemplateResponse retrieveTransactionTemplate(Long loanId, String command, String dateFormat, String transactionDate, String locale) { return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.retrieveTransactionTemplate(loanId, command, dateFormat, - transactionDate, locale)); + transactionDate, locale, null)); } public GetLoansLoanIdTransactionsTemplateResponse retrieveTransactionTemplate(String loanExternalIdStr, String command, String dateFormat, String transactionDate, String locale) { return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, command, - dateFormat, transactionDate, locale)); + dateFormat, transactionDate, locale, null)); } public GetLoansApprovalTemplateResponse getLoanApprovalTemplate(String loanExternalIdStr) {