FINERACT-2042 Chargeback with credit allocation, accounting

This commit is contained in:
Peter Bagrij
2024-03-07 11:34:48 +01:00
committed by Peter Bagrij
parent 5f9845f86a
commit 697449e7e7
15 changed files with 960 additions and 308 deletions
@@ -35,6 +35,7 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
@@ -763,7 +764,14 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
thisTransactionData.put("currencyCode", currencyCode);
thisTransactionData.put("amount", this.amount);
thisTransactionData.put("netDisbursalAmount", this.loan.getNetDisbursalAmount());
thisTransactionData.put("principalPortion", this.principalPortion);
if (transactionType.isChargeback()
&& (getLoan().getCreditAllocationRules() == null || getLoan().getCreditAllocationRules().size() == 0)) {
thisTransactionData.put("principalPortion", this.amount);
} else {
thisTransactionData.put("principalPortion", this.principalPortion);
}
thisTransactionData.put("interestPortion", this.interestPortion);
thisTransactionData.put("feeChargesPortion", this.feeChargesPortion);
thisTransactionData.put("penaltyChargesPortion", this.penaltyChargesPortion);
@@ -790,6 +798,26 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
thisTransactionData.put("loanChargesPaid", loanChargesPaidData);
}
if (transactionType.isChargeback() && this.overPaymentPortion != null && this.overPaymentPortion.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal principalPaid = overPaymentPortion;
BigDecimal feePaid = BigDecimal.ZERO;
BigDecimal penaltyPaid = BigDecimal.ZERO;
if (getLoanTransactionToRepaymentScheduleMappings().size() > 0) {
principalPaid = getLoanTransactionToRepaymentScheduleMappings().stream()
.map(mapping -> Optional.ofNullable(mapping.getPrincipalPortion()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add);
feePaid = getLoanTransactionToRepaymentScheduleMappings().stream()
.map(mapping -> Optional.ofNullable(mapping.getFeeChargesPortion()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add);
penaltyPaid = getLoanTransactionToRepaymentScheduleMappings().stream()
.map(mapping -> Optional.ofNullable(mapping.getPenaltyChargesPortion()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
thisTransactionData.put("principalPaid", principalPaid);
thisTransactionData.put("feePaid", feePaid);
thisTransactionData.put("penaltyPaid", penaltyPaid);
}
LoanTransactionRelation loanTransactionRelation = loanTransactionRelations.stream()
.filter(e -> LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT.equals(e.getRelationType())).findAny().orElse(null);
if (loanTransactionRelation != null) {
@@ -60,4 +60,9 @@ public class LoanTransactionDTO {
private final String chargeRefundChargeType;
private final LoanChargeData loanChargeData;
/** In case chargeback and overpayment the below field contains the distribution payment **/
private final BigDecimal principalPaid;
private final BigDecimal feePaid;
private final BigDecimal penaltyPaid;
}
@@ -156,9 +156,15 @@ public class AccountingProcessorHelper {
isAccountTransfer = this.accountTransfersReadPlatformService.isAccountTransfer(Long.parseLong(transactionId),
PortfolioAccountType.LOAN);
}
BigDecimal principalPaid = (BigDecimal) map.get("principalPaid");
BigDecimal feePaid = (BigDecimal) map.get("feePaid");
BigDecimal penaltyPaid = (BigDecimal) map.get("penaltyPaid");
final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, reversed,
penaltyPaymentDetails, feePaymentDetails, isAccountTransfer, chargeRefundChargeType, loanChargeData);
penaltyPaymentDetails, feePaymentDetails, isAccountTransfer, chargeRefundChargeType, loanChargeData, principalPaid,
feePaid, penaltyPaid);
Boolean isLoanToLoanTransfer = (Boolean) accountingBridgeData.get("isLoanToLoanTransfer");
transaction.setLoanToLoanTransfer(isLoanToLoanTransfer != null && isLoanToLoanTransfer);
newLoanTransactions.add(transaction);
@@ -430,25 +430,54 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal overpaidAmount = Objects.isNull(loanTransactionDTO.getOverPayment()) ? BigDecimal.ZERO
: loanTransactionDTO.getOverPayment();
if (BigDecimal.ZERO.compareTo(overpaidAmount) == 0) { // when no overpay
helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
amount, isReversal);
} else if (overpaidAmount.compareTo(amount) >= 0) { // when the overpay amount is matching with the normal
// amount
helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
amount, isReversal);
} else {
BigDecimal diff = amount.subtract(overpaidAmount);
helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, diff, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, amount, isReversal);
final BigDecimal principalCredited = Objects.isNull(loanTransactionDTO.getPrincipal()) ? BigDecimal.ZERO
: loanTransactionDTO.getPrincipal();
final BigDecimal feeCredited = Objects.isNull(loanTransactionDTO.getFees()) ? BigDecimal.ZERO : loanTransactionDTO.getFees();
final BigDecimal penaltyCredited = Objects.isNull(loanTransactionDTO.getPenalties()) ? BigDecimal.ZERO
: loanTransactionDTO.getPenalties();
final BigDecimal principalPaid = Objects.isNull(loanTransactionDTO.getPrincipalPaid()) ? BigDecimal.ZERO
: loanTransactionDTO.getPrincipalPaid();
final BigDecimal feePaid = Objects.isNull(loanTransactionDTO.getFeePaid()) ? BigDecimal.ZERO : loanTransactionDTO.getFeePaid();
final BigDecimal penaltyPaid = Objects.isNull(loanTransactionDTO.getPenaltyPaid()) ? BigDecimal.ZERO
: loanTransactionDTO.getPenaltyPaid();
helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, amount, isReversal);
if (overpaidAmount.compareTo(BigDecimal.ZERO) > 0) {
helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overpaidAmount, isReversal);
}
if (principalCredited.compareTo(principalPaid) > 0) {
helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalCredited.subtract(principalPaid),
isReversal);
} else if (principalCredited.compareTo(principalPaid) < 0) {
helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalPaid.subtract(principalCredited),
isReversal);
}
if (feeCredited.compareTo(feePaid) > 0) {
helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, feeCredited.subtract(feePaid), isReversal);
} else if (feeCredited.compareTo(feePaid) < 0) {
helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, feePaid.subtract(feeCredited), isReversal);
}
if (penaltyCredited.compareTo(penaltyPaid) > 0) {
helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, penaltyCredited.subtract(penaltyPaid),
isReversal);
} else if (penaltyCredited.compareTo(penaltyPaid) < 0) {
helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, penaltyPaid.subtract(penaltyCredited),
isReversal);
}
}
/**
@@ -3194,13 +3194,13 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
);
// verify journal entries
verifyJournalEntries(loanResponse.getLoanId(), journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(50.0, overpaymentAccount, "CREDIT"), //
journalEntry(150.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(150.0, fundSource, "DEBIT"), //
journalEntry(50.0, loansReceivableAccount, "DEBIT"), //
journalEntry(50.0, overpaymentAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
journalEntry(100.0, fundSource, "CREDIT") //
);
});
}
@@ -3273,12 +3273,12 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
);
// verify journal entries
verifyJournalEntries(loanResponse.getLoanId(), journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(50.0, overpaymentAccount, "CREDIT"), //
journalEntry(150.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(150.0, fundSource, "DEBIT"), //
journalEntry(28.0, overpaymentAccount, "DEBIT"), //
journalEntry(28.0, suspenseClearingAccount, "CREDIT") //
journalEntry(28.0, fundSource, "CREDIT") //
);
});
}
@@ -72,6 +72,7 @@ import org.apache.fineract.integrationtests.common.BatchHelper;
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.accounting.Account;
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
@@ -114,6 +115,7 @@ public abstract class BaseLoanIntegrationTest {
protected final LoanProductHelper loanProductHelper = new LoanProductHelper();
protected JournalEntryHelper journalEntryHelper = new JournalEntryHelper(requestSpec, responseSpec);
protected ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
protected SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
protected final InlineLoanCOBHelper inlineLoanCOBHelper = new InlineLoanCOBHelper(requestSpec, responseSpec);
protected BusinessDateHelper businessDateHelper = new BusinessDateHelper();
@@ -123,18 +125,22 @@ public abstract class BaseLoanIntegrationTest {
protected DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
// asset
protected final Account loansReceivableAccount = accountHelper.createAssetAccount();
protected final Account interestFeeReceivableAccount = accountHelper.createAssetAccount();
protected final Account suspenseAccount = accountHelper.createAssetAccount();
protected final Account loansReceivableAccount = accountHelper.createAssetAccount("loanPortfolio");
protected final Account interestReceivableAccount = accountHelper.createAssetAccount("interestReceivable");
protected final Account feeReceivableAccount = accountHelper.createAssetAccount("feeReceivable");
protected final Account penaltyReceivableAccount = accountHelper.createAssetAccount("penaltyReceivable");
protected final Account suspenseAccount = accountHelper.createAssetAccount("suspense");
// liability
protected final Account suspenseClearingAccount = accountHelper.createLiabilityAccount();
protected final Account overpaymentAccount = accountHelper.createLiabilityAccount();
protected final Account fundSource = accountHelper.createLiabilityAccount("fundSource");
protected final Account overpaymentAccount = accountHelper.createLiabilityAccount("overpayment");
// income
protected final Account interestIncomeAccount = accountHelper.createIncomeAccount();
protected final Account feeIncomeAccount = accountHelper.createIncomeAccount();
protected final Account feeChargeOffAccount = accountHelper.createIncomeAccount();
protected final Account recoveriesAccount = accountHelper.createIncomeAccount();
protected final Account interestIncomeChargeOffAccount = accountHelper.createIncomeAccount();
protected final Account interestIncomeAccount = accountHelper.createIncomeAccount("interestIncome");
protected final Account feeIncomeAccount = accountHelper.createIncomeAccount("feeIncome");
protected final Account penaltyIncomeAccount = accountHelper.createIncomeAccount("penaltyIncome");
protected final Account feeChargeOffAccount = accountHelper.createIncomeAccount("feeChargeOff");
protected final Account recoveriesAccount = accountHelper.createIncomeAccount("recoveries");
protected final Account interestIncomeChargeOffAccount = accountHelper.createIncomeAccount("interestIncomeChargeOff");
// expense
protected final Account creditLossBadDebtAccount = accountHelper.createExpenseAccount();
protected final Account creditLossBadDebtFraudAccount = accountHelper.createExpenseAccount();
@@ -159,6 +165,7 @@ public abstract class BaseLoanIntegrationTest {
.includeInBorrowerCycle(false)//
.currencyCode("USD")//
.digitsAfterDecimal(2)//
.inMultiplesOf(0)//
.installmentAmountInMultiplesOf(1)//
.useBorrowerCycle(false)//
.minPrincipal(100.0)//
@@ -207,24 +214,18 @@ public abstract class BaseLoanIntegrationTest {
.outstandingLoanBalance(10000.0)//
.charges(Collections.emptyList())//
.accountingRule(3)//
.fundSourceAccountId(suspenseClearingAccount.getAccountID().longValue())//
.fundSourceAccountId(fundSource.getAccountID().longValue())//
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
.incomeFromFeeAccountId(feeIncomeAccount.getAccountID().longValue())//
.incomeFromPenaltyAccountId(feeIncomeAccount.getAccountID().longValue())//
.incomeFromPenaltyAccountId(penaltyIncomeAccount.getAccountID().longValue())//
.incomeFromRecoveryAccountId(recoveriesAccount.getAccountID().longValue())//
.writeOffAccountId(writtenOffAccount.getAccountID().longValue())//
.overpaymentLiabilityAccountId(overpaymentAccount.getAccountID().longValue())//
.receivableInterestAccountId(interestFeeReceivableAccount.getAccountID().longValue())//
.receivableFeeAccountId(interestFeeReceivableAccount.getAccountID().longValue())//
.receivablePenaltyAccountId(interestFeeReceivableAccount.getAccountID().longValue())//
.dateFormat(DATETIME_PATTERN)//
.locale("en_GB")//
.disallowExpectedDisbursements(true)//
.allowApprovedDisbursedAmountsOverApplied(true)//
.overAppliedCalculationType("percentage")//
.overAppliedNumber(50)//
.receivableInterestAccountId(interestReceivableAccount.getAccountID().longValue())//
.receivableFeeAccountId(feeReceivableAccount.getAccountID().longValue())//
.receivablePenaltyAccountId(penaltyReceivableAccount.getAccountID().longValue())//
.goodwillCreditAccountId(goodwillExpenseAccount.getAccountID().longValue())//
.incomeFromGoodwillCreditInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
.incomeFromGoodwillCreditFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
@@ -233,7 +234,12 @@ public abstract class BaseLoanIntegrationTest {
.incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
.chargeOffExpenseAccountId(creditLossBadDebtAccount.getAccountID().longValue())//
.chargeOffFraudExpenseAccountId(creditLossBadDebtFraudAccount.getAccountID().longValue())//
.incomeFromChargeOffPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue());
.incomeFromChargeOffPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue()).dateFormat(DATETIME_PATTERN)//
.locale("en_GB")//
.disallowExpectedDisbursements(true)//
.allowApprovedDisbursedAmountsOverApplied(true)//
.overAppliedCalculationType("percentage")//
.overAppliedNumber(50);
}
protected PostLoanProductsRequest createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation() {
@@ -344,7 +350,7 @@ public abstract class BaseLoanIntegrationTest {
boolean found = loanDetails.getTransactions().stream().anyMatch(item -> Objects.equals(item.getAmount(), tr.amount) //
&& Objects.equals(item.getType().getValue(), tr.type) //
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
&& Objects.equals(item.getOutstandingLoanBalance(), tr.outstandingAmount) //
&& Objects.equals(item.getOutstandingLoanBalance(), tr.outstandingPrincipal) //
&& Objects.equals(item.getPrincipalPortion(), tr.principalPortion) //
&& Objects.equals(item.getInterestPortion(), tr.interestPortion) //
&& Objects.equals(item.getFeeChargesPortion(), tr.feePortion) //
@@ -416,6 +422,18 @@ public abstract class BaseLoanIntegrationTest {
});
}
protected void verifyTRJournalEntries(Long transactionId, Journal... entries) {
GetJournalEntriesTransactionIdResponse journalEntriesForLoan = journalEntryHelper.getJournalEntries("L" + transactionId.toString());
Assertions.assertEquals(entries.length, journalEntriesForLoan.getPageItems().size());
Arrays.stream(entries).forEach(journalEntry -> {
boolean found = journalEntriesForLoan.getPageItems().stream()
.anyMatch(item -> Objects.equals(item.getAmount(), journalEntry.amount)
&& Objects.equals(item.getGlAccountId(), journalEntry.account.getAccountID().longValue())
&& Objects.requireNonNull(item.getEntryType()).getValue().equals(journalEntry.type));
Assertions.assertTrue(found, "Required journal entry not found: " + journalEntry);
});
}
protected Long addCharge(Long loanId, boolean isPenalty, double amount, String dueDate) {
Integer chargeId = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, String.valueOf(amount), isPenalty));
@@ -602,9 +620,10 @@ public abstract class BaseLoanIntegrationTest {
return response.getResourceId();
}
protected void addChargebackForLoan(Long loanId, Long transactionId, Double amount) {
loanTransactionHelper.chargebackLoanTransaction(loanId, transactionId,
protected Long addChargebackForLoan(Long loanId, Long transactionId, Double amount) {
PostLoansLoanIdTransactionsResponse response = loanTransactionHelper.chargebackLoanTransaction(loanId, transactionId,
new PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(amount).paymentTypeId(1L));
return response.getResourceId();
}
protected PostChargesResponse createCharge(Double amount) {
@@ -627,8 +646,22 @@ public abstract class BaseLoanIntegrationTest {
new BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en"));
}
protected Journal journalEntry(double principalAmount, Account account, String type) {
return new Journal(principalAmount, account, type);
protected Long getTransactionId(Long loanId, String type, String date) {
GetLoansLoanIdResponse loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
return loan.getTransactions().stream().filter(tr -> Objects.equals(tr.getType().getValue(), type)
&& Objects.equals(tr.getDate(), LocalDate.parse(date, dateTimeFormatter))).findAny().orElseThrow().getId();
}
protected Journal journalEntry(double amount, Account account, String type) {
return new Journal(amount, account, type);
}
protected Journal debit(Account account, double amount) {
return new Journal(amount, account, "DEBIT");
}
protected Journal credit(Account account, double amount) {
return new Journal(amount, account, "CREDIT");
}
protected Transaction transaction(double principalAmount, String type, String date) {
@@ -639,16 +672,16 @@ public abstract class BaseLoanIntegrationTest {
return new Transaction(principalAmount, type, date, true);
}
protected TransactionExt transaction(double amount, String type, String date, double outstandingAmount, double principalPortion,
protected TransactionExt transaction(double amount, String type, String date, double outstandingPrincipal, double principalPortion,
double interestPortion, double feePortion, double penaltyPortion, double unrecognizedIncomePortion, double overpaymentPortion) {
return new TransactionExt(amount, type, date, outstandingAmount, principalPortion, interestPortion, feePortion, penaltyPortion,
return new TransactionExt(amount, type, date, outstandingPrincipal, principalPortion, interestPortion, feePortion, penaltyPortion,
unrecognizedIncomePortion, overpaymentPortion, false);
}
protected TransactionExt transaction(double amount, String type, String date, double outstandingAmount, double principalPortion,
protected TransactionExt transaction(double amount, String type, String date, double outstandingPrincipal, double principalPortion,
double interestPortion, double feePortion, double penaltyPortion, double unrecognizedIncomePortion, double overpaymentPortion,
boolean reversed) {
return new TransactionExt(amount, type, date, outstandingAmount, principalPortion, interestPortion, feePortion, penaltyPortion,
return new TransactionExt(amount, type, date, outstandingPrincipal, principalPortion, interestPortion, feePortion, penaltyPortion,
unrecognizedIncomePortion, overpaymentPortion, reversed);
}
@@ -765,7 +798,7 @@ public abstract class BaseLoanIntegrationTest {
Double amount;
String type;
String date;
Double outstandingAmount;
Double outstandingPrincipal;
Double principalPortion;
Double interestPortion;
Double feePortion;
@@ -1011,6 +1011,455 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
});
}
@Test
public void testAccountingChargebackOnPrincipal() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProduct(//
createDefaultPaymentAllocation(), //
chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
// Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
verifyRepaymentSchedule(loanId, //
installment(0, null, "01 January 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 March 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
);
// Repayment #1
updateBusinessDate("01 February 2023");
Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
// Repayment #2
updateBusinessDate("01 March 2023");
Long repaymentTransaction2 = addRepaymentForLoan(loanId, 250.0, "01 March 2023");
// Repayment #3
updateBusinessDate("30 March 2023");
Long repaymentTransaction3 = addRepaymentForLoan(loanId, 250.0, "30 March 2023");
// Chargeback 250
Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 250.0);
verifyTransactions(loanId, //
transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "30 March 2023", 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Chargeback", "30 March 2023", 250, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0) //
);
// Verify GL entries
verifyTRJournalEntries(repaymentTransaction1, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250)//
);
verifyTRJournalEntries(repaymentTransaction2, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250) //
);
verifyTRJournalEntries(repaymentTransaction3, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250)//
);
verifyTRJournalEntries(chargeback, //
debit(loansReceivableAccount, 250), //
credit(fundSource, 250) //
);
});
}
@Test
public void testAccountingChargebackOnPrincipalAndFees() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProduct(//
createDefaultPaymentAllocation(), //
chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
// Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
Long feeId = addCharge(loanId, false, 30, "15 February 2023");
verifyRepaymentSchedule(loanId, //
installment(0, null, "01 January 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
installment(250.0, 0, 30, 0, 280.0, false, "01 March 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
);
// Repayment #1
updateBusinessDate("01 February 2023");
Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
// Repayment #2
updateBusinessDate("01 March 2023");
Long repaymentTransaction2 = addRepaymentForLoan(loanId, 280.0, "01 March 2023");
// Run periodic accrual
schedulerJobHelper.executeAndAwaitJob("Add Accrual Transactions");
// Repayment #3
updateBusinessDate("30 March 2023");
Long repaymentTransaction3 = addRepaymentForLoan(loanId, 250.0, "30 March 2023");
// Chargeback 250
Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 280.0);
verifyTransactions(loanId, //
transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(280.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 30.0, 0.0, 0.0, 0.0), //
transaction(30.0, "Accrual", "01 March 2023", 0.0, 0.0, 0.0, 30.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "30 March 2023", 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(280.0, "Chargeback", "30 March 2023", 250, 250.0, 0.0, 30.0, 0.0, 0.0, 0.0) //
);
// Verify GL entries
verifyTRJournalEntries(repaymentTransaction1, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250) //
);
verifyTRJournalEntries(repaymentTransaction2, //
debit(fundSource, 280), //
credit(loansReceivableAccount, 250), //
credit(feeReceivableAccount, 30)//
);
verifyTRJournalEntries(repaymentTransaction3, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250)//
);
verifyTRJournalEntries(chargeback, //
debit(loansReceivableAccount, 250), //
debit(feeReceivableAccount, 30), //
credit(fundSource, 280) //
);
});
}
@Test
public void testAccountingChargebackOnPrincipalAndPenalties() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProduct(//
createDefaultPaymentAllocation(), //
chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
// Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
Long feeId = addCharge(loanId, true, 30, "15 February 2023");
verifyRepaymentSchedule(loanId, //
installment(0, null, "01 January 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
installment(250.0, 0, 0, 30.0, 280.0, false, "01 March 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
);
// Repayment #1
updateBusinessDate("01 February 2023");
Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
// Repayment #2
updateBusinessDate("01 March 2023");
Long repaymentTransaction2 = addRepaymentForLoan(loanId, 280.0, "01 March 2023");
// Run periodic accrual
schedulerJobHelper.executeAndAwaitJob("Add Accrual Transactions");
// Repayment #3
updateBusinessDate("30 March 2023");
Long repaymentTransaction3 = addRepaymentForLoan(loanId, 250.0, "30 March 2023");
// Chargeback 250
Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 280.0);
verifyTransactions(loanId, //
transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(280.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 0.0, 30.0, 0.0, 0.0), //
transaction(30.0, "Accrual", "01 March 2023", 0.0, 0.0, 0.0, 0.0, 30.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "30 March 2023", 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(280.0, "Chargeback", "30 March 2023", 250, 250.0, 0.0, 0.0, 30.0, 0.0, 0.0) //
);
// Verify GL entries
verifyTRJournalEntries(repaymentTransaction1, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250) //
);
verifyTRJournalEntries(repaymentTransaction2, //
debit(fundSource, 280), //
credit(loansReceivableAccount, 250), //
credit(penaltyReceivableAccount, 30)//
);
verifyTRJournalEntries(repaymentTransaction3, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250)//
);
verifyTRJournalEntries(chargeback, //
debit(loansReceivableAccount, 250), //
debit(penaltyReceivableAccount, 30), //
credit(fundSource, 280) //
);
});
}
@Test
public void testAccountingOverpaymentAmountIsSmallerThanChargeback() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProduct(//
createDefaultPaymentAllocation(), //
chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
// Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
verifyRepaymentSchedule(loanId, //
installment(0, null, "01 January 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 March 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
);
// Repayment #1
updateBusinessDate("01 February 2023");
Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
// Repayment #2
updateBusinessDate("01 March 2023");
Long repaymentTransaction2 = addRepaymentForLoan(loanId, 250.0, "01 March 2023");
// Repayment #3
updateBusinessDate("30 March 2023");
Long repaymentTransaction3 = addRepaymentForLoan(loanId, 400.0, "30 March 2023");
// Chargeback 250
updateBusinessDate("31 March 2023");
Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 250.0);
verifyTransactions(loanId, //
transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(400.0, "Repayment", "30 March 2023", 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Chargeback", "31 March 2023", 100, 250.0, 0.0, 0.0, 0.0, 0.0, 150.0) //
);
// Verify GL entries
verifyTRJournalEntries(repaymentTransaction1, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250) //
);
verifyTRJournalEntries(repaymentTransaction2, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250) //
);
verifyTRJournalEntries(repaymentTransaction3, //
debit(fundSource, 400), //
credit(loansReceivableAccount, 250), //
credit(overpaymentAccount, 150) //
);
verifyTRJournalEntries(chargeback, //
debit(loansReceivableAccount, 100), //
debit(overpaymentAccount, 150), //
credit(fundSource, 250) //
);
});
}
@Test
public void testAccountingOverpaymentAmountIsBiggerThanChargeback() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProduct(//
createDefaultPaymentAllocation(), //
chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
// Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
verifyRepaymentSchedule(loanId, //
installment(0, null, "01 January 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 March 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
);
// Repayment #1
updateBusinessDate("01 February 2023");
Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
// Repayment #2
updateBusinessDate("01 March 2023");
Long repaymentTransaction2 = addRepaymentForLoan(loanId, 250.0, "01 March 2023");
// Repayment #3
updateBusinessDate("30 March 2023");
Long repaymentTransaction3 = addRepaymentForLoan(loanId, 400.0, "30 March 2023");
// Chargeback 250
updateBusinessDate("31 March 2023");
Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 100.0);
verifyTransactions(loanId, //
transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(400.0, "Repayment", "30 March 2023", 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(100.0, "Chargeback", "31 March 2023", 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, -50.0) //
);
// Verify GL entries
verifyTRJournalEntries(repaymentTransaction1, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250) //
);
verifyTRJournalEntries(repaymentTransaction2, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250) //
);
verifyTRJournalEntries(repaymentTransaction3, //
debit(fundSource, 400), //
credit(loansReceivableAccount, 250), //
credit(overpaymentAccount, 150) //
);
verifyTRJournalEntries(chargeback, //
debit(overpaymentAccount, 100), //
credit(fundSource, 100) //
);
});
}
@Test
public void testAccountingOverpaidLoansWithFeesWhenOverpaymentAmountIsBiggerThanChargeback() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProduct(//
createDefaultPaymentAllocation(), //
chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
// Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
verifyRepaymentSchedule(loanId, //
installment(0, null, "01 January 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 March 2023"), //
installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
);
// Repayment #1
updateBusinessDate("01 February 2023");
Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
// Add fee & Repayment #2
updateBusinessDate("01 March 2023");
Long feeId = addCharge(loanId, false, 30, "01 March 2023");
Long repaymentTransaction2 = addRepaymentForLoan(loanId, 280.0, "01 March 2023");
schedulerJobHelper.executeAndAwaitJob("Add Accrual Transactions");
// Repayment #3
updateBusinessDate("30 March 2023");
Long repaymentTransaction3 = addRepaymentForLoan(loanId, 400.0, "30 March 2023");
// Chargeback 250
updateBusinessDate("31 March 2023");
Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 100.0);
verifyTransactions(loanId, //
transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(280.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 30.0, 0.0, 0.0, 0.0), //
transaction(30.0, "Accrual", "01 March 2023", 0.0, 0.0, 0.0, 30.0, 0.0, 0.0, 0.0), //
transaction(400.0, "Repayment", "30 March 2023", 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(100.0, "Chargeback", "31 March 2023", 0.0, 70.0, 0.0, 30.0, 0.0, 0.0, -50.0) //
);
// Verify GL entries
verifyTRJournalEntries(repaymentTransaction1, //
debit(fundSource, 250), //
credit(loansReceivableAccount, 250) //
);
verifyTRJournalEntries(repaymentTransaction2, //
debit(fundSource, 280), //
credit(loansReceivableAccount, 250), //
credit(feeReceivableAccount, 30) //
);
verifyTRJournalEntries(getTransactionId(loanId, "Accrual", "01 March 2023"), //
debit(feeReceivableAccount, 30), //
credit(feeIncomeAccount, 30) //
);
verifyTRJournalEntries(repaymentTransaction3, //
debit(fundSource, 400), //
credit(loansReceivableAccount, 250), //
credit(overpaymentAccount, 150) //
);
verifyTRJournalEntries(chargeback, //
debit(overpaymentAccount, 100), //
credit(fundSource, 100) //
);
});
}
private void verifyLoanSummaryAmounts(Long loanId, double creditedPrincipal, double creditedFee, double creditedPenalty,
double totalOutstanding) {
GetLoansLoanIdResponse loanResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
@@ -1023,10 +1472,10 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
}
@Nullable
private Long applyAndApproveLoan(Long clientId, Long loanProductId) {
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductId, "01 January 2023", 1250.0, 4)//
private Long applyAndApproveLoan(Long clientId, Long loanProductId, int numberOfRepayments) {
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductId, "01 January 2023", 1250.0, numberOfRepayments)//
.repaymentEvery(1)//
.loanTermFrequency(4)//
.loanTermFrequency(numberOfRepayments)//
.repaymentFrequencyType(RepaymentFrequencyType.MONTHS)//
.loanTermFrequencyType(RepaymentFrequencyType.MONTHS)//
.transactionProcessingStrategyCode("advanced-payment-allocation-strategy");
@@ -1036,8 +1485,12 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
approveLoanRequest(1250.0, "01 January 2023"));
Long loanId = approvedLoanResult.getLoanId();
return loanId;
return approvedLoanResult.getLoanId();
}
@Nullable
private Long applyAndApproveLoan(Long clientId, Long loanProductId) {
return applyAndApproveLoan(clientId, loanProductId, 4);
}
public Long createLoanProduct(AdvancedPaymentData defaultAllocation, CreditAllocationData creditAllocationData) {
@@ -23,7 +23,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.math.BigDecimal;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
import org.apache.fineract.client.models.PostLoanProductsRequest;
@@ -88,15 +87,10 @@ public class LoanDownPaymentTransactionChargebackTest extends BaseLoanIntegratio
);
// verify journal entries for chargeback transaction
GetJournalEntriesTransactionIdResponse journalEntries = journalEntryHelper
.getJournalEntries("L" + chargebackTransactionId.toString());
assertEquals(2L, journalEntries.getTotalFilteredRecords());
assertEquals(50.0, journalEntries.getPageItems().get(0).getAmount());
assertEquals("CREDIT", journalEntries.getPageItems().get(0).getEntryType().getValue());
assertEquals(50.0, journalEntries.getPageItems().get(1).getAmount());
assertEquals("DEBIT", journalEntries.getPageItems().get(1).getEntryType().getValue());
verifyTRJournalEntries(chargebackTransactionId, //
credit(fundSource, 50.0), //
debit(loansReceivableAccount, 50.0) //
);
});
}
@@ -152,15 +146,10 @@ public class LoanDownPaymentTransactionChargebackTest extends BaseLoanIntegratio
);
// verify journal entries for chargeback transaction
GetJournalEntriesTransactionIdResponse journalEntries = journalEntryHelper
.getJournalEntries("L" + chargebackTransactionId.toString());
assertEquals(2L, journalEntries.getTotalFilteredRecords());
assertEquals(50.0, journalEntries.getPageItems().get(0).getAmount());
assertEquals("CREDIT", journalEntries.getPageItems().get(0).getEntryType().getValue());
assertEquals(50.0, journalEntries.getPageItems().get(1).getAmount());
assertEquals("DEBIT", journalEntries.getPageItems().get(1).getEntryType().getValue());
verifyTRJournalEntries(chargebackTransactionId, //
credit(fundSource, 50.0), //
debit(loansReceivableAccount, 50.0) //
);
});
}
@@ -1252,10 +1252,11 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
transaction(1000.0, "Disbursement", "03 March 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Down Payment", "03 March 2023", 750.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0) //
);
verifyJournalEntries(loanResponse.getResourceId(), journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
verifyJournalEntries(loanResponse.getResourceId(), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT") //
journalEntry(250.0, fundSource, "DEBIT") //
);
loanTransactionHelper.makeLoanRepayment(loanResponse.getResourceId(), new PostLoansLoanIdTransactionsRequest()
@@ -1464,10 +1465,11 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
transaction(1000.0, "Disbursement", "03 March 2023", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(250.0, "Down Payment", "03 March 2023", 750.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0) //
);
verifyJournalEntries(loanResponse.getResourceId(), journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
verifyJournalEntries(loanResponse.getResourceId(), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT") //
journalEntry(250.0, fundSource, "DEBIT") //
);
String externalId = UUID.randomUUID().toString();
@@ -74,9 +74,9 @@ public class LoanRescheduleTestWithDownpayment extends BaseLoanIntegrationTest {
// verify journal entries
verifyJournalEntries(loanId, journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.integrationtests;
import static org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder.ACCRUAL_PERIODIC;
import static org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder.DEFAULT_STRATEGY;
import static org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -39,7 +40,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
@@ -53,7 +53,6 @@ import org.apache.fineract.integrationtests.common.BusinessDateHelper;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.accounting.Account;
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
import org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
@@ -72,7 +71,7 @@ import org.junit.jupiter.params.provider.MethodSource;
@Slf4j
@ExtendWith(LoanTestLifecycleExtension.class)
public class LoanTransactionChargebackTest {
public class LoanTransactionChargebackTest extends BaseLoanIntegrationTest {
private ResponseSpecification responseSpec;
private ResponseSpecification responseSpecErr400;
@@ -139,14 +138,10 @@ public class LoanTransactionChargebackTest {
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, amount.doubleValue());
GetJournalEntriesTransactionIdResponse journalEntries = journalEntryHelper
.getJournalEntries("L" + chargebackTransactionId.toString());
assertEquals(2L, journalEntries.getTotalFilteredRecords());
assertEquals(1000.0, journalEntries.getPageItems().get(0).getAmount());
assertEquals("CREDIT", journalEntries.getPageItems().get(0).getEntryType().getValue());
assertEquals(1000.0, journalEntries.getPageItems().get(1).getAmount());
assertEquals("DEBIT", journalEntries.getPageItems().get(1).getEntryType().getValue());
verifyTRJournalEntries(chargebackTransactionId, //
credit(fundSource, 1000.0), //
debit(loansReceivableAccount, 1000.0) //
);
// Try to reverse a Loan Transaction charge back
PostLoansLoanIdTransactionsResponse reverseTransactionResponse = loanTransactionHelper.reverseLoanTransaction(loanId,
@@ -431,17 +426,11 @@ public class LoanTransactionChargebackTest {
assertEquals(getLoansLoanIdResponse.getTimeline().getExpectedMaturityDate(),
getLoansLoanIdResponse.getTimeline().getActualMaturityDate());
GetJournalEntriesTransactionIdResponse journalEntries = journalEntryHelper
.getJournalEntries("L" + chargebackTransactionId.toString());
assertEquals(3L, journalEntries.getTotalFilteredRecords());
assertEquals(100.0, journalEntries.getPageItems().get(0).getAmount());
assertEquals("DEBIT", journalEntries.getPageItems().get(0).getEntryType().getValue());
assertEquals(200.0, journalEntries.getPageItems().get(1).getAmount());
assertEquals("CREDIT", journalEntries.getPageItems().get(1).getEntryType().getValue());
assertEquals(100.0, journalEntries.getPageItems().get(2).getAmount());
assertEquals("DEBIT", journalEntries.getPageItems().get(2).getEntryType().getValue());
verifyTRJournalEntries(chargebackTransactionId, //
credit(fundSource, 200.0), //
debit(loansReceivableAccount, 100.0), //
debit(overpaymentAccount, 100.0) //
);
final GetDelinquencyRangesResponse delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
assertNull(delinquencyRange);
@@ -525,14 +514,10 @@ public class LoanTransactionChargebackTest {
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, Double.valueOf("0.00"));
GetJournalEntriesTransactionIdResponse journalEntries = journalEntryHelper
.getJournalEntries("L" + chargebackTransactionId.toString());
assertEquals(2L, journalEntries.getTotalFilteredRecords());
assertEquals(50.0, journalEntries.getPageItems().get(0).getAmount());
assertEquals("CREDIT", journalEntries.getPageItems().get(0).getEntryType().getValue());
assertEquals(50.0, journalEntries.getPageItems().get(1).getAmount());
assertEquals("DEBIT", journalEntries.getPageItems().get(1).getEntryType().getValue());
verifyTRJournalEntries(chargebackTransactionId, //
credit(fundSource, 50.0), //
debit(overpaymentAccount, 50.0) //
);
}
@ParameterizedTest
@@ -632,12 +617,20 @@ public class LoanTransactionChargebackTest {
final Integer delinquencyBucketId, final boolean withJournalEntries, LoanProductTestBuilder loanProductTestBuilder) {
final HashMap<String, Object> loanProductMap;
if (withJournalEntries) {
final Account assetAccount = accountHelper.createAssetAccount();
final Account expenseAccount = accountHelper.createExpenseAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
final Account overpaymentAccount = accountHelper.createLiabilityAccount();
loanProductMap = loanProductTestBuilder
.withAccountingRulePeriodicAccrual(new Account[] { assetAccount, expenseAccount, incomeAccount, overpaymentAccount })
.withFullAccountingConfig(ACCRUAL_PERIODIC,
LoanProductTestBuilder.FullAccountingConfig.builder().fundSourceAccountId(fundSource.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(interestReceivableAccount.getAccountID().longValue())//
.receivablePenaltyAccountId(interestReceivableAccount.getAccountID().longValue()).build())
.build(null, delinquencyBucketId);
} else {
loanProductMap = loanProductTestBuilder.build(null, delinquencyBucketId);
@@ -69,9 +69,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -91,14 +91,14 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// original entries
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// original entries reverted
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT")); //
journalEntry(1000.0, fundSource, "DEBIT")); //
verifyRepaymentSchedule(loanId, //
installment(1000.0, null, "01 January 2023"), //
@@ -139,9 +139,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// make a repayment
@@ -164,23 +164,23 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId, //
// original entries down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// repayment entries
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
// original entries compensated
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "DEBIT"), //
// repayment entries compensated
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT") //
journalEntry(250.0, fundSource, "CREDIT") //
);
verifyRepaymentSchedule(loanId, //
@@ -225,9 +225,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// undoDisbursal
@@ -240,15 +240,15 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId, //
// original entries
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// original entries are compensated
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT") //
journalEntry(1000.0, fundSource, "DEBIT") //
);
// verify repayment entries are reverted
@@ -298,11 +298,11 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -322,23 +322,23 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId, //
// original entries
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// original entries compensated
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "DEBIT"), //
// manual partial repayment of the first installment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
// manual partial repayment of the first installment compensation after undoDisburse
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
journalEntry(100.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -381,9 +381,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -429,9 +429,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -476,9 +476,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -498,14 +498,14 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId, //
// original entries
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// original entries reverted
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT") //
journalEntry(1000.0, fundSource, "DEBIT") //
);
// Verify Repayment Schedule
@@ -548,9 +548,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// make a repayment
@@ -573,23 +573,23 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// original entries down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// repayment entries
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
// original entries compensated
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "DEBIT"), //
// repayment entries compensated
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT") //
journalEntry(250.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -635,9 +635,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// undoDisbursal
@@ -650,15 +650,15 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// original entries
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// original entries are compensated
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT") //
journalEntry(1000.0, fundSource, "DEBIT") //
);
// Verify Repayment Schedule
@@ -707,11 +707,11 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -731,23 +731,23 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// original entries
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// original entries compensated
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "DEBIT"), //
// manual partial repayment of the first installment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
// manual partial repayment of the first installment compensation after undoDisburse
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
journalEntry(100.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -787,9 +787,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -815,13 +815,13 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -846,21 +846,21 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// first disbursement + down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// second disbursement + down-payment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(400.0, fundSource, "CREDIT"), //
// compensation of second disbursement + down-payment
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
journalEntry(400.0, fundSource, "DEBIT") //
);
verifyRepaymentSchedule(loanId, //
@@ -901,9 +901,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -941,15 +941,15 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(300.0, loansReceivableAccount, "CREDIT"), //
journalEntry(300.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(300.0, fundSource, "DEBIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -975,25 +975,25 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// first disbursement + down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// repayment
journalEntry(300.0, loansReceivableAccount, "CREDIT"), //
journalEntry(300.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(300.0, fundSource, "DEBIT"), //
// second disbursement + down-payment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(400.0, fundSource, "CREDIT"), //
// compensation of second disbursement + down-payment
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
journalEntry(400.0, fundSource, "DEBIT") //
);
verifyRepaymentSchedule(loanId, //
@@ -1038,9 +1038,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1070,13 +1070,13 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1101,21 +1101,21 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// first disbursement + down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// second disbursement + down-payment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(400.0, fundSource, "CREDIT"), //
// compensation of second disbursement + down-payment
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
journalEntry(400.0, fundSource, "DEBIT") //
);
verifyRepaymentSchedule(loanId, //
@@ -1157,9 +1157,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1186,13 +1186,13 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1249,9 +1249,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1281,13 +1281,13 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1341,9 +1341,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1370,13 +1370,13 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1404,27 +1404,27 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// 1st disbursal + down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// 2nd disbursal + down-payment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(400.0, fundSource, "CREDIT"), //
// compensation of the 1st disbursal + down-payment
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "DEBIT"), //
// compensation of the 2nd disbursal + down-payment
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
journalEntry(400.0, fundSource, "DEBIT") //
);
});
}
@@ -1463,9 +1463,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1495,13 +1495,13 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1529,27 +1529,27 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// 1st disbursal + down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// 2nd disbursal + down-payment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(400.0, fundSource, "CREDIT"), //
// compensation of the 1st disbursal + down-payment
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "DEBIT"), //
// compensation of the 2nd disbursal + down-payment
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
journalEntry(400.0, fundSource, "DEBIT") //
);
});
}
@@ -1585,9 +1585,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1614,13 +1614,13 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1654,35 +1654,35 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// 1st disbursal + down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// 2nd disbursal + down-payment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(400.0, fundSource, "CREDIT"), //
// manual repayment
journalEntry(50.0, loansReceivableAccount, "CREDIT"), //
journalEntry(50.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(50.0, fundSource, "DEBIT"), //
// compensation of the 1st disbursal + down-payment
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "DEBIT"), //
// compensation of the 2nd disbursal + down-payment
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
journalEntry(400.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(400.0, fundSource, "DEBIT"), //
// compensation of repayment
journalEntry(50.0, loansReceivableAccount, "DEBIT"), //
journalEntry(50.0, suspenseClearingAccount, "CREDIT") //
journalEntry(50.0, fundSource, "CREDIT") //
);
});
}
@@ -1721,9 +1721,9 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, //
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
journalEntry(1000.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1752,13 +1752,13 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
// verify journal entries
verifyJournalEntries(loanId, journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
journalEntry(400.0, fundSource, "CREDIT") //
);
// Verify Repayment Schedule
@@ -1792,35 +1792,35 @@ public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanInt
verifyJournalEntries(loanId,
// 1st disbursal + down-payment
journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(250.0, fundSource, "DEBIT"), //
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(1000.0, fundSource, "CREDIT"), //
// 2nd disbursal + down-payment
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(100.0, fundSource, "DEBIT"), //
journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(400.0, fundSource, "CREDIT"), //
// manual repayment
journalEntry(50.0, loansReceivableAccount, "CREDIT"), //
journalEntry(50.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(50.0, fundSource, "DEBIT"), //
// compensation of the 1st disbursal + down-payment
journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(250.0, fundSource, "CREDIT"), //
journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(1000.0, fundSource, "DEBIT"), //
// compensation of the 2nd disbursal + down-payment
journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, fundSource, "CREDIT"), //
journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
journalEntry(400.0, suspenseClearingAccount, "DEBIT"), //
journalEntry(400.0, fundSource, "DEBIT"), //
// compensation of repayment
journalEntry(50.0, loansReceivableAccount, "DEBIT"), //
journalEntry(50.0, suspenseClearingAccount, "CREDIT") //
journalEntry(50.0, fundSource, "CREDIT") //
);
});
}
@@ -42,35 +42,55 @@ public class AccountHelper extends IntegrationTest {
}
public Account createAssetAccount() {
final String assetAccountJSON = new GLAccountBuilder().withAccountTypeAsAsset().build();
return this.createAssetAccount(null);
}
public Account createIncomeAccount() {
return this.createIncomeAccount(null);
}
public Account createExpenseAccount() {
return this.createExpenseAccount(null);
}
public Account createLiabilityAccount() {
return this.createLiabilityAccount(null);
}
public Account createEquityAccount() {
return this.createEquityAccount(null);
}
public Account createAssetAccount(String accountName) {
final String assetAccountJSON = new GLAccountBuilder().withName(accountName).withAccountTypeAsAsset().build();
final Integer accountID = Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_GL_ACCOUNT_URL, assetAccountJSON,
GL_ACCOUNT_ID_RESPONSE);
return new Account(accountID, Account.AccountType.ASSET);
}
public Account createIncomeAccount() {
final String assetAccountJSON = new GLAccountBuilder().withAccountTypeAsIncome().build();
public Account createIncomeAccount(String accountName) {
final String assetAccountJSON = new GLAccountBuilder().withName(accountName).withAccountTypeAsIncome().build();
final Integer accountID = Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_GL_ACCOUNT_URL, assetAccountJSON,
GL_ACCOUNT_ID_RESPONSE);
return new Account(accountID, Account.AccountType.INCOME);
}
public Account createExpenseAccount() {
final String assetAccountJSON = new GLAccountBuilder().withAccountTypeAsExpense().build();
public Account createExpenseAccount(String accountName) {
final String assetAccountJSON = new GLAccountBuilder().withName(accountName).withAccountTypeAsExpense().build();
final Integer accountID = Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_GL_ACCOUNT_URL, assetAccountJSON,
GL_ACCOUNT_ID_RESPONSE);
return new Account(accountID, Account.AccountType.EXPENSE);
}
public Account createLiabilityAccount() {
final String liabilityAccountJSON = new GLAccountBuilder().withAccountTypeAsLiability().build();
public Account createLiabilityAccount(String accountName) {
final String liabilityAccountJSON = new GLAccountBuilder().withName(accountName).withAccountTypeAsLiability().build();
final Integer accountID = Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_GL_ACCOUNT_URL, liabilityAccountJSON,
GL_ACCOUNT_ID_RESPONSE);
return new Account(accountID, Account.AccountType.LIABILITY);
}
public Account createEquityAccount() {
final String equityAccountJSON = new GLAccountBuilder().withAccountTypeAsAsEquity().build();
public Account createEquityAccount(String accountName) {
final String equityAccountJSON = new GLAccountBuilder().withName(accountName).withAccountTypeAsAsEquity().build();
final Integer accountID = Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_GL_ACCOUNT_URL, equityAccountJSON,
GL_ACCOUNT_ID_RESPONSE);
return new Account(accountID, Account.AccountType.EQUITY);
@@ -21,6 +21,7 @@ package org.apache.fineract.integrationtests.common.accounting;
import com.google.gson.Gson;
import java.util.Calendar;
import java.util.HashMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.integrationtests.common.Utils;
public class GLAccountBuilder {
@@ -86,6 +87,13 @@ public class GLAccountBuilder {
return this;
}
public GLAccountBuilder withName(String name) {
if (StringUtils.isNotBlank(name)) {
this.name = Utils.uniqueRandomStringGenerator(name + "_", 5);
}
return this;
}
public GLAccountBuilder withAccountUsageAsHeader() {
accountUsage = ACCOUNT_USAGE_HEADER;
return this;
@@ -25,6 +25,8 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.Builder;
import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.CreditAllocationData;
import org.apache.fineract.integrationtests.common.Utils;
@@ -69,10 +71,10 @@ public class LoanProductTestBuilder {
public static final String RECALCULATION_COMPOUNDING_METHOD_FEE = "2";
public static final String RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE = "3";
private static final String NONE = "1";
private static final String CASH_BASED = "2";
private static final String ACCRUAL_PERIODIC = "3";
private static final String ACCRUAL_UPFRONT = "4";
public static final String NONE = "1";
public static final String CASH_BASED = "2";
public static final String ACCRUAL_PERIODIC = "3";
public static final String ACCRUAL_UPFRONT = "4";
public static final String INTEREST_APPLICABLE_STRATEGY_REST_DATE = "2";
public static final String INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE = "1";
@@ -157,6 +159,7 @@ public class LoanProductTestBuilder {
private Integer repaymentStartDateType = null;
private String loanScheduleType = LoanScheduleType.CUMULATIVE.name();
private String loanScheduleProcessingType = LoanScheduleProcessingType.HORIZONTAL.name();
private FullAccountingConfig fullAccountingConfig;
public String build() {
final HashMap<String, Object> map = build(null, null);
@@ -231,7 +234,9 @@ public class LoanProductTestBuilder {
map.put("outstandingLoanBalance", this.outstandingLoanBalance);
}
if (this.accountingRule.equals(ACCRUAL_UPFRONT) || this.accountingRule.equals(ACCRUAL_PERIODIC)) {
if (this.fullAccountingConfig != null) {
map.putAll(this.fullAccountingConfig.toMap());
} else if (this.accountingRule.equals(ACCRUAL_UPFRONT) || this.accountingRule.equals(ACCRUAL_PERIODIC)) {
map.putAll(getAccountMappingForAccrualBased(this.feeAndPenaltyAssetAccount));
} else if (this.accountingRule.equals(CASH_BASED)) {
map.putAll(getAccountMappingForCashBased());
@@ -487,6 +492,13 @@ public class LoanProductTestBuilder {
return this;
}
public LoanProductTestBuilder withFullAccountingConfig(String accountingRule, FullAccountingConfig fullAccountingConfig) {
this.accountingRule = accountingRule;
this.fullAccountingConfig = fullAccountingConfig;
this.accountList = null;
return this;
}
private Map<String, String> getAccountMappingForCashBased() {
final Map<String, String> map = new HashMap<>();
for (int i = 0; i < this.accountList.length; i++) {
@@ -768,4 +780,78 @@ public class LoanProductTestBuilder {
return transactionProcessingStrategyCode;
}
@Builder
public static class FullAccountingConfig {
private final Long fundSourceAccountId;
private final Long loanPortfolioAccountId;
private final Long transfersInSuspenseAccountId;
private final Long interestOnLoanAccountId;
private final Long incomeFromFeeAccountId;
private final Long incomeFromPenaltyAccountId;
private final Long incomeFromRecoveryAccountId;
private final Long writeOffAccountId;
private final Long overpaymentLiabilityAccountId;
private final Long receivableInterestAccountId;
private final Long receivableFeeAccountId;
private final Long receivablePenaltyAccountId;
private final Long goodwillCreditAccountId;
private final Long incomeFromGoodwillCreditInterestAccountId;
private final Long incomeFromGoodwillCreditFeesAccountId;
private final Long incomeFromGoodwillCreditPenaltyAccountId;
private final Long incomeFromChargeOffInterestAccountId;
private final Long incomeFromChargeOffFeesAccountId;
private final Long chargeOffExpenseAccountId;
private final Long chargeOffFraudExpenseAccountId;
private final Long incomeFromChargeOffPenaltyAccountId;
private final Long accountingRule;
public Map<String, String> toMap() {
Map<String, String> map = new HashMap<>();
Optional.ofNullable(fundSourceAccountId)
.ifPresent(fundSourceAccountId -> map.put("fundSourceAccountId", Long.toString(fundSourceAccountId)));
Optional.ofNullable(loanPortfolioAccountId)
.ifPresent(loanPortfolioAccountId -> map.put("loanPortfolioAccountId", Long.toString(loanPortfolioAccountId)));
Optional.ofNullable(transfersInSuspenseAccountId).ifPresent(
transfersInSuspenseAccountId -> map.put("transfersInSuspenseAccountId", Long.toString(transfersInSuspenseAccountId)));
Optional.ofNullable(interestOnLoanAccountId)
.ifPresent(interestOnLoanAccountId -> map.put("interestOnLoanAccountId", Long.toString(interestOnLoanAccountId)));
Optional.ofNullable(incomeFromFeeAccountId)
.ifPresent(incomeFromFeeAccountId -> map.put("incomeFromFeeAccountId", Long.toString(incomeFromFeeAccountId)));
Optional.ofNullable(incomeFromPenaltyAccountId).ifPresent(
incomeFromPenaltyAccountId -> map.put("incomeFromPenaltyAccountId", Long.toString(incomeFromPenaltyAccountId)));
Optional.ofNullable(incomeFromRecoveryAccountId).ifPresent(
incomeFromRecoveryAccountId -> map.put("incomeFromRecoveryAccountId", Long.toString(incomeFromRecoveryAccountId)));
Optional.ofNullable(writeOffAccountId)
.ifPresent(writeOffAccountId -> map.put("writeOffAccountId", Long.toString(writeOffAccountId)));
Optional.ofNullable(overpaymentLiabilityAccountId).ifPresent(overpaymentLiabilityAccountId -> map
.put("overpaymentLiabilityAccountId", Long.toString(overpaymentLiabilityAccountId)));
Optional.ofNullable(receivableInterestAccountId).ifPresent(
receivableInterestAccountId -> map.put("receivableInterestAccountId", Long.toString(receivableInterestAccountId)));
Optional.ofNullable(receivableFeeAccountId)
.ifPresent(receivableFeeAccountId -> map.put("receivableFeeAccountId", Long.toString(receivableFeeAccountId)));
Optional.ofNullable(receivablePenaltyAccountId).ifPresent(
receivablePenaltyAccountId -> map.put("receivablePenaltyAccountId", Long.toString(receivablePenaltyAccountId)));
Optional.ofNullable(goodwillCreditAccountId)
.ifPresent(goodwillCreditAccountId -> map.put("goodwillCreditAccountId", Long.toString(goodwillCreditAccountId)));
Optional.ofNullable(incomeFromGoodwillCreditInterestAccountId).ifPresent(incomeFromGoodwillCreditInterestAccountId -> map
.put("incomeFromGoodwillCreditInterestAccountId", Long.toString(incomeFromGoodwillCreditInterestAccountId)));
Optional.ofNullable(incomeFromGoodwillCreditFeesAccountId).ifPresent(incomeFromGoodwillCreditFeesAccountId -> map
.put("incomeFromGoodwillCreditFeesAccountId", Long.toString(incomeFromGoodwillCreditFeesAccountId)));
Optional.ofNullable(incomeFromGoodwillCreditPenaltyAccountId).ifPresent(incomeFromGoodwillCreditPenaltyAccountId -> map
.put("incomeFromGoodwillCreditPenaltyAccountId", Long.toString(incomeFromGoodwillCreditPenaltyAccountId)));
Optional.ofNullable(incomeFromChargeOffInterestAccountId).ifPresent(incomeFromChargeOffInterestAccountId -> map
.put("incomeFromChargeOffInterestAccountId", Long.toString(incomeFromChargeOffInterestAccountId)));
Optional.ofNullable(incomeFromChargeOffFeesAccountId).ifPresent(incomeFromChargeOffFeesAccountId -> map
.put("incomeFromChargeOffFeesAccountId", Long.toString(incomeFromChargeOffFeesAccountId)));
Optional.ofNullable(chargeOffExpenseAccountId)
.ifPresent(chargeOffExpenseAccountId -> map.put("chargeOffExpenseAccountId", Long.toString(chargeOffExpenseAccountId)));
Optional.ofNullable(chargeOffFraudExpenseAccountId).ifPresent(chargeOffFraudExpenseAccountId -> map
.put("chargeOffFraudExpenseAccountId", Long.toString(chargeOffFraudExpenseAccountId)));
Optional.ofNullable(incomeFromChargeOffPenaltyAccountId).ifPresent(incomeFromChargeOffPenaltyAccountId -> map
.put("incomeFromChargeOffPenaltyAccountId", Long.toString(incomeFromChargeOffPenaltyAccountId)));
return map;
}
}
}