FINERACT-2232: Capitalized Incomce Adjustment template

This commit is contained in:
Jose Alberto Hernandez
2025-05-24 01:17:55 +02:00
committed by Adam Saghy
parent f6e24d420a
commit 544607a05a
16 changed files with 221 additions and 24 deletions
@@ -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;
@@ -579,7 +579,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.body().getLoanId();
Response<GetLoansLoanIdTransactionsTemplateResponse> 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);
@@ -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";
@@ -54,5 +54,6 @@ public interface LoanTransactionApiConstants {
accrualAdjustment, //
capitalizedIncome, //
capitalizedIncomeAmortization, //
capitalizedIncomeAdjustment, //
}
}
@@ -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<PaymentTypeData> 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<PaymentTypeData> paymentOptions, final BigDecimal retriveLastEmiAmount,
@@ -650,6 +650,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
return LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION.equals(getTypeOf()) && isNotReversed();
}
public boolean isCapitalizedIncomeAdjustment() {
return LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.equals(getTypeOf()) && isNotReversed();
}
public boolean isWaiver() {
return isInterestWaiver() || isChargesWaiver();
}
@@ -69,6 +69,7 @@ public enum LoanTransactionType {
ACCRUAL_ADJUSTMENT(34, "loanTransactionType.accrualAdjustment"), //
CAPITALIZED_INCOME(35, "loanTransactionType.capitalizedIncome"), //
CAPITALIZED_INCOME_AMORTIZATION(36, "loanTransactionType.capitalizedIncomeAmortization"), //
CAPITALIZED_INCOME_ADJUSTMENT(37, "loanTransactionType.capitalizedIncomeAdjustment"), //
;
private final Integer value;
@@ -121,6 +122,7 @@ public enum LoanTransactionType {
case 34 -> 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;
}
}
@@ -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);
@@ -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");
};
}
@@ -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
@@ -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<String> 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);
}
@@ -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<PaymentTypeData> 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<CurrencyData> {
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<LoanAccountData> {
private final DatabaseSpecificSQLGenerator sqlGenerator;
@@ -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
@@ -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) {
@@ -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);
});
}
}
@@ -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) {