FINERACT-2311: Add buydown fees adjustments
This commit is contained in:
+9
@@ -3841,6 +3841,15 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder buyDownFeeAdjustment(final Long loanId, final Long transactionId) {
|
||||||
|
this.actionName = "BUYDOWNFEEADJUSTMENT";
|
||||||
|
this.entityName = "LOAN";
|
||||||
|
this.entityId = transactionId;
|
||||||
|
this.loanId = loanId;
|
||||||
|
this.href = "/loans/" + loanId + "/transactions/" + transactionId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder applyContractTermination(final Long loanId) {
|
public CommandWrapperBuilder applyContractTermination(final Long loanId) {
|
||||||
this.actionName = "CONTRACT_TERMINATION";
|
this.actionName = "CONTRACT_TERMINATION";
|
||||||
this.entityName = "LOAN";
|
this.entityName = "LOAN";
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.event.business.domain.loan.transaction;
|
||||||
|
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
|
||||||
|
public class LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent extends LoanTransactionBusinessEvent {
|
||||||
|
|
||||||
|
private static final String TYPE = "LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent";
|
||||||
|
|
||||||
|
public LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent(LoanTransaction value) {
|
||||||
|
super(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.event.business.domain.loan.transaction;
|
||||||
|
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
|
||||||
|
public class LoanBuyDownFeeTransactionCreatedBusinessEvent extends LoanTransactionBusinessEvent {
|
||||||
|
|
||||||
|
private static final String TYPE = "LoanBuyDownFeeTransactionCreatedBusinessEvent";
|
||||||
|
|
||||||
|
public LoanBuyDownFeeTransactionCreatedBusinessEvent(LoanTransaction value) {
|
||||||
|
super(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -332,6 +332,12 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
amount.getAmount(), amount.getAmount(), null, null, null, null, false, paymentDetail, externalId);
|
amount.getAmount(), amount.getAmount(), null, null, null, null, false, paymentDetail, externalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LoanTransaction buyDownFeeAdjustment(final Loan loan, final Money amount, final PaymentDetail paymentDetail,
|
||||||
|
final LocalDate transactionDate, final ExternalId externalId) {
|
||||||
|
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT, transactionDate, amount.getAmount(),
|
||||||
|
amount.getAmount(), null, null, null, null, false, paymentDetail, externalId);
|
||||||
|
}
|
||||||
|
|
||||||
public static LoanTransaction capitalizedIncomeAmortizationAdjustment(final Loan loan, final Money amount,
|
public static LoanTransaction capitalizedIncomeAmortizationAdjustment(final Loan loan, final Money amount,
|
||||||
final LocalDate transactionDate, final ExternalId externalId) {
|
final LocalDate transactionDate, final ExternalId externalId) {
|
||||||
return switch (loan.getLoanProductRelatedDetail().getCapitalizedIncomeType()) {
|
return switch (loan.getLoanProductRelatedDetail().getCapitalizedIncomeType()) {
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.annotation.CommandType;
|
||||||
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.apache.fineract.infrastructure.DataIntegrityErrorHandler;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.BuyDownFeePlatformService;
|
||||||
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
|
import org.springframework.orm.jpa.JpaSystemException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@CommandType(entity = "LOAN", action = "BUYDOWNFEEADJUSTMENT")
|
||||||
|
public class BuyDownFeeAdjustmentCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final BuyDownFeePlatformService buyDownFeePlatformService;
|
||||||
|
private final DataIntegrityErrorHandler dataIntegrityErrorHandler;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return this.buyDownFeePlatformService.buyDownFeeAdjustment(command.getLoanId(), command.entityId(), command);
|
||||||
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
|
dataIntegrityErrorHandler.handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve, "loan.buy.down.fee",
|
||||||
|
"Buy Down Fee");
|
||||||
|
return CommandProcessingResult.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-1
@@ -18,9 +18,32 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.repository;
|
package org.apache.fineract.portfolio.loanaccount.repository;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
|
||||||
public interface LoanBuyDownFeesBalanceRepository
|
public interface LoanBuyDownFeesBalanceRepository
|
||||||
extends JpaRepository<LoanBuyDownFeeBalance, Long>, JpaSpecificationExecutor<LoanBuyDownFeeBalance> {}
|
extends JpaRepository<LoanBuyDownFeeBalance, Long>, JpaSpecificationExecutor<LoanBuyDownFeeBalance> {
|
||||||
|
|
||||||
|
String FIND_BALANCE_REPAYMENT_SCHEDULE_DATA = "SELECT new org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData(lbfb.loanTransaction.id, lbfb.loan.id, lbfb.loanTransaction.dateOf, lbfb.loanTransaction.reversed, lbfb.amount, lbfb.unrecognizedAmount, lbfb.loanTransaction.feeChargesPortion) FROM LoanBuyDownFeeBalance lbfb ";
|
||||||
|
|
||||||
|
List<LoanBuyDownFeeBalance> findAllByLoanId(Long loanId);
|
||||||
|
|
||||||
|
LoanBuyDownFeeBalance findByLoanIdAndLoanTransactionId(Long loanId, Long transactionId);
|
||||||
|
|
||||||
|
@Query(FIND_BALANCE_REPAYMENT_SCHEDULE_DATA + " WHERE lbfb.loan.id = :loanId")
|
||||||
|
List<LoanTransactionRepaymentPeriodData> findRepaymentPeriodDataByLoanId(Long loanId);
|
||||||
|
|
||||||
|
@Query("SELECT SUM(lbfb.amount) FROM LoanBuyDownFeeBalance lbfb WHERE lbfb.loan.id = :loanId")
|
||||||
|
BigDecimal calculateBuydownFeeAmount(Long loanId);
|
||||||
|
|
||||||
|
@Query("SELECT SUM(lbfb.amountAdjustment) FROM LoanBuyDownFeeBalance lbfb WHERE lbfb.loan.id = :loanId")
|
||||||
|
BigDecimal calculateBuydownFeeAdjustment(Long loanId);
|
||||||
|
|
||||||
|
@Query("SELECT lbfb FROM LoanBuyDownFeeBalance lbfb, LoanTransaction lt, LoanTransactionRelation ltr WHERE lt.loan.id = lbfb.loan.id AND ltr.fromTransaction.id =:transactionId AND ltr.toTransaction.id=lt.id AND lbfb.loanTransaction.id = lt.id")
|
||||||
|
LoanBuyDownFeeBalance findBalanceForAdjustment(Long transactionId);
|
||||||
|
}
|
||||||
|
|||||||
+3
@@ -26,4 +26,7 @@ public interface BuyDownFeePlatformService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
CommandProcessingResult makeLoanBuyDownFee(Long loanId, JsonCommand command);
|
CommandProcessingResult makeLoanBuyDownFee(Long loanId, JsonCommand command);
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
CommandProcessingResult buyDownFeeAdjustment(Long loanId, Long buyDownFeeTransactionId, JsonCommand command);
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-1
@@ -24,6 +24,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -31,7 +32,13 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
|||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanBuyDownFeeTransactionCreatedBusinessEvent;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
||||||
@@ -39,7 +46,10 @@ import org.apache.fineract.portfolio.group.domain.Group;
|
|||||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeesBalanceRepository;
|
import org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeesBalanceRepository;
|
||||||
import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
|
import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
|
||||||
@@ -59,6 +69,10 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
|
|||||||
private final NoteWritePlatformService noteWritePlatformService;
|
private final NoteWritePlatformService noteWritePlatformService;
|
||||||
private final ExternalIdFactory externalIdFactory;
|
private final ExternalIdFactory externalIdFactory;
|
||||||
private final LoanBuyDownFeesBalanceRepository loanBuyDownFeesBalanceRepository;
|
private final LoanBuyDownFeesBalanceRepository loanBuyDownFeesBalanceRepository;
|
||||||
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
|
private final LoanBalanceService loanBalanceService;
|
||||||
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
|
private final BusinessEventNotifierService businessEventNotifierService;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
@@ -92,7 +106,7 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
|
|||||||
// Save transaction
|
// Save transaction
|
||||||
loanTransactionRepository.saveAndFlush(buyDownFeeTransaction);
|
loanTransactionRepository.saveAndFlush(buyDownFeeTransaction);
|
||||||
|
|
||||||
// Create Buy Down Fee balances
|
// Create Buy Down Fee balance
|
||||||
createBuyDownFeeBalance(buyDownFeeTransaction);
|
createBuyDownFeeBalance(buyDownFeeTransaction);
|
||||||
|
|
||||||
// Update loan derived fields
|
// Update loan derived fields
|
||||||
@@ -107,6 +121,10 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
|
|||||||
// Post journal entries
|
// Post journal entries
|
||||||
loanJournalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
loanJournalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
|
// Notify business events
|
||||||
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanBuyDownFeeTransactionCreatedBusinessEvent(buyDownFeeTransaction));
|
||||||
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder() //
|
return new CommandProcessingResultBuilder() //
|
||||||
.withClientId(loan.getClientId()) //
|
.withClientId(loan.getClientId()) //
|
||||||
.withOfficeId(loan.getOfficeId()) //
|
.withOfficeId(loan.getOfficeId()) //
|
||||||
@@ -116,6 +134,90 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult buyDownFeeAdjustment(final Long loanId, final Long buyDownFeeTransactionId, final JsonCommand command) {
|
||||||
|
this.loanTransactionValidator.validateBuyDownFeeAdjustment(command, loanId, buyDownFeeTransactionId);
|
||||||
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
|
checkClientOrGroupActive(loan);
|
||||||
|
|
||||||
|
final List<Long> existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan));
|
||||||
|
final List<Long> existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan));
|
||||||
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Create payment details
|
||||||
|
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
||||||
|
|
||||||
|
// Extract transaction details
|
||||||
|
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
|
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
|
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, "externalId");
|
||||||
|
|
||||||
|
// Find and validate original buy down fee transaction
|
||||||
|
Optional<LoanTransaction> originalBuyDownFee = loanTransactionRepository.findById(buyDownFeeTransactionId);
|
||||||
|
if (originalBuyDownFee.isEmpty() || !originalBuyDownFee.get().isBuyDownFee()) {
|
||||||
|
throw new IllegalArgumentException("Original transaction must be a valid Buy Down Fee transaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create buy down fee adjustment transaction
|
||||||
|
LoanTransaction buyDownFeeAdjustment = LoanTransaction.buyDownFeeAdjustment(loan, Money.of(loan.getCurrency(), transactionAmount),
|
||||||
|
paymentDetail, transactionDate, txnExternalId);
|
||||||
|
|
||||||
|
// Link to original transaction
|
||||||
|
buyDownFeeAdjustment.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(buyDownFeeAdjustment,
|
||||||
|
originalBuyDownFee.get(), LoanTransactionRelationTypeEnum.ADJUSTMENT));
|
||||||
|
|
||||||
|
// Add transaction to loan
|
||||||
|
loan.addLoanTransaction(buyDownFeeAdjustment);
|
||||||
|
|
||||||
|
// Recalculate loan transactions
|
||||||
|
recalculateLoanTransactions(loan, transactionDate, buyDownFeeAdjustment);
|
||||||
|
|
||||||
|
// Save transaction
|
||||||
|
LoanTransaction savedBuyDownFeeAdjustment = loanTransactionRepository.saveAndFlush(buyDownFeeAdjustment);
|
||||||
|
|
||||||
|
// Update buy down fee balance
|
||||||
|
LoanBuyDownFeeBalance buydownFeeBalance = loanBuyDownFeesBalanceRepository.findByLoanIdAndLoanTransactionId(loanId,
|
||||||
|
buyDownFeeTransactionId);
|
||||||
|
if (buydownFeeBalance != null) {
|
||||||
|
buydownFeeBalance.setAmountAdjustment(MathUtil.nullToZero(buydownFeeBalance.getAmountAdjustment()).add(transactionAmount));
|
||||||
|
buydownFeeBalance
|
||||||
|
.setUnrecognizedAmount(MathUtil.negativeToZero(buydownFeeBalance.getUnrecognizedAmount().subtract(transactionAmount)));
|
||||||
|
loanBuyDownFeesBalanceRepository.save(buydownFeeBalance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update outstanding loan balances
|
||||||
|
loanBalanceService.updateLoanOutstandingBalances(loan);
|
||||||
|
|
||||||
|
// Create a note if provided
|
||||||
|
final String noteText = command.stringValueOfParameterNamed("note");
|
||||||
|
if (StringUtils.isNotBlank(noteText)) {
|
||||||
|
noteWritePlatformService.createLoanTransactionNote(savedBuyDownFeeAdjustment.getId(), noteText);
|
||||||
|
}
|
||||||
|
// Post journal entries
|
||||||
|
loanJournalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
|
// Determine loan lifecycle transition
|
||||||
|
loanLifecycleStateMachine.determineAndTransition(loan, transactionDate);
|
||||||
|
|
||||||
|
// Notify business events
|
||||||
|
businessEventNotifierService
|
||||||
|
.notifyPostBusinessEvent(new LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent(savedBuyDownFeeAdjustment));
|
||||||
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(savedBuyDownFeeAdjustment.getId())
|
||||||
|
.withEntityExternalId(savedBuyDownFeeAdjustment.getExternalId()).withOfficeId(loan.getOfficeId())
|
||||||
|
.withClientId(loan.getClientId()).withLoanId(loan.getId()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction transaction) {
|
||||||
|
if (loan.isInterestBearingAndInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transactionDate)) {
|
||||||
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
|
} else {
|
||||||
|
reprocessLoanTransactionsService.processLatestTransaction(transaction, loan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void checkClientOrGroupActive(final Loan loan) {
|
private void checkClientOrGroupActive(final Loan loan) {
|
||||||
final Client client = loan.client();
|
final Client client = loan.client();
|
||||||
if (client != null && client.isNotActive()) {
|
if (client != null && client.isNotActive()) {
|
||||||
|
|||||||
+2
@@ -30,4 +30,6 @@ public interface ProgressiveLoanTransactionValidator extends LoanTransactionVali
|
|||||||
void validateContractTerminationUndo(JsonCommand command, Long loanId);
|
void validateContractTerminationUndo(JsonCommand command, Long loanId);
|
||||||
|
|
||||||
void validateBuyDownFee(JsonCommand command, Long loanId);
|
void validateBuyDownFee(JsonCommand command, Long loanId);
|
||||||
|
|
||||||
|
void validateBuyDownFeeAdjustment(JsonCommand command, Long loanId, Long buyDownFeeTransactionId);
|
||||||
}
|
}
|
||||||
|
|||||||
+96
@@ -48,6 +48,7 @@ import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
|||||||
import org.apache.fineract.portfolio.common.service.Validator;
|
import org.apache.fineract.portfolio.common.service.Validator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
||||||
@@ -57,6 +58,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingException;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeesBalanceRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator;
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||||
@@ -69,6 +71,7 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
|
|||||||
private final LoanTransactionValidator loanTransactionValidator;
|
private final LoanTransactionValidator loanTransactionValidator;
|
||||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||||
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
||||||
|
private final LoanBuyDownFeesBalanceRepository loanBuydownFeeBalanceRepository;
|
||||||
private final LoanTransactionRepository loanTransactionRepository;
|
private final LoanTransactionRepository loanTransactionRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -320,6 +323,94 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateBuyDownFeeAdjustment(JsonCommand command, Long loanId, Long buyDownFeeTransactionId) {
|
||||||
|
final String json = command.json();
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, getBuyDownFeeAdjustmentParameters());
|
||||||
|
|
||||||
|
Validator.validateOrThrow("loan.buyDownFeeAdjustment", baseDataValidator -> {
|
||||||
|
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||||
|
validateLoanClientIsActive(loan);
|
||||||
|
validateLoanGroupIsActive(loan);
|
||||||
|
|
||||||
|
// Validate loan is progressive
|
||||||
|
if (!loan.isProgressiveSchedule()) {
|
||||||
|
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.progressive.loan");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate buy down fee is enabled
|
||||||
|
if (!loan.getLoanProductRelatedDetail().isEnableBuyDownFee()) {
|
||||||
|
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("buy.down.fee.not.enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate loan is active, or closed or overpaid
|
||||||
|
final LoanStatus loanStatus = loan.getStatus();
|
||||||
|
if (!loanStatus.isActive() && !loanStatus.isClosed() && !loanStatus.isOverpaid()) {
|
||||||
|
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.valid.loan.status");
|
||||||
|
}
|
||||||
|
|
||||||
|
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||||
|
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||||
|
|
||||||
|
// Validate transaction date is not before disbursement date
|
||||||
|
if (transactionDate != null && loan.getDisbursementDate() != null && transactionDate.isBefore(loan.getDisbursementDate())) {
|
||||||
|
baseDataValidator.reset().parameter("transactionDate").failWithCode("before.disbursement.date",
|
||||||
|
"Transaction date cannot be before disbursement date");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate transaction date is not in the future
|
||||||
|
if (transactionDate != null && transactionDate.isAfter(DateUtils.getBusinessLocalDate())) {
|
||||||
|
baseDataValidator.reset().parameter("transactionDate").failWithCode("cannot.be.in.the.future",
|
||||||
|
"Transaction date cannot be in the future");
|
||||||
|
}
|
||||||
|
|
||||||
|
final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element);
|
||||||
|
baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).notNull().positiveAmount();
|
||||||
|
|
||||||
|
Optional<LoanTransaction> buyDownFeeTransactionOpt = loanTransactionRepository.findById(buyDownFeeTransactionId);
|
||||||
|
if (buyDownFeeTransactionOpt.isEmpty()) {
|
||||||
|
baseDataValidator.reset().parameter("buyDownFeeTransactionId").failWithCode("loan.transaction.not.found",
|
||||||
|
"Buy Down Fee transaction not found.");
|
||||||
|
} else {
|
||||||
|
// Validate that the transaction is actually a buy down fee transaction
|
||||||
|
if (!buyDownFeeTransactionOpt.get().isBuyDownFee()) {
|
||||||
|
baseDataValidator.reset().parameter("buyDownFeeTransactionId").failWithCode("not.buyDownFee.transaction",
|
||||||
|
"The specified transaction is not a Buy Down Fee transaction.");
|
||||||
|
}
|
||||||
|
// Validate not before buy down fee transaction
|
||||||
|
if (transactionDate != null && transactionDate.isBefore(buyDownFeeTransactionOpt.get().getTransactionDate())) {
|
||||||
|
baseDataValidator.reset().parameter("transactionDate").failWithCode("before.buyDownFee.transaction.date",
|
||||||
|
"Transaction date cannot be before buy down fee transaction date");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (transactionAmount != null) {
|
||||||
|
LoanBuyDownFeeBalance buydownFeeBalance = loanBuydownFeeBalanceRepository.findByLoanIdAndLoanTransactionId(loanId,
|
||||||
|
buyDownFeeTransactionId);
|
||||||
|
if (buydownFeeBalance == null) {
|
||||||
|
baseDataValidator.reset().parameter("buyDownFeeTransactionId").failWithCode("buydown.fee.balance.not.found",
|
||||||
|
"Buy down fee balance not found for the specified transaction.");
|
||||||
|
} else if (MathUtil.isLessThan(
|
||||||
|
buydownFeeBalance.getAmount().subtract(MathUtil.nullToZero(buydownFeeBalance.getAmountAdjustment())),
|
||||||
|
transactionAmount)) {
|
||||||
|
baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).failWithCode(
|
||||||
|
"cannot.be.more.than.remaining.amount",
|
||||||
|
" Buy down fee adjustment amount cannot be more than remaining amount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validatePaymentDetails(baseDataValidator, element);
|
||||||
|
validateNote(baseDataValidator, element);
|
||||||
|
validateExternalId(baseDataValidator, element);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||||
if (!dataValidationErrors.isEmpty()) {
|
if (!dataValidationErrors.isEmpty()) {
|
||||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||||
@@ -495,6 +586,11 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
|
|||||||
return new HashSet<>(Arrays.asList("note", "reversalExternalId"));
|
return new HashSet<>(Arrays.asList("note", "reversalExternalId"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<String> getBuyDownFeeAdjustmentParameters() {
|
||||||
|
return new HashSet<>(
|
||||||
|
Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId"));
|
||||||
|
}
|
||||||
|
|
||||||
private BigDecimal getOverAppliedMax(final Loan loan) {
|
private BigDecimal getOverAppliedMax(final Loan loan) {
|
||||||
final LoanProduct loanProduct = loan.getLoanProduct();
|
final LoanProduct loanProduct = loan.getLoanProduct();
|
||||||
if ("percentage".equals(loanProduct.getOverAppliedCalculationType())) {
|
if ("percentage".equals(loanProduct.getOverAppliedCalculationType())) {
|
||||||
|
|||||||
+3
-2
@@ -24,6 +24,7 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeesBalanceRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator;
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.CapitalizedIncomeBalanceService;
|
import org.apache.fineract.portfolio.loanaccount.service.CapitalizedIncomeBalanceService;
|
||||||
@@ -65,9 +66,9 @@ public class ProgressiveLoanAccountConfiguration {
|
|||||||
public ProgressiveLoanTransactionValidator progressiveLoanTransactionValidator(FromJsonHelper fromApiJsonHelper,
|
public ProgressiveLoanTransactionValidator progressiveLoanTransactionValidator(FromJsonHelper fromApiJsonHelper,
|
||||||
LoanTransactionValidator loanTransactionValidator, LoanRepositoryWrapper loanRepositoryWrapper,
|
LoanTransactionValidator loanTransactionValidator, LoanRepositoryWrapper loanRepositoryWrapper,
|
||||||
LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository,
|
LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository,
|
||||||
LoanTransactionRepository loanTransactionRepository) {
|
LoanBuyDownFeesBalanceRepository loanBuydownFeesBalanceRepository, LoanTransactionRepository loanTransactionRepository) {
|
||||||
return new ProgressiveLoanTransactionValidatorImpl(fromApiJsonHelper, loanTransactionValidator, loanRepositoryWrapper,
|
return new ProgressiveLoanTransactionValidatorImpl(fromApiJsonHelper, loanTransactionValidator, loanRepositoryWrapper,
|
||||||
loanCapitalizedIncomeBalanceRepository, loanTransactionRepository);
|
loanCapitalizedIncomeBalanceRepository, loanBuydownFeesBalanceRepository, loanTransactionRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
+26
@@ -149,6 +149,10 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
if (transactionType.isBuyDownFee()) {
|
if (transactionType.isBuyDownFee()) {
|
||||||
createJournalEntriesForBuyDownFee(loanDTO, loanTransactionDTO, office);
|
createJournalEntriesForBuyDownFee(loanDTO, loanTransactionDTO, office);
|
||||||
}
|
}
|
||||||
|
// Handle Buy Down Fee Adjustment
|
||||||
|
if (transactionType.isBuyDownFeeAdjustment()) {
|
||||||
|
createJournalEntriesForBuyDownFeeAdjustment(loanDTO, loanTransactionDTO, office);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +440,28 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createJournalEntriesForBuyDownFeeAdjustment(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
|
||||||
|
final Office office) {
|
||||||
|
// loan properties
|
||||||
|
final Long loanProductId = loanDTO.getLoanProductId();
|
||||||
|
final Long loanId = loanDTO.getLoanId();
|
||||||
|
final String currencyCode = loanDTO.getCurrencyCode();
|
||||||
|
|
||||||
|
// transaction properties
|
||||||
|
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||||
|
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
||||||
|
final BigDecimal amount = loanTransactionDTO.getAmount();
|
||||||
|
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||||
|
|
||||||
|
if (MathUtil.isGreaterThanZero(amount)) {
|
||||||
|
// Mirror of Buy Down Fee entries (as per PS-2574 requirements)
|
||||||
|
// Debit: Deferred Income Liability, Credit: Buy Down Expense
|
||||||
|
this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
|
||||||
|
AccrualAccountsForLoan.BUY_DOWN_EXPENSE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
|
||||||
|
transactionDate, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createJournalEntriesForInterestPaymentWaiverOrInterestRefund(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO,
|
private void createJournalEntriesForInterestPaymentWaiverOrInterestRefund(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO,
|
||||||
Office office) {
|
Office office) {
|
||||||
final Long loanProductId = loanDTO.getLoanProductId();
|
final Long loanProductId = loanDTO.getLoanProductId();
|
||||||
|
|||||||
+2
@@ -710,6 +710,8 @@ public class LoanTransactionsApiResource {
|
|||||||
commandRequest = builder.chargebackTransaction(resolvedLoanId, resolvedTransactionId).build();
|
commandRequest = builder.chargebackTransaction(resolvedLoanId, resolvedTransactionId).build();
|
||||||
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND)) {
|
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND)) {
|
||||||
commandRequest = builder.capitalizedIncomeAdjustment(resolvedLoanId, resolvedTransactionId).build();
|
commandRequest = builder.capitalizedIncomeAdjustment(resolvedLoanId, resolvedTransactionId).build();
|
||||||
|
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.BUY_DOWN_FEE_ADJUSTMENT_COMMAND)) {
|
||||||
|
commandRequest = builder.buyDownFeeAdjustment(resolvedLoanId, resolvedTransactionId).build();
|
||||||
} else { // Default to adjust the Loan Transaction
|
} else { // Default to adjust the Loan Transaction
|
||||||
commandRequest = builder.adjustTransaction(resolvedLoanId, resolvedTransactionId).build();
|
commandRequest = builder.adjustTransaction(resolvedLoanId, resolvedTransactionId).build();
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -383,10 +383,13 @@ public class LoanAccountConfiguration {
|
|||||||
LoanAssembler loanAssembler, LoanTransactionRepository loanTransactionRepository,
|
LoanAssembler loanAssembler, LoanTransactionRepository loanTransactionRepository,
|
||||||
PaymentDetailWritePlatformService paymentDetailWritePlatformService, LoanJournalEntryPoster loanJournalEntryPoster,
|
PaymentDetailWritePlatformService paymentDetailWritePlatformService, LoanJournalEntryPoster loanJournalEntryPoster,
|
||||||
NoteWritePlatformService noteWritePlatformService, ExternalIdFactory externalIdFactory,
|
NoteWritePlatformService noteWritePlatformService, ExternalIdFactory externalIdFactory,
|
||||||
LoanBuyDownFeesBalanceRepository loanBuyDownFeesBalanceRepository) {
|
LoanBuyDownFeesBalanceRepository loanBuyDownFeesBalanceRepository,
|
||||||
|
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanBalanceService loanBalanceService,
|
||||||
|
LoanLifecycleStateMachine loanLifecycleStateMachine, BusinessEventNotifierService businessEventNotifierService) {
|
||||||
return new BuyDownFeeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
|
return new BuyDownFeeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
|
||||||
paymentDetailWritePlatformService, loanJournalEntryPoster, noteWritePlatformService, externalIdFactory,
|
paymentDetailWritePlatformService, loanJournalEntryPoster, noteWritePlatformService, externalIdFactory,
|
||||||
loanBuyDownFeesBalanceRepository);
|
loanBuyDownFeesBalanceRepository, reprocessLoanTransactionsService, loanBalanceService, loanLifecycleStateMachine,
|
||||||
|
businessEventNotifierService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -207,4 +207,5 @@
|
|||||||
<include file="parts/0186_add_buy_down_fee_to_loan_product.xml" relativeToChangelogFile="true" />
|
<include file="parts/0186_add_buy_down_fee_to_loan_product.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0187_add_Loan_modified_and_withdrawn_event_configuration.xml" relativeToChangelogFile="true" />
|
<include file="parts/0187_add_Loan_modified_and_withdrawn_event_configuration.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0188_create_loan_buy_down_fee_balance.xml" relativeToChangelogFile="true" />
|
<include file="parts/0188_create_loan_buy_down_fee_balance.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="parts/0189_add_loan_buydown_fee_event.xml" relativeToChangelogFile="true" />
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||||
|
<changeSet id="1" author="fineract">
|
||||||
|
<insert tableName="m_external_event_configuration">
|
||||||
|
<column name="type" value="LoanBuyDownFeeTransactionCreatedBusinessEvent"/>
|
||||||
|
<column name="enabled" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="m_external_event_configuration">
|
||||||
|
<column name="type" value="LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent"/>
|
||||||
|
<column name="enabled" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+4
-2
@@ -109,7 +109,8 @@ public class ExternalEventConfigurationValidationServiceTest {
|
|||||||
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
||||||
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
||||||
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "LoanUndoContractTerminationBusinessEvent");
|
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "LoanUndoContractTerminationBusinessEvent",
|
||||||
|
"LoanBuyDownFeeTransactionCreatedBusinessEvent", "LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent");
|
||||||
|
|
||||||
List<FineractPlatformTenant> tenants = Arrays
|
List<FineractPlatformTenant> tenants = Arrays
|
||||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||||
@@ -200,7 +201,8 @@ public class ExternalEventConfigurationValidationServiceTest {
|
|||||||
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
||||||
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
||||||
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "LoanUndoContractTerminationBusinessEvent");
|
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "LoanUndoContractTerminationBusinessEvent",
|
||||||
|
"LoanBuyDownFeeTransactionCreatedBusinessEvent", "LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent");
|
||||||
|
|
||||||
List<FineractPlatformTenant> tenants = Arrays
|
List<FineractPlatformTenant> tenants = Arrays
|
||||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||||
|
|||||||
+164
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -32,6 +33,7 @@ import org.apache.fineract.client.models.PostLoanProductsRequest;
|
|||||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||||
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansResponse;
|
import org.apache.fineract.client.models.PostLoansResponse;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.apache.fineract.integrationtests.common.Utils;
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
@@ -243,6 +245,168 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
.buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE).locale("en").dateFormat("dd MMMM yyyy");
|
.buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE).locale("en").dateFormat("dd MMMM yyyy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuyDownFeeAdjustment() {
|
||||||
|
runAt("06 September 2024", () -> {
|
||||||
|
// Add initial buy down fee
|
||||||
|
Long buyDownFeeTransactionId = addBuyDownFeeForLoan(loanId, 500.0, "06 September 2024");
|
||||||
|
assertNotNull(buyDownFeeTransactionId);
|
||||||
|
|
||||||
|
// Create buy down fee adjustment (use same date as business date)
|
||||||
|
PostLoansLoanIdTransactionsResponse adjustmentResponse = loanTransactionHelper.buyDownFeeAdjustment(loanId,
|
||||||
|
buyDownFeeTransactionId, "06 September 2024", 100.0);
|
||||||
|
|
||||||
|
assertNotNull(adjustmentResponse);
|
||||||
|
assertNotNull(adjustmentResponse.getLoanId());
|
||||||
|
assertNotNull(adjustmentResponse.getClientId());
|
||||||
|
assertNotNull(adjustmentResponse.getOfficeId());
|
||||||
|
assertEquals(loanId, adjustmentResponse.getLoanId());
|
||||||
|
|
||||||
|
// Verify loan details show both transactions
|
||||||
|
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
assertNotNull(loanDetails);
|
||||||
|
|
||||||
|
List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
|
||||||
|
assertTrue(transactions.size() >= 3); // Disbursement, Buy Down Fee, Buy Down Fee Adjustment
|
||||||
|
|
||||||
|
// Find the buy down fee adjustment transaction
|
||||||
|
GetLoansLoanIdTransactions adjustmentTransaction = transactions.stream()
|
||||||
|
.filter(txn -> "Buy Down Fee Adjustment".equals(txn.getType().getValue())).findFirst().orElse(null);
|
||||||
|
|
||||||
|
assertNotNull(adjustmentTransaction);
|
||||||
|
assertEquals(0, BigDecimal.valueOf(100.0).compareTo(adjustmentTransaction.getAmount()));
|
||||||
|
assertEquals("06 September 2024", adjustmentTransaction.getDate().format(DateTimeFormatter.ofPattern("dd MMMM yyyy")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuyDownFeeAdjustmentValidations() {
|
||||||
|
runAt("08 September 2024", () -> {
|
||||||
|
// Add initial buy down fee
|
||||||
|
Long buyDownFeeTransactionId = addBuyDownFeeForLoan(loanId, 300.0, "08 September 2024");
|
||||||
|
assertNotNull(buyDownFeeTransactionId);
|
||||||
|
|
||||||
|
// Test 1: Adjustment amount more than original amount (should fail)
|
||||||
|
try {
|
||||||
|
loanTransactionHelper.buyDownFeeAdjustment(loanId, buyDownFeeTransactionId, "08 September 2024", 400.0);
|
||||||
|
assertTrue(false, "Expected validation error for adjustment amount exceeding original amount");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.info("Expected validation error for excessive adjustment amount: {}", e.getMessage());
|
||||||
|
assertTrue(e.getMessage().contains("amount") || e.getMessage().contains("exceed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Adjustment date before original transaction date (should fail)
|
||||||
|
try {
|
||||||
|
loanTransactionHelper.buyDownFeeAdjustment(loanId, buyDownFeeTransactionId, "07 September 2024", 100.0);
|
||||||
|
assertTrue(false, "Expected validation error for adjustment date before original transaction date");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.info("Expected validation error for early adjustment date: {}", e.getMessage());
|
||||||
|
assertTrue(e.getMessage().contains("date") || e.getMessage().contains("before"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Valid adjustment should succeed
|
||||||
|
PostLoansLoanIdTransactionsResponse validAdjustment = loanTransactionHelper.buyDownFeeAdjustment(loanId,
|
||||||
|
buyDownFeeTransactionId, "08 September 2024", 150.0);
|
||||||
|
assertNotNull(validAdjustment);
|
||||||
|
|
||||||
|
// Test 4: Second adjustment that would exceed total should fail
|
||||||
|
try {
|
||||||
|
loanTransactionHelper.buyDownFeeAdjustment(loanId, buyDownFeeTransactionId, "08 September 2024", 200.0);
|
||||||
|
assertTrue(false, "Expected validation error for total adjustments exceeding original amount");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.info("Expected validation error for cumulative adjustment excess: {}", e.getMessage());
|
||||||
|
assertTrue(e.getMessage().contains("amount") || e.getMessage().contains("exceed"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuyDownFeeAdjustmentAccountingEntries() {
|
||||||
|
runAt("10 September 2024", () -> {
|
||||||
|
// Add initial buy down fee
|
||||||
|
Long buyDownFeeTransactionId = addBuyDownFeeForLoan(loanId, 400.0, "10 September 2024");
|
||||||
|
assertNotNull(buyDownFeeTransactionId);
|
||||||
|
|
||||||
|
// Verify initial buy down fee accounting entries
|
||||||
|
verifyTRJournalEntries(buyDownFeeTransactionId, debit(buyDownExpenseAccount, 400.0),
|
||||||
|
credit(deferredIncomeLiabilityAccount, 400.0));
|
||||||
|
|
||||||
|
// Create buy down fee adjustment
|
||||||
|
PostLoansLoanIdTransactionsResponse adjustmentResponse = loanTransactionHelper.buyDownFeeAdjustment(loanId,
|
||||||
|
buyDownFeeTransactionId, "10 September 2024", 120.0);
|
||||||
|
assertNotNull(adjustmentResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleBuyDownFeeAdjustments() {
|
||||||
|
runAt("12 September 2024", () -> {
|
||||||
|
// Add initial buy down fee
|
||||||
|
Long buyDownFeeTransactionId = addBuyDownFeeForLoan(loanId, 600.0, "12 September 2024");
|
||||||
|
assertNotNull(buyDownFeeTransactionId);
|
||||||
|
|
||||||
|
// First adjustment
|
||||||
|
PostLoansLoanIdTransactionsResponse adjustment1 = loanTransactionHelper.buyDownFeeAdjustment(loanId, buyDownFeeTransactionId,
|
||||||
|
"12 September 2024", 100.0);
|
||||||
|
assertNotNull(adjustment1);
|
||||||
|
|
||||||
|
// Second adjustment
|
||||||
|
PostLoansLoanIdTransactionsResponse adjustment2 = loanTransactionHelper.buyDownFeeAdjustment(loanId, buyDownFeeTransactionId,
|
||||||
|
"12 September 2024", 150.0);
|
||||||
|
assertNotNull(adjustment2);
|
||||||
|
|
||||||
|
// Verify both adjustments are recorded
|
||||||
|
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
assertNotNull(loanDetails);
|
||||||
|
|
||||||
|
List<GetLoansLoanIdTransactions> adjustmentTransactions = loanDetails.getTransactions().stream()
|
||||||
|
.filter(txn -> "Buy Down Fee Adjustment".equals(txn.getType().getValue())).toList();
|
||||||
|
|
||||||
|
assertEquals(2, adjustmentTransactions.size());
|
||||||
|
|
||||||
|
// Verify total adjustment amounts
|
||||||
|
BigDecimal totalAdjustments = adjustmentTransactions.stream().map(GetLoansLoanIdTransactions::getAmount).reduce(BigDecimal.ZERO,
|
||||||
|
BigDecimal::add);
|
||||||
|
assertEquals(0, BigDecimal.valueOf(250.0).compareTo(totalAdjustments));
|
||||||
|
|
||||||
|
// Third adjustment that would exceed limit should fail
|
||||||
|
try {
|
||||||
|
loanTransactionHelper.buyDownFeeAdjustment(loanId, buyDownFeeTransactionId, "12 September 2024", 400.0);
|
||||||
|
assertTrue(false, "Expected validation error for total adjustments exceeding original amount");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.info("Expected validation error for cumulative adjustments exceeding limit: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuyDownFeeAdjustmentWithExternalId() {
|
||||||
|
runAt("16 September 2024", () -> {
|
||||||
|
// Add initial buy down fee
|
||||||
|
Long buyDownFeeTransactionId = addBuyDownFeeForLoan(loanId, 350.0, "16 September 2024");
|
||||||
|
assertNotNull(buyDownFeeTransactionId);
|
||||||
|
|
||||||
|
// Create adjustment with external ID
|
||||||
|
String adjustmentExternalId = UUID.randomUUID().toString();
|
||||||
|
PostLoansLoanIdTransactionsResponse adjustmentResponse = loanTransactionHelper.buyDownFeeAdjustment(loanId,
|
||||||
|
buyDownFeeTransactionId,
|
||||||
|
new PostLoansLoanIdTransactionsTransactionIdRequest().transactionDate("16 September 2024").transactionAmount(80.0)
|
||||||
|
.externalId(adjustmentExternalId).note("Buy Down Fee Adjustment with external ID").dateFormat("dd MMMM yyyy")
|
||||||
|
.locale("en"));
|
||||||
|
|
||||||
|
assertNotNull(adjustmentResponse);
|
||||||
|
assertEquals(adjustmentExternalId, adjustmentResponse.getResourceExternalId());
|
||||||
|
|
||||||
|
// Verify adjustment transaction details
|
||||||
|
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
GetLoansLoanIdTransactions adjustmentTransaction = loanDetails.getTransactions().stream()
|
||||||
|
.filter(txn -> adjustmentExternalId.equals(txn.getExternalId())).findFirst().orElse(null);
|
||||||
|
|
||||||
|
assertNotNull(adjustmentTransaction);
|
||||||
|
assertEquals(0, BigDecimal.valueOf(80.0).compareTo(adjustmentTransaction.getAmount()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to add buy down fee for a loan
|
* Helper method to add buy down fee for a loan
|
||||||
*
|
*
|
||||||
|
|||||||
+10
@@ -636,6 +636,16 @@ public class ExternalEventConfigurationHelper {
|
|||||||
loanTransactionUndoContractTerminationBusinessEvent.put("enabled", false);
|
loanTransactionUndoContractTerminationBusinessEvent.put("enabled", false);
|
||||||
defaults.add(loanTransactionUndoContractTerminationBusinessEvent);
|
defaults.add(loanTransactionUndoContractTerminationBusinessEvent);
|
||||||
|
|
||||||
|
Map<String, Object> loanBuyDownFeeTransactionCreatedBusinessEvent = new HashMap<>();
|
||||||
|
loanBuyDownFeeTransactionCreatedBusinessEvent.put("type", "LoanBuyDownFeeTransactionCreatedBusinessEvent");
|
||||||
|
loanBuyDownFeeTransactionCreatedBusinessEvent.put("enabled", false);
|
||||||
|
defaults.add(loanBuyDownFeeTransactionCreatedBusinessEvent);
|
||||||
|
|
||||||
|
Map<String, Object> loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent = new HashMap<>();
|
||||||
|
loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent.put("type", "LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent");
|
||||||
|
loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent.put("enabled", false);
|
||||||
|
defaults.add(loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent);
|
||||||
|
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+30
@@ -963,6 +963,36 @@ public class LoanTransactionHelper {
|
|||||||
.transactionAmount(amount).transactionDate(transactionDate).dateFormat("dd MMMM yyyy").locale("en"));
|
.transactionAmount(amount).transactionDate(transactionDate).dateFormat("dd MMMM yyyy").locale("en"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PostLoansLoanIdTransactionsResponse buyDownFeeAdjustment(final Long loanId, final Long buyDownFeeTransactionId,
|
||||||
|
final PostLoansLoanIdTransactionsTransactionIdRequest request) {
|
||||||
|
return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.adjustLoanTransaction(loanId, buyDownFeeTransactionId,
|
||||||
|
request, "buyDownFeeAdjustment"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostLoansLoanIdTransactionsResponse buyDownFeeAdjustment(final String loanExternalId, final Long transactionId,
|
||||||
|
final PostLoansLoanIdTransactionsTransactionIdRequest request) {
|
||||||
|
return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.adjustLoanTransaction2(loanExternalId, transactionId,
|
||||||
|
request, "buyDownFeeAdjustment"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostLoansLoanIdTransactionsResponse buyDownFeeAdjustment(final String loanExternalId, final String transactionExternalId,
|
||||||
|
final PostLoansLoanIdTransactionsTransactionIdRequest request) {
|
||||||
|
return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.adjustLoanTransaction3(loanExternalId,
|
||||||
|
transactionExternalId, request, "buyDownFeeAdjustment"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostLoansLoanIdTransactionsResponse buyDownFeeAdjustment(final Long loanId, final String transactionExternalId,
|
||||||
|
final PostLoansLoanIdTransactionsTransactionIdRequest request) {
|
||||||
|
return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.adjustLoanTransaction1(loanId, transactionExternalId,
|
||||||
|
request, "buyDownFeeAdjustment"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostLoansLoanIdTransactionsResponse buyDownFeeAdjustment(final Long loanId, final Long buyDownFeeTransactionId,
|
||||||
|
final String transactionDate, final double amount) {
|
||||||
|
return buyDownFeeAdjustment(loanId, buyDownFeeTransactionId, new PostLoansLoanIdTransactionsTransactionIdRequest()
|
||||||
|
.transactionAmount(amount).transactionDate(transactionDate).dateFormat("dd MMMM yyyy").locale("en"));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Rewrite to use fineract-client instead!
|
// TODO: Rewrite to use fineract-client instead!
|
||||||
// Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
|
// Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
|
||||||
// org.apache.fineract.client.models.PostLoansLoanIdRequest)
|
// org.apache.fineract.client.models.PostLoansLoanIdRequest)
|
||||||
|
|||||||
Reference in New Issue
Block a user