FINERACT-2104: Accrual Activity - Support final Accrual Activity transaction creation at the event of the loan got fully paid
This commit is contained in:
+9
@@ -394,4 +394,13 @@ public final class DateUtils {
|
||||
return formatter;
|
||||
}
|
||||
|
||||
public static boolean occursOnDayFromExclusiveAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
|
||||
final LocalDate target) {
|
||||
return DateUtils.isAfter(target, fromNotInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
|
||||
}
|
||||
|
||||
public static boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive,
|
||||
final LocalDate target) {
|
||||
return target != null && !DateUtils.isBefore(target, fromAndInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -3877,7 +3877,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
public LocalDate getLastUserTransactionDate() {
|
||||
LocalDate currentTransactionDate = getDisbursementDate();
|
||||
for (final LoanTransaction previousTransaction : this.loanTransactions) {
|
||||
if (!(previousTransaction.isReversed() || previousTransaction.isAccrual() || previousTransaction.isIncomePosting())
|
||||
if (!(previousTransaction.isReversed() || previousTransaction.isAccrual() || previousTransaction.isIncomePosting()
|
||||
|| previousTransaction.isAccrualActivity())
|
||||
&& DateUtils.isBefore(currentTransactionDate, previousTransaction.getTransactionDate())) {
|
||||
currentTransactionDate = previousTransaction.getTransactionDate();
|
||||
}
|
||||
|
||||
+2
-12
@@ -618,22 +618,12 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
public boolean isDueForCollectionFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) {
|
||||
final LocalDate dueDate = getDueLocalDate();
|
||||
return occursOnDayFromExclusiveAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, dueDate);
|
||||
return DateUtils.occursOnDayFromExclusiveAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, dueDate);
|
||||
}
|
||||
|
||||
public boolean isDueForCollectionFromIncludingAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive) {
|
||||
final LocalDate dueDate = getDueLocalDate();
|
||||
return occursOnDayFromAndUpToAndIncluding(fromAndInclusive, upToAndInclusive, dueDate);
|
||||
}
|
||||
|
||||
private boolean occursOnDayFromExclusiveAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
|
||||
final LocalDate target) {
|
||||
return DateUtils.isAfter(target, fromNotInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
|
||||
}
|
||||
|
||||
private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive,
|
||||
final LocalDate target) {
|
||||
return target != null && !DateUtils.isBefore(target, fromAndInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
|
||||
return DateUtils.occursOnDayFromAndUpToAndIncluding(fromAndInclusive, upToAndInclusive, dueDate);
|
||||
}
|
||||
|
||||
public boolean isFeeCharge() {
|
||||
|
||||
+15
-8
@@ -220,13 +220,20 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
Money interestPortion = Money.zero(currency);
|
||||
Money feeChargesPortion = Money.zero(currency);
|
||||
Money penaltychargesPortion = Money.zero(currency);
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
if (loanTransaction.getDateOf().isEqual(currentInstallment.getDueDate())) {
|
||||
interestPortion = interestPortion.plus(currentInstallment.getInterestCharged(currency));
|
||||
feeChargesPortion = feeChargesPortion.plus(currentInstallment.getFeeChargesCharged(currency));
|
||||
penaltychargesPortion = penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
|
||||
}
|
||||
}
|
||||
final int firstNormalInstallmentNumber = LoanRepaymentScheduleProcessingWrapper.fetchFirstNormalInstallmentNumber(installments);
|
||||
|
||||
final LoanRepaymentScheduleInstallment currentInstallment = installments.stream()
|
||||
.filter(installment -> installment.getInstallmentNumber().equals(firstNormalInstallmentNumber)
|
||||
? DateUtils.occursOnDayFromExclusiveAndUpToAndIncluding(installment.getFromDate(), installment.getDueDate(),
|
||||
loanTransaction.getTransactionDate())
|
||||
: DateUtils.occursOnDayFromAndUpToAndIncluding(installment.getFromDate(), installment.getDueDate(),
|
||||
loanTransaction.getTransactionDate()))
|
||||
.findFirst().orElseThrow();
|
||||
|
||||
interestPortion = interestPortion.plus(currentInstallment.getInterestCharged(currency));
|
||||
feeChargesPortion = feeChargesPortion.plus(currentInstallment.getFeeChargesCharged(currency));
|
||||
penaltychargesPortion = penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
|
||||
|
||||
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
|
||||
}
|
||||
|
||||
@@ -602,7 +609,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
installmentNumber = installments.get(0).getInstallmentNumber();
|
||||
}
|
||||
|
||||
if (loanTransaction.isNotWaiver() && !loanTransaction.isAccrual()) {
|
||||
if (loanTransaction.isNotWaiver() && !loanTransaction.isAccrual() && !loanTransaction.isAccrualActivity()) {
|
||||
Money feeCharges = loanTransaction.getFeeChargesPortion(currency);
|
||||
Money penaltyCharges = loanTransaction.getPenaltyChargesPortion(currency);
|
||||
if (chargeAmountToProcess != null && feeCharges.isGreaterThan(chargeAmountToProcess)) {
|
||||
|
||||
+5
@@ -29,4 +29,9 @@ public interface LoanAccrualActivityProcessingService {
|
||||
|
||||
Loan makeAccrualActivityTransaction(Loan loan, LocalDate currentDate);
|
||||
|
||||
@Transactional
|
||||
void processAccrualActivityForLoanClosure(Loan loan);
|
||||
|
||||
@Transactional
|
||||
void processAccrualActivityForLoanReopen(Loan loan);
|
||||
}
|
||||
|
||||
+7
@@ -53,9 +53,16 @@ public interface LoanWritePlatformService {
|
||||
CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(LoanTransactionType repaymentTransactionType, Long loanId,
|
||||
JsonCommand command, boolean isRecoveryRepayment, String chargeRefundChargeType);
|
||||
|
||||
@Transactional
|
||||
Loan reverseReplayAccrualActivityTransaction(Loan loan, LoanTransaction loanTransaction, LoanRepaymentScheduleInstallment installment,
|
||||
LocalDate transactionDate);
|
||||
|
||||
@Transactional
|
||||
Loan makeAccrualActivityTransaction(Loan loan, LoanRepaymentScheduleInstallment installment, LocalDate transactionDate);
|
||||
|
||||
@Transactional
|
||||
Loan makeAccrualActivityTransaction(Loan loan, LoanTransaction accrualActivityTransaction);
|
||||
|
||||
@Transactional
|
||||
CommandProcessingResult makeInterestPaymentWaiver(JsonCommand command);
|
||||
|
||||
|
||||
+104
@@ -18,15 +18,23 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -38,6 +46,8 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi
|
||||
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final LoanWritePlatformService loanWritePlatformService;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@@ -66,4 +76,98 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi
|
||||
return loan;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void processAccrualActivityForLoanClosure(Loan loan) {
|
||||
LocalDate date = loan.isOverPaid() ? loan.getOverpaidOnDate() : loan.getClosedOnDate();
|
||||
List<LoanTransaction> accrualActivityTransaction = loan.getLoanTransactions().stream().filter(LoanTransaction::isNotReversed)
|
||||
.filter(LoanTransaction::isAccrualActivity).filter(loanTransaction -> loanTransaction.getDateOf().isAfter(date)).toList();
|
||||
if (!accrualActivityTransaction.isEmpty()) {
|
||||
accrualActivityTransaction.forEach(this::reverseAccrualActivityTransaction);
|
||||
}
|
||||
LoanTransaction loanTransaction = assembleClosingAccrualActivityTransaction(loan, date);
|
||||
if (loanTransaction.getAmount().compareTo(BigDecimal.ZERO) != 0) {
|
||||
loanWritePlatformService.makeAccrualActivityTransaction(loan, loanTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
private void reverseAccrualActivityTransaction(LoanTransaction loanTransaction) {
|
||||
loanTransaction.reverse();
|
||||
LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
|
||||
}
|
||||
|
||||
private LoanTransaction assembleClosingAccrualActivityTransaction(Loan loan, LocalDate date) {
|
||||
// collect fees
|
||||
BigDecimal feeChargesPortion = BigDecimal.ZERO;
|
||||
// collect penalties
|
||||
BigDecimal penaltyChargesPortion = BigDecimal.ZERO;
|
||||
// collect interests
|
||||
BigDecimal interestPortion = BigDecimal.ZERO;
|
||||
var currency = loan.getCurrency();
|
||||
// sum up all accruals
|
||||
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
|
||||
feeChargesPortion = installment.getFeeAccrued(currency).getAmount().add(feeChargesPortion);
|
||||
penaltyChargesPortion = installment.getPenaltyAccrued(currency).getAmount().add(penaltyChargesPortion);
|
||||
interestPortion = installment.getInterestAccrued(currency).getAmount().add(interestPortion);
|
||||
}
|
||||
List<LoanTransaction> accrualActivities = loan.getLoanTransactions().stream().filter(LoanTransaction::isAccrualActivity)
|
||||
.filter(LoanTransaction::isNotReversed).toList();
|
||||
// subtract already Posted accruals
|
||||
for (LoanTransaction accrualActivity : accrualActivities) {
|
||||
if (accrualActivity.getFeeChargesPortion() != null) {
|
||||
feeChargesPortion = feeChargesPortion.subtract(accrualActivity.getFeeChargesPortion());
|
||||
}
|
||||
if (accrualActivity.getPenaltyChargesPortion() != null) {
|
||||
penaltyChargesPortion = penaltyChargesPortion.subtract(accrualActivity.getPenaltyChargesPortion());
|
||||
}
|
||||
if (accrualActivity.getInterestPortion() != null) {
|
||||
interestPortion = interestPortion.subtract(accrualActivity.getInterestPortion());
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal transactionAmount = feeChargesPortion.add(penaltyChargesPortion).add(interestPortion);
|
||||
ExternalId externalId = externalIdFactory.create();
|
||||
|
||||
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), date, transactionAmount, null,
|
||||
interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void processAccrualActivityForLoanReopen(Loan loan) {
|
||||
LoanTransaction lastAccrualActivityMarkedToReverse = null;
|
||||
List<LoanTransaction> accrualActivityTransaction = loan.getLoanTransactions().stream()
|
||||
.filter(loanTransaction -> loanTransaction.isNotReversed() && loanTransaction.isAccrualActivity())
|
||||
.sorted(Comparator.comparing(LoanTransaction::getDateOf)).toList();
|
||||
// grab the latest AccrualActivityTransaction
|
||||
// it does not matter if it is on an installment due date or not because it was posted due to loan close
|
||||
if (!accrualActivityTransaction.isEmpty()) {
|
||||
lastAccrualActivityMarkedToReverse = accrualActivityTransaction.get(accrualActivityTransaction.size() - 1);
|
||||
}
|
||||
final LocalDate lastAccrualActivityTransactionDate = lastAccrualActivityMarkedToReverse == null ? null
|
||||
: lastAccrualActivityMarkedToReverse.getDateOf();
|
||||
LocalDate today = DateUtils.getBusinessLocalDate();
|
||||
final List<LoanRepaymentScheduleInstallment> installmentsBetweenBusinessDateAndLastAccrualActivityTransactionDate = loan
|
||||
.getRepaymentScheduleInstallments().stream()
|
||||
.filter(installment -> installment.getDueDate().isBefore(today) && (lastAccrualActivityTransactionDate == null
|
||||
|| installment.getDueDate().isAfter(lastAccrualActivityTransactionDate)
|
||||
// if close event happened on installment due date
|
||||
// we should reverse replay it to calculate installment related accrual parts only
|
||||
|| installment.getDueDate().isEqual(lastAccrualActivityTransactionDate)))
|
||||
.sorted(Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate)).toList();
|
||||
for (LoanRepaymentScheduleInstallment installment : installmentsBetweenBusinessDateAndLastAccrualActivityTransactionDate) {
|
||||
if (lastAccrualActivityMarkedToReverse != null) {
|
||||
loanWritePlatformService.reverseReplayAccrualActivityTransaction(loan, lastAccrualActivityMarkedToReverse, installment,
|
||||
installment.getDueDate());
|
||||
lastAccrualActivityMarkedToReverse = null;
|
||||
} else {
|
||||
loanWritePlatformService.makeAccrualActivityTransaction(loan, installment, installment.getDueDate());
|
||||
}
|
||||
}
|
||||
if (lastAccrualActivityMarkedToReverse != null) {
|
||||
reverseAccrualActivityTransaction(lastAccrualActivityMarkedToReverse);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+20
@@ -32,10 +32,13 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
|
||||
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
private final LoanAccrualActivityProcessingService loanAccrualActivityProcessingService;
|
||||
|
||||
@PostConstruct
|
||||
public void addListeners() {
|
||||
businessEventNotifierService.addPostBusinessEventListener(LoanStatusChangedBusinessEvent.class, new LoanStatusChangedListener());
|
||||
businessEventNotifierService.addPostBusinessEventListener(LoanStatusChangedBusinessEvent.class,
|
||||
new LoanAccrualActivityPostingLoanStatusChangedListener());
|
||||
}
|
||||
|
||||
private final class LoanStatusChangedListener implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
|
||||
@@ -53,4 +56,21 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class LoanAccrualActivityPostingLoanStatusChangedListener
|
||||
implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
|
||||
|
||||
@Override
|
||||
public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
|
||||
final Loan loan = event.get();
|
||||
if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
|
||||
if (loan.getStatus().isClosedObligationsMet() || loan.getStatus().isOverpaid()) {
|
||||
loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
|
||||
}
|
||||
if (loan.isOpen()) {
|
||||
loanAccrualActivityProcessingService.processAccrualActivityForLoanReopen(loan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-1
@@ -1162,15 +1162,50 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public Loan reverseReplayAccrualActivityTransaction(Loan loan, final LoanTransaction loanTransaction,
|
||||
final LoanRepaymentScheduleInstallment installment, final LocalDate transactionDate) {
|
||||
|
||||
LoanTransaction newLoanTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
|
||||
transactionDate);
|
||||
if (!newLoanTransaction.getDateOf().isEqual(loanTransaction.getDateOf())
|
||||
|| !LoanTransaction.transactionAmountsMatch(loan.getCurrency(), loanTransaction, newLoanTransaction)) {
|
||||
loanTransaction.reverse();
|
||||
loanTransaction.updateExternalId(null);
|
||||
newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
|
||||
newLoanTransaction.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(newLoanTransaction,
|
||||
loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED));
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newLoanTransaction);
|
||||
loan.addLoanTransaction(newLoanTransaction);
|
||||
|
||||
LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
|
||||
data.setNewTransactionDetail(newLoanTransaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
|
||||
}
|
||||
return loan;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public Loan makeAccrualActivityTransaction(Loan loan, final LoanRepaymentScheduleInstallment installment,
|
||||
final LocalDate transactionDate) {
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionAccrualActivityPreBusinessEvent(loan));
|
||||
|
||||
LoanTransaction newAccrualActivityTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
|
||||
transactionDate);
|
||||
|
||||
if (newAccrualActivityTransaction.getAmount().compareTo(BigDecimal.ZERO) == 0) {
|
||||
return loan;
|
||||
}
|
||||
|
||||
return makeAccrualActivityTransaction(loan, newAccrualActivityTransaction);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public Loan makeAccrualActivityTransaction(Loan loan, LoanTransaction newAccrualActivityTransaction) {
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionAccrualActivityPreBusinessEvent(loan));
|
||||
|
||||
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newAccrualActivityTransaction);
|
||||
|
||||
loan.addLoanTransaction(newAccrualActivityTransaction);
|
||||
|
||||
+5
-2
@@ -94,6 +94,7 @@ import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlat
|
||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualActivityProcessingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService;
|
||||
@@ -329,8 +330,10 @@ public class LoanAccountConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanStatusChangePlatformService.class)
|
||||
public LoanStatusChangePlatformService loanStatusChangePlatformService(BusinessEventNotifierService businessEventNotifierService,
|
||||
LoanAccrualsProcessingService loanAccrualsProcessingService) {
|
||||
return new LoanStatusChangePlatformServiceImpl(businessEventNotifierService, loanAccrualsProcessingService);
|
||||
LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||
LoanAccrualActivityProcessingService loanAccrualActivityProcessingService) {
|
||||
return new LoanStatusChangePlatformServiceImpl(businessEventNotifierService, loanAccrualsProcessingService,
|
||||
loanAccrualActivityProcessingService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+646
-40
@@ -28,6 +28,7 @@ import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
@@ -35,6 +36,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.fineract.client.models.AdvancedPaymentData;
|
||||
import org.apache.fineract.client.models.AllowAttributeOverrides;
|
||||
import org.apache.fineract.client.models.ChargeData;
|
||||
import org.apache.fineract.client.models.ChargeToGLAccountMapper;
|
||||
import org.apache.fineract.client.models.GetLoanFeeToIncomeAccountMappings;
|
||||
import org.apache.fineract.client.models.GetLoanPaymentChannelToFundSourceMappings;
|
||||
import org.apache.fineract.client.models.PaymentAllocationOrder;
|
||||
import org.apache.fineract.client.models.PostChargesRequest;
|
||||
import org.apache.fineract.client.models.PostChargesResponse;
|
||||
@@ -119,7 +125,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
final String repaymentPeriod1OneDayAfterCloseDate = "03 February 2023";
|
||||
final String repaymentPeriod2DueDate = "01 March 2023";
|
||||
final String repaymentPeriod3DueDate = "01 April 2023";
|
||||
final String creationBusinessDay = "15 May 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
|
||||
@@ -127,10 +133,6 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
loanId.set(applyForLoanApplicationWithInterest(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||
disbursementDay));
|
||||
|
||||
LOG.info("Test Loan Product Id {1} http://localhost:4200/#/products/loan-products/{}/general", localLoanProductId);
|
||||
LOG.info("Test Client Id {1} http://localhost:4200/#/clients/{}", client.getClientId());
|
||||
LOG.info("Test Loan Id {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions", client.getClientId(), loanId);
|
||||
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
@@ -193,7 +195,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
final String repaymentPeriod1OneDayAfterCloseDate = "03 February 2023";
|
||||
final String repaymentPeriod2DueDate = "01 March 2023";
|
||||
final String repaymentPeriod3DueDate = "01 April 2023";
|
||||
final String creationBusinessDay = "15 May 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
|
||||
@@ -201,10 +203,6 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
loanId.set(applyForLoanApplicationWithInterest(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||
disbursementDay));
|
||||
|
||||
LOG.info("Test Loan Product Id {1} http://localhost:4200/#/products/loan-products/{}/general", localLoanProductId);
|
||||
LOG.info("Test Client Id {1} http://localhost:4200/#/clients/{}", client.getClientId());
|
||||
LOG.info("Test Loan Id {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions", client.getClientId(), loanId);
|
||||
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
@@ -256,7 +254,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String creationBusinessDay = "15 May 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId1 = new AtomicReference<>();
|
||||
AtomicReference<Long> loanId2 = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
@@ -316,7 +314,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String repaymentPeriod1OneDayAfterCloseDate = "03 February 2023";
|
||||
final String creationBusinessDay = "15 May 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
|
||||
@@ -399,7 +397,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String repaymentPeriod1OneDayAfterCloseDate = "03 February 2023";
|
||||
final String creationBusinessDay = "15 May 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
|
||||
@@ -413,10 +411,6 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
|
||||
|
||||
LOG.info("Test Loan Product Id {1} http://localhost:4200/#/products/loan-products/{}/general", localLoanProductId);
|
||||
LOG.info("Test Client Id {1} http://localhost:4200/#/clients/{}", client.getClientId());
|
||||
LOG.info("Test Loan Id {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions", client.getClientId(), loanId);
|
||||
|
||||
chargePenalty(loanId.get(), 20.0, chargeDueDate1st);
|
||||
|
||||
addRepaymentForLoan(loanId.get(), 50.0, "10 January 2023");
|
||||
@@ -482,7 +476,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String repaymentPeriod1OneDayAfterCloseDate = "03 February 2023";
|
||||
final String creationBusinessDay = "15 May 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
|
||||
@@ -496,10 +490,6 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
|
||||
|
||||
LOG.info("Test Loan Product Id {1} http://localhost:4200/#/products/loan-products/{}/general", localLoanProductId);
|
||||
LOG.info("Test Client Id {1} http://localhost:4200/#/clients/{}", client.getClientId());
|
||||
LOG.info("Test Loan Id {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions", client.getClientId(), loanId);
|
||||
|
||||
chargePenalty(loanId.get(), 20.0, chargeDueDate1st);
|
||||
|
||||
verifyTransactions(loanId.get(), //
|
||||
@@ -532,9 +522,566 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccrualActivityPostingForProgressiveLoanWithEarlyRepaymentAndReverseRepayment() {
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String repaymentDate1 = "15 January 2023";
|
||||
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
AtomicReference<Long> repaymentId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation();
|
||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||
disbursementDay));
|
||||
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
|
||||
|
||||
chargePenalty(loanId.get(), 30.0, repaymentPeriod1DueDate);
|
||||
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
|
||||
|
||||
repaymentId.set(addRepaymentForLoan(loanId.get(), 1070.0, repaymentDate1));
|
||||
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1070.0, "Repayment", repaymentDate1, 0.0, 1000.0, 0.0, 40.0, 30, 0.0, 0.0, false), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
runAt(repaymentPeriod1CloseDate, () -> {
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1070.0, "Repayment", repaymentDate1, 0.0, 1000.0, 0.0, 40.0, 30, 0.0, 0.0, false), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false)); //
|
||||
|
||||
loanTransactionHelper.reverseRepayment(loanId.get().intValue(), repaymentId.get().intValue(), repaymentDate1);
|
||||
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1070.0, "Repayment", repaymentDate1, 0.0, 1000.0, 0.0, 40.0, 30, 0.0, 0.0, true), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccrualActivityPostingForLoanWithEarlyRepaymentAndReverseRepayment() {
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String repaymentDate1 = "15 January 2023";
|
||||
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
AtomicReference<Long> repaymentId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicWithInterest();
|
||||
loanId.set(applyForLoanApplicationWithInterest(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||
disbursementDay));
|
||||
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
|
||||
|
||||
chargePenalty(loanId.get(), 30.0, repaymentPeriod1DueDate);
|
||||
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
|
||||
|
||||
repaymentId.set(addRepaymentForLoan(loanId.get(), 1150.0, repaymentDate1));
|
||||
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1150.0, "Repayment", repaymentDate1, 0.0, 1000.0, 80.0, 40.0, 30, 0.0, 0.0, false), //
|
||||
transaction(150.0, "Accrual", repaymentDate1, 0.0, 0.0, 80.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(150.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 80.0, 40.0, 30.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
runAt(repaymentPeriod1CloseDate, () -> {
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1150.0, "Repayment", repaymentDate1, 0.0, 1000.0, 80.0, 40, 30, 0.0, 0.0, false), //
|
||||
transaction(150.0, "Accrual", repaymentDate1, 0.0, 0.0, 80.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(150.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 80.0, 40.0, 30.0, 0.0, 0.0, false)); //
|
||||
|
||||
loanTransactionHelper.reverseRepayment(loanId.get().intValue(), repaymentId.get().intValue(), repaymentDate1);
|
||||
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1150.0, "Repayment", repaymentDate1, 0.0, 1000.0, 80.0, 40.0, 30, 0.0, 0.0, true), //
|
||||
transaction(90.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 20.0, 40.0, 30.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccrualActivityPostingForProgressiveMultiDisburseLoanWithEarlyRepayment2ndDisbursement() {
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String disbursementDay2 = "25 January 2023";
|
||||
final String repaymentDate1 = "15 January 2023";
|
||||
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
||||
|
||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||
disbursementDay, disbursementDay2));
|
||||
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
|
||||
chargePenalty(loanId.get(), 30.0, repaymentPeriod1DueDate);
|
||||
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
|
||||
|
||||
addRepaymentForLoan(loanId.get(), 650.0, repaymentDate1);
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
runAt(disbursementDay2, () -> {
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay2)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
runAt(repaymentPeriod1CloseDate, () -> {
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
|
||||
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccrualActivityPostingForProgressiveMultiDisburseLoanWithEarlyRepaymentBackdated2ndDisbursement() {
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String disbursementDay2 = "25 January 2023";
|
||||
final String repaymentDate1 = "15 January 2023";
|
||||
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
||||
|
||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||
disbursementDay, disbursementDay2));
|
||||
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
|
||||
chargePenalty(loanId.get(), 30.0, repaymentPeriod1DueDate);
|
||||
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
|
||||
|
||||
addRepaymentForLoan(loanId.get(), 650.0, repaymentDate1);
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
runAt(repaymentPeriod1CloseDate, () -> {
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay2)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
|
||||
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccrualActivityPostingForMultiDisburseLoanWithEarlyRepaymentBackdated2ndDisbursement() {
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String disbursementDay2 = "25 January 2023";
|
||||
final String repaymentDate1 = "15 January 2023";
|
||||
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicWithInterest(true);
|
||||
loanId.set(applyForLoanApplicationWithInterest(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||
disbursementDay, disbursementDay2));
|
||||
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
|
||||
chargePenalty(loanId.get(), 30.0, repaymentPeriod1DueDate);
|
||||
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
|
||||
|
||||
addRepaymentForLoan(loanId.get(), 650.0, repaymentDate1);
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(109.45, "Accrual", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(109.45, "Accrual Activity", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
|
||||
});
|
||||
runAt(repaymentPeriod1CloseDate, () -> {
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(109.45, "Accrual", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(109.45, "Accrual Activity", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay2)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(89.72, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 19.72, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay2, 479.16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(650.0, "Repayment", repaymentDate1, 0.0, 520.84, 59.16, 40.0, 30.0, 0.0, 0.0, false)); //
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccrualActivityPostingForMultiDisburseProgressiveLoan() {
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String disbursementDay2 = "02 February 2023";
|
||||
final String repaymentDate1 = "15 January 2023";
|
||||
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String repaymentPeriod2CloseDate = "02 March 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000),
|
||||
disbursementDay, disbursementDay2));
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
chargePenalty(loanId.get(), 30.0, repaymentPeriod1DueDate);
|
||||
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
|
||||
|
||||
addRepaymentForLoan(loanId.get(), 570.0, repaymentDate1);
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false),
|
||||
transaction(570, "Repayment", repaymentDate1, 0.0, 500.0, 0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(70.0, "Accrual Activity", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false));
|
||||
});
|
||||
runAt(repaymentPeriod1CloseDate, () -> {
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false),
|
||||
transaction(570, "Repayment", repaymentDate1, 0.0, 500.0, 0, 40.0, 30.0, 0, 0, true),
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(70.0, "Accrual Activity", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay2)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false),
|
||||
transaction(570, "Repayment", repaymentDate1, 0.0, 500.0, 0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(500.0, "Disbursement", disbursementDay2, 500, 0, 0, 0, 0, 0, 0, false));
|
||||
});
|
||||
runAt(repaymentPeriod2CloseDate, () -> {
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
|
||||
verifyTransactions(loanId.get(), transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false),
|
||||
transaction(70.0, "Accrual", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(570.0, "Repayment", repaymentDate1, 0.0, 500.0, 0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
|
||||
transaction(500.0, "Disbursement", disbursementDay2, 500.0, 0, 0, 0, 0, 0, 0, false));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccrualActivityPostingForMultiDisburseLoan() {
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String disbursementDay2 = "02 February 2023";
|
||||
final String repaymentDate1 = "15 January 2023";
|
||||
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||
final String repaymentPeriod2DueDate = "01 March 2023";
|
||||
final String repaymentPeriod2CloseDate = "02 March 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicWithInterest(true);
|
||||
loanId.set(applyForLoanApplicationWithInterest(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||
disbursementDay, disbursementDay2));
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
chargePenalty(loanId.get(), 30.0, repaymentPeriod1DueDate);
|
||||
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
|
||||
addRepaymentForLoan(loanId.get(), 650.0, repaymentDate1);
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), // transaction(109.45,
|
||||
// "Accrual",
|
||||
// repaymentDate1,
|
||||
// 0.0,
|
||||
// 0,
|
||||
// 39.45,
|
||||
// 40.0,
|
||||
// 30.0,
|
||||
// 0,
|
||||
// 0,
|
||||
// false),
|
||||
// //
|
||||
transaction(109.45, "Accrual Activity", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(109.45, "Accrual", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false) //
|
||||
);
|
||||
});
|
||||
runAt(repaymentPeriod1CloseDate, () -> {
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), // transaction(109.45,
|
||||
// "Accrual",
|
||||
// repaymentDate1,
|
||||
// 0.0,
|
||||
// 0,
|
||||
// 39.45,
|
||||
// 40.0,
|
||||
// 30.0,
|
||||
// 0,
|
||||
// 0,
|
||||
// false),
|
||||
// //
|
||||
transaction(109.45, "Accrual Activity", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(109.45, "Accrual", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false) //
|
||||
);
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay2)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(500.0, "Disbursement", disbursementDay2, 479.16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(650.0, "Repayment", repaymentDate1, 0.0, 520.84, 59.16, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(89.72, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 19.72, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false) //
|
||||
);
|
||||
});
|
||||
runAt(repaymentPeriod2CloseDate, () -> {
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(500.0, "Disbursement", disbursementDay2, 479.16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(650.0, "Repayment", repaymentDate1, 0.0, 520.84, 59.16, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(89.72, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 19.72, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(89.72, "Accrual", repaymentPeriod1DueDate, 0.0, 0.0, 19.72, 40.0, 30.0, 0.0, 0.0, false), //
|
||||
transaction(19.72, "Accrual", repaymentPeriod2DueDate, 0.0, 0.0, 19.72, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(19.72, "Accrual Activity", repaymentPeriod2DueDate, 0.0, 0.0, 19.72, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false) //
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
final String disbursementDay = "01 January 2023";
|
||||
final String repaymentDate1 = "15 January 2023";
|
||||
final String repaymentPeriod1DueDate = "31 January 2023";
|
||||
final String repaymentPeriod1CloseDate = "01 February 2023";
|
||||
final String creationBusinessDay = "15 January 2023";
|
||||
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||
runAt(creationBusinessDay, () -> {
|
||||
Long localLoanProductId = loanTransactionHelper
|
||||
.createLoanProduct(loanProductsRequestPin30InterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity())
|
||||
.getResourceId();
|
||||
loanId.set(applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000), disbursementDay));
|
||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
|
||||
});
|
||||
runAt(repaymentPeriod1CloseDate, () -> {
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(1.64, "Accrual", repaymentPeriod1DueDate, 0.0, 0.0, 1.64, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1.64, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 1.64, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false));
|
||||
loanTransactionHelper.makeLoanRepayment(repaymentDate1, 150.0F, loanId.get().intValue());
|
||||
|
||||
verifyTransactions(loanId.get(),
|
||||
transaction(150.0, "Repayment", repaymentDate1, 851.52, 148.48, 1.52, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1.52, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 1.52, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1.52, "Accrual", repaymentPeriod1DueDate, 0.0, 0.0, 1.52, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false));
|
||||
});
|
||||
}
|
||||
|
||||
private PostLoanProductsRequest loanProductsRequestPin30InterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity() {
|
||||
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
||||
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
||||
|
||||
List<Integer> principalVariationsForBorrowerCycle = new ArrayList<>();
|
||||
List<Integer> numberOfRepaymentVariationsForBorrowerCycle = new ArrayList<>();
|
||||
List<Integer> interestRateVariationsForBorrowerCycle = new ArrayList<>();
|
||||
List<ChargeData> charges = new ArrayList<>();
|
||||
List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = new ArrayList<>();
|
||||
List<GetLoanFeeToIncomeAccountMappings> feeToIncomeAccountMappings = new ArrayList<>();
|
||||
|
||||
List<GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings = new ArrayList<>();
|
||||
GetLoanPaymentChannelToFundSourceMappings loanPaymentChannelToFundSourceMappings = new GetLoanPaymentChannelToFundSourceMappings();
|
||||
loanPaymentChannelToFundSourceMappings.fundSourceAccountId(fundSource.getAccountID().longValue());
|
||||
loanPaymentChannelToFundSourceMappings.paymentTypeId(1L);
|
||||
paymentChannelToFundSourceMappings.add(loanPaymentChannelToFundSourceMappings);
|
||||
return new PostLoanProductsRequest()//
|
||||
.name(name)//
|
||||
.enableAccrualActivityPosting(true)//
|
||||
.shortName(shortName)//
|
||||
.description(
|
||||
"LP1 with 12% DECLINING BALANCE interest, interest period: Daily, Interest recalculation-Daily, Compounding:none")//
|
||||
.fundId(1L)//
|
||||
.startDate(null)//
|
||||
.closeDate(null)//
|
||||
.includeInBorrowerCycle(false)//
|
||||
.currencyCode("EUR")//
|
||||
.digitsAfterDecimal(2)//
|
||||
.inMultiplesOf(1)//
|
||||
.installmentAmountInMultiplesOf(1)//
|
||||
.useBorrowerCycle(false)//
|
||||
.minPrincipal(100.0)//
|
||||
.principal(1000.0)//
|
||||
.maxPrincipal(10000.0)//
|
||||
.minNumberOfRepayments(1)//
|
||||
.numberOfRepayments(1)//
|
||||
.maxNumberOfRepayments(30)//
|
||||
.isLinkedToFloatingInterestRates(false)//
|
||||
.minInterestRatePerPeriod(0.0)//
|
||||
.interestRatePerPeriod(12.0)//
|
||||
.maxInterestRatePerPeriod(30.0)//
|
||||
.interestRateFrequencyType(3)//
|
||||
.repaymentEvery(30)//
|
||||
.repaymentFrequencyType(0L)//
|
||||
.principalVariationsForBorrowerCycle(principalVariationsForBorrowerCycle)//
|
||||
.numberOfRepaymentVariationsForBorrowerCycle(numberOfRepaymentVariationsForBorrowerCycle)//
|
||||
.interestRateVariationsForBorrowerCycle(interestRateVariationsForBorrowerCycle)//
|
||||
.amortizationType(1)//
|
||||
.interestType(0)//
|
||||
.isEqualAmortization(false)//
|
||||
.interestCalculationPeriodType(0)//
|
||||
.transactionProcessingStrategyCode("mifos-standard-strategy")//
|
||||
.daysInYearType(1)//
|
||||
.daysInMonthType(1)//
|
||||
.canDefineInstallmentAmount(true)//
|
||||
.graceOnArrearsAgeing(3)//
|
||||
.overdueDaysForNPA(179)//
|
||||
.accountMovesOutOfNPAOnlyOnArrearsCompletion(false)//
|
||||
.principalThresholdForLastInstallment(50)//
|
||||
.allowVariableInstallments(false)//
|
||||
.canUseForTopup(false)//
|
||||
.holdGuaranteeFunds(false)//
|
||||
.multiDisburseLoan(false)//
|
||||
.allowAttributeOverrides(new AllowAttributeOverrides()//
|
||||
.amortizationType(true)//
|
||||
.interestType(true)//
|
||||
.transactionProcessingStrategyCode(true)//
|
||||
.interestCalculationPeriodType(true)//
|
||||
.inArrearsTolerance(true)//
|
||||
.repaymentEvery(true)//
|
||||
.graceOnPrincipalAndInterestPayment(true)//
|
||||
.graceOnArrearsAgeing(true))
|
||||
.outstandingLoanBalance(10000.0)//
|
||||
.charges(charges)//
|
||||
.accountingRule(3)//
|
||||
|
||||
.fundSourceAccountId(suspenseAccount.getAccountID().longValue())//
|
||||
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
|
||||
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
|
||||
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
|
||||
.incomeFromFeeAccountId(feeIncomeAccount.getAccountID().longValue())//
|
||||
.incomeFromPenaltyAccountId(penaltyIncomeAccount.getAccountID().longValue())//
|
||||
.incomeFromRecoveryAccountId(recoveriesAccount.getAccountID().longValue())//
|
||||
.writeOffAccountId(writtenOffAccount.getAccountID().longValue())//
|
||||
.overpaymentLiabilityAccountId(overpaymentAccount.getAccountID().longValue())//
|
||||
.receivableInterestAccountId(interestReceivableAccount.getAccountID().longValue())//
|
||||
.receivableFeeAccountId(feeReceivableAccount.getAccountID().longValue())//
|
||||
.receivablePenaltyAccountId(penaltyReceivableAccount.getAccountID().longValue())//
|
||||
.goodwillCreditAccountId(goodwillExpenseAccount.getAccountID().longValue())//
|
||||
.incomeFromGoodwillCreditInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromGoodwillCreditFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromGoodwillCreditPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromChargeOffInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
||||
.chargeOffExpenseAccountId(chargeOffExpenseAccount.getAccountID().longValue())//
|
||||
.chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
|
||||
.incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())//
|
||||
|
||||
.dateFormat("dd MMMM yyyy")//
|
||||
.locale("en")//
|
||||
.disallowExpectedDisbursements(false)//
|
||||
.allowApprovedDisbursedAmountsOverApplied(false)//
|
||||
.delinquencyBucketId(1L)//
|
||||
.paymentChannelToFundSourceMappings(paymentChannelToFundSourceMappings)//
|
||||
.penaltyToIncomeAccountMappings(penaltyToIncomeAccountMappings)//
|
||||
.feeToIncomeAccountMappings(feeToIncomeAccountMappings)//
|
||||
.isInterestRecalculationEnabled(true)//
|
||||
.preClosureInterestCalculationStrategy(1)//
|
||||
.rescheduleStrategyMethod(3)//
|
||||
.interestRecalculationCompoundingMethod(0)//
|
||||
.recalculationRestFrequencyType(2)//
|
||||
.recalculationRestFrequencyInterval(1)//
|
||||
.allowPartialPeriodInterestCalcualtion(false);//
|
||||
|
||||
}
|
||||
|
||||
private void chargeFee(Long loanId, Double amount, String dueDate) {
|
||||
LOG.info("----------------------------------CHARGE FEE---------------------------------");
|
||||
LOG.info("FEE amount {} dueDate {}", amount, dueDate);
|
||||
LOG.info("Charge FEE amount {} dueDate {}", amount, dueDate);
|
||||
PostChargesResponse feeCharge = chargesHelper.createCharges(new PostChargesRequest().penalty(false).amount(9.0)
|
||||
.chargeCalculationType(ChargeCalculationType.FLAT.getValue()).chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
||||
@@ -548,8 +1095,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
}
|
||||
|
||||
private void chargePenalty(Long loanId, Double amount, String dueDate) {
|
||||
LOG.info("----------------------------------CHARGE PENALTY---------------------------------");
|
||||
LOG.info("PENALTY amount {} dueDate {}", amount, dueDate);
|
||||
LOG.info("Charge PENALTY amount {} dueDate {}", amount, dueDate);
|
||||
PostChargesResponse penaltyCharge = chargesHelper.createCharges(new PostChargesRequest().penalty(true).amount(10.0)
|
||||
.chargeCalculationType(ChargeCalculationType.FLAT.getValue()).chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
||||
@@ -560,21 +1106,25 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
.amount(amount).dueDate(dueDate));
|
||||
assertNotNull(penaltyLoanChargeResult);
|
||||
assertNotNull(penaltyLoanChargeResult.getResourceId());
|
||||
LOG.info("----------------------------------CHARGE PENALTY---------------------------------");
|
||||
}
|
||||
|
||||
private Long createLoanProductAccountingAccrualPeriodicWithInterest() {
|
||||
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||
return createLoanProductAccountingAccrualPeriodicWithInterest(false);
|
||||
}
|
||||
|
||||
private Long createLoanProductAccountingAccrualPeriodicWithInterest(boolean isMultiDisburse) {
|
||||
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
||||
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
||||
return loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest().name(name).shortName(shortName)
|
||||
Long resourceId = loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest().name(name).shortName(shortName)
|
||||
.multiDisburseLoan(isMultiDisburse).maxTrancheCount(isMultiDisburse ? 2 : 1).interestType(isMultiDisburse ? 0 : 1)
|
||||
.interestCalculationPeriodType(isMultiDisburse ? 0 : 1).disallowExpectedDisbursements(isMultiDisburse)
|
||||
.description("Test loan description").currencyCode("USD").digitsAfterDecimal(2).daysInYearType(1).daysInMonthType(1)
|
||||
.interestRecalculationCompoundingMethod(0).recalculationRestFrequencyType(1).rescheduleStrategyMethod(1)
|
||||
.recalculationRestFrequencyInterval(0).isInterestRecalculationEnabled(false).interestRateFrequencyType(2).locale("en_GB")
|
||||
.numberOfRepayments(4).repaymentFrequencyType(2L).interestRatePerPeriod(2.0).repaymentEvery(1).minPrincipal(100.0)
|
||||
.principal(1000.0).maxPrincipal(10000000.0).amortizationType(1).interestType(1).interestCalculationPeriodType(1)
|
||||
.dateFormat("dd MMMM yyyy").transactionProcessingStrategyCode(DEFAULT_STRATEGY).accountingRule(3)
|
||||
.enableAccrualActivityPosting(true).fundSourceAccountId(fundSource.getAccountID().longValue())//
|
||||
.principal(1000.0).maxPrincipal(10000000.0).amortizationType(1).dateFormat("dd MMMM yyyy")
|
||||
.transactionProcessingStrategyCode(DEFAULT_STRATEGY).accountingRule(3).enableAccrualActivityPosting(true)
|
||||
.fundSourceAccountId(fundSource.getAccountID().longValue())//
|
||||
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
|
||||
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
|
||||
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
|
||||
@@ -596,32 +1146,68 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
.chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
|
||||
.incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())//
|
||||
).getResourceId();
|
||||
LOG.info("Test Loan Product With Interest Id {} isMultiDisburse {} http://localhost:4200/#/products/loan-products/{1}/general",
|
||||
resourceId, isMultiDisburse);
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
private static Long applyForLoanApplication(final Long clientID, final Long loanProductID, BigDecimal principal,
|
||||
String applicationDisbursementDate) {
|
||||
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
||||
.locale("en_GB").dateFormat("dd MMMM yyyy").expectedDisbursementDate(applicationDisbursementDate)
|
||||
.submittedOnDate(applicationDisbursementDate).interestCalculationPeriodType(0).repaymentFrequencyType(0).repaymentEvery(30)
|
||||
.principal(BigDecimal.valueOf(1000)).loanTermFrequency(120).loanTermFrequencyType(0)
|
||||
.transactionProcessingStrategyCode(DEFAULT_STRATEGY).interestType(0).loanType("individual").numberOfRepayments(4)
|
||||
.amortizationType(1).interestRatePerPeriod(BigDecimal.valueOf(2)).clientId(clientID).productId(loanProductID);
|
||||
Long loanId = loanTransactionHelper.applyLoan(loanRequest).getLoanId();
|
||||
LOG.info("Test Loan http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions", client.getClientId(), loanId);
|
||||
return loanId;
|
||||
}
|
||||
|
||||
private static Long applyForLoanApplicationWithInterest(final Long clientID, final Long loanProductID, BigDecimal principal,
|
||||
String applicationDisbursementDate) {
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
||||
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
||||
.interestRatePerPeriod(BigDecimal.valueOf(2)).repaymentEvery(1).principal(principal).amortizationType(1).interestType(1)
|
||||
.interestCalculationPeriodType(1).dateFormat("dd MMMM yyyy").transactionProcessingStrategyCode(DEFAULT_STRATEGY)
|
||||
.loanType("individual").expectedDisbursementDate(applicationDisbursementDate).submittedOnDate(applicationDisbursementDate)
|
||||
.clientId(clientID).productId(loanProductID);
|
||||
return loanTransactionHelper.applyLoan(loanRequest).getLoanId();
|
||||
Long loanId = loanTransactionHelper.applyLoan(loanRequest).getLoanId();
|
||||
LOG.info("Test Loan with Interest Id {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions", client.getClientId(),
|
||||
loanId);
|
||||
return loanId;
|
||||
}
|
||||
|
||||
private static Long applyForLoanApplicationWithInterest(final Long clientID, final Long loanProductID, BigDecimal principal,
|
||||
String applicationDisbursementDate, String applicationDisbursementDate2) {
|
||||
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
||||
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
||||
.interestRatePerPeriod(BigDecimal.valueOf(2)).repaymentEvery(1).principal(principal).amortizationType(1).interestType(1)
|
||||
.interestCalculationPeriodType(0).dateFormat("dd MMMM yyyy").transactionProcessingStrategyCode(DEFAULT_STRATEGY)
|
||||
.loanType("individual").submittedOnDate(applicationDisbursementDate).expectedDisbursementDate(applicationDisbursementDate2)
|
||||
.clientId(clientID).productId(loanProductID);
|
||||
Long loanId = loanTransactionHelper.applyLoan(loanRequest).getLoanId();
|
||||
LOG.info("Test Loan with Interest Id MultiDisbursed {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions",
|
||||
client.getClientId(), loanId);
|
||||
return loanId;
|
||||
}
|
||||
|
||||
private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation() {
|
||||
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||
return createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(false);
|
||||
}
|
||||
|
||||
private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(boolean isMultiDisburse) {
|
||||
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
||||
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
||||
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation();
|
||||
return loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest().name(name).shortName(shortName)
|
||||
Long resourceId = loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest().name(name).shortName(shortName)
|
||||
.multiDisburseLoan(isMultiDisburse).maxTrancheCount(isMultiDisburse ? 2 : 1).interestType(isMultiDisburse ? 0 : 1)
|
||||
.interestCalculationPeriodType(isMultiDisburse ? 0 : 1).disallowExpectedDisbursements(isMultiDisburse)
|
||||
.description("Test loan description").currencyCode("USD").digitsAfterDecimal(2).daysInYearType(1).daysInMonthType(1)
|
||||
.recalculationRestFrequencyType(1).rescheduleStrategyMethod(1).loanScheduleType(LoanScheduleType.PROGRESSIVE.name())
|
||||
.recalculationRestFrequencyInterval(0).isInterestRecalculationEnabled(false).locale("en_GB").numberOfRepayments(4)
|
||||
.repaymentFrequencyType(2L).repaymentEvery(1).minPrincipal(100.0).principal(1000.0).maxPrincipal(10000000.0)
|
||||
.amortizationType(1).interestType(0).interestRatePerPeriod(0.0).interestRateFrequencyType(1)
|
||||
.interestCalculationPeriodType(1).dateFormat("dd MMMM yyyy")
|
||||
.amortizationType(1).interestRatePerPeriod(0.0).interestRateFrequencyType(1).dateFormat("dd MMMM yyyy")
|
||||
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).paymentAllocation(List.of(defaultAllocation))
|
||||
.accountingRule(3).enableAccrualActivityPosting(true).fundSourceAccountId(fundSource.getAccountID().longValue())//
|
||||
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
|
||||
@@ -645,6 +1231,9 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
.chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
|
||||
.incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())//
|
||||
).getResourceId();
|
||||
LOG.info("Test Progressive Loan Product Id {} isMultiDisburse {} http://localhost:4200/#/products/loan-products/{1}/general",
|
||||
resourceId, isMultiDisburse);
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
private static List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
|
||||
@@ -674,7 +1263,6 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
|
||||
private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID,
|
||||
BigDecimal principal, String applicationDisbursementDate) {
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
||||
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
||||
.repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(BigDecimal.ZERO)
|
||||
@@ -682,7 +1270,25 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
||||
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanType("individual")
|
||||
.expectedDisbursementDate(applicationDisbursementDate).submittedOnDate(applicationDisbursementDate).clientId(clientID)
|
||||
.productId(loanProductID);
|
||||
return loanTransactionHelper.applyLoan(loanRequest).getLoanId();
|
||||
Long loanId = loanTransactionHelper.applyLoan(loanRequest).getLoanId();
|
||||
LOG.info("Test Progressive Loan Id {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions", client.getClientId(),
|
||||
loanId);
|
||||
return loanId;
|
||||
}
|
||||
|
||||
private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID,
|
||||
BigDecimal principal, String applicationDisbursementDate, String applicationDisbursementDate2) {
|
||||
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
||||
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
||||
.repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(BigDecimal.ZERO)
|
||||
.interestCalculationPeriodType(0).dateFormat("dd MMMM yyyy")
|
||||
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanType("individual")
|
||||
.submittedOnDate(applicationDisbursementDate).clientId(clientID).expectedDisbursementDate(applicationDisbursementDate2)
|
||||
.productId(loanProductID);
|
||||
Long loanId = loanTransactionHelper.applyLoan(loanRequest).getLoanId();
|
||||
LOG.info("Test Progressive Loan Multi Disbursed Id {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions",
|
||||
client.getClientId(), loanId);
|
||||
return loanId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user