FINERACT-2348: Calculate and store Capitalized income / Buydown fee amortization allocation schedule
This commit is contained in:
committed by
Adam Saghy
parent
9790f59e4a
commit
cd0ecfcda0
+140
@@ -62,6 +62,7 @@ import org.apache.fineract.avro.loan.v1.LoanChargePaidByDataV1;
|
||||
import org.apache.fineract.avro.loan.v1.LoanStatusEnumDataV1;
|
||||
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1;
|
||||
import org.apache.fineract.client.models.AdvancedPaymentData;
|
||||
import org.apache.fineract.client.models.AmortizationMappingData;
|
||||
import org.apache.fineract.client.models.BusinessDateResponse;
|
||||
import org.apache.fineract.client.models.BuyDownFeeAmortizationDetails;
|
||||
import org.apache.fineract.client.models.CapitalizedIncomeDetails;
|
||||
@@ -78,6 +79,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdDisbursementDetails;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargePaidByData;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdLoanTermVariations;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdLoanTransactionEnumData;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdLoanTransactionRelation;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
|
||||
@@ -87,6 +89,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
|
||||
import org.apache.fineract.client.models.IsCatchUpRunningDTO;
|
||||
import org.apache.fineract.client.models.LoanAmortizationAllocationResponse;
|
||||
import org.apache.fineract.client.models.LoanProductChargeData;
|
||||
import org.apache.fineract.client.models.OldestCOBProcessedLoanDTO;
|
||||
import org.apache.fineract.client.models.PaymentAllocationOrder;
|
||||
@@ -4955,6 +4958,41 @@ public class LoanStepDef extends AbstractStepDef {
|
||||
log.debug("Capitalized Income Adjustment created: Transaction ID {}", adjustmentResponse.body().getResourceId());
|
||||
}
|
||||
|
||||
@And("Admin adds capitalized income adjustment of capitalized income transaction made on {string} with {string} payment type to the loan on {string} with {string} EUR transaction amount")
|
||||
public void adminAddsCapitalizedIncomeAdjustmentToTheLoan(final String originalTransactionDate, final String transactionPaymentType,
|
||||
final String transactionDate, final String amount) throws IOException {
|
||||
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
final long loanId = loanResponse.body().getLoanId();
|
||||
|
||||
// Get current business date to ensure we're not creating backdated transactions
|
||||
String currentBusinessDate = businessDateHelper.getBusinessDate();
|
||||
log.debug("Current business date: {}, Transaction date: {}", currentBusinessDate, transactionDate);
|
||||
|
||||
final Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
|
||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||
|
||||
assert loanDetailsResponse.body() != null;
|
||||
final List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
|
||||
assert transactions != null;
|
||||
final GetLoansLoanIdTransactions capitalizedIncomeTransaction = transactions.stream().filter(t -> {
|
||||
assert t.getType() != null;
|
||||
if (!"Capitalized Income".equals(t.getType().getValue())) {
|
||||
return false;
|
||||
}
|
||||
assert t.getDate() != null;
|
||||
return FORMATTER.format(t.getDate()).equals(originalTransactionDate);
|
||||
}).findFirst().orElseThrow(() -> new IllegalStateException("No Capitalized Income transaction found for loan " + loanId));
|
||||
|
||||
final Response<PostLoansLoanIdTransactionsResponse> adjustmentResponse = adjustCapitalizedIncome(transactionPaymentType,
|
||||
transactionDate, amount, capitalizedIncomeTransaction.getId());
|
||||
|
||||
testContext().set(TestContextKey.LOAN_CAPITALIZED_INCOME_ADJUSTMENT_RESPONSE, adjustmentResponse);
|
||||
ErrorHelper.checkSuccessfulApiCall(adjustmentResponse);
|
||||
|
||||
assert adjustmentResponse.body() != null;
|
||||
log.debug("Capitalized Income Adjustment created: Transaction ID {}", adjustmentResponse.body().getResourceId());
|
||||
}
|
||||
|
||||
@Then("Loan's available disbursement amount is {string}")
|
||||
public void verifyAvailableDisbursementAmount(String expectedAmount) throws IOException {
|
||||
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
@@ -5256,6 +5294,34 @@ public class LoanStepDef extends AbstractStepDef {
|
||||
log.debug("BuyDown Fee Adjustment created: Transaction ID {}", adjustmentResponse.body().getResourceId());
|
||||
}
|
||||
|
||||
@And("Admin adds buy down fee adjustment of buy down fee transaction made on {string} with {string} payment type to the loan on {string} with {string} EUR transaction amount")
|
||||
public void adminAddsBuyDownFeesAdjustmentToTheLoan(final String originalTransactionDate, final String transactionPaymentType,
|
||||
final String transactionDate, final String amount) throws IOException {
|
||||
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
final long loanId = loanResponse.body().getLoanId();
|
||||
|
||||
final Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
|
||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||
|
||||
final List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
|
||||
final GetLoansLoanIdTransactions buyDownFeeTransaction = transactions.stream().filter(t -> {
|
||||
assert t.getType() != null;
|
||||
if (!"Buy Down Fee".equals(t.getType().getValue())) {
|
||||
return false;
|
||||
}
|
||||
assert t.getDate() != null;
|
||||
return FORMATTER.format(t.getDate()).equals(originalTransactionDate);
|
||||
}).findFirst().orElseThrow(() -> new IllegalStateException("No Buy Down Fee transaction found for loan " + loanId));
|
||||
|
||||
final Response<PostLoansLoanIdTransactionsResponse> adjustmentResponse = adjustBuyDownFee(transactionPaymentType, transactionDate,
|
||||
amount, buyDownFeeTransaction.getId());
|
||||
|
||||
testContext().set(TestContextKey.LOAN_BUY_DOWN_FEE_ADJUSTMENT_RESPONSE, adjustmentResponse);
|
||||
ErrorHelper.checkSuccessfulApiCall(adjustmentResponse);
|
||||
|
||||
log.debug("BuyDown Fee Adjustment created: Transaction ID {}", adjustmentResponse.body().getResourceId());
|
||||
}
|
||||
|
||||
@And("Buy down fee contains the following data:")
|
||||
public void checkBuyDownFeeData(DataTable table) throws IOException {
|
||||
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
@@ -5665,4 +5731,78 @@ public class LoanStepDef extends AbstractStepDef {
|
||||
|
||||
return response.body().getSubResourceId();
|
||||
}
|
||||
|
||||
@And("Loan Amortization Allocation Mapping for {string} transaction created on {string} contains the following data:")
|
||||
public void checkLoanAmortizationAllocationMapping(final String transactionType, final String transactionDate, DataTable table)
|
||||
throws IOException {
|
||||
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
|
||||
final Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
final long loanId = loanCreateResponse.body().getLoanId();
|
||||
final String resourceId = String.valueOf(loanId);
|
||||
|
||||
final Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
|
||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||
|
||||
final TransactionType transactionType1 = TransactionType.valueOf(transactionType);
|
||||
final String transactionTypeExpected = transactionType1.getValue();
|
||||
|
||||
assert loanDetailsResponse.body() != null;
|
||||
final List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
|
||||
assert transactions != null;
|
||||
final List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream().filter(t -> {
|
||||
assert t.getDate() != null;
|
||||
if (!transactionDate.equals(formatter.format(t.getDate()))) {
|
||||
return false;
|
||||
}
|
||||
assert t.getType() != null;
|
||||
assert t.getType().getCode() != null;
|
||||
return transactionTypeExpected.equals(t.getType().getCode().substring(20));
|
||||
}).toList();
|
||||
|
||||
final Response<LoanAmortizationAllocationResponse> loanAmortizationAllocationResponse = transactionsMatch.getFirst().getType()
|
||||
.getCode().substring(20).equals(GetLoansLoanIdLoanTransactionEnumData.SERIALIZED_NAME_CAPITALIZED_INCOME)
|
||||
? loanCapitalizedIncomeApi.retrieveCapitalizedIncomeAllocationData(loanId, transactionsMatch.getFirst().getId())
|
||||
.execute()
|
||||
: loanBuyDownFeesApi.retrieveBuyDownFeesAllocationData(loanId, transactionsMatch.getFirst().getId()).execute();
|
||||
ErrorHelper.checkSuccessfulApiCall(loanAmortizationAllocationResponse);
|
||||
|
||||
checkLoanAmortizationAllocationMappingData(resourceId, loanAmortizationAllocationResponse.body(), table);
|
||||
}
|
||||
|
||||
private void checkLoanAmortizationAllocationMappingData(final String resourceId,
|
||||
final LoanAmortizationAllocationResponse amortizationAllocationResponse, final DataTable table) {
|
||||
final List<List<String>> data = table.asLists();
|
||||
for (int i = 1; i < data.size(); i++) {
|
||||
final List<String> expectedValues = data.get(i);
|
||||
assert amortizationAllocationResponse.getAmortizationMappings() != null;
|
||||
final boolean found = amortizationAllocationResponse.getAmortizationMappings().stream().anyMatch(t -> {
|
||||
final List<String> actualValues = fetchValuesOfAmortizationAllocationMapping(table.row(0), t);
|
||||
return actualValues.equals(expectedValues);
|
||||
});
|
||||
|
||||
assertThat(found).as(ErrorMessageHelper.wrongValueInLineInDeferredIncomeTab(resourceId, i,
|
||||
amortizationAllocationResponse.getAmortizationMappings().stream()
|
||||
.map(t -> fetchValuesOfAmortizationAllocationMapping(table.row(0), t)).collect(Collectors.toList()),
|
||||
expectedValues)).isTrue();
|
||||
}
|
||||
assertThat(amortizationAllocationResponse.getAmortizationMappings().size())
|
||||
.as(ErrorMessageHelper.nrOfLinesWrongInDeferredIncomeTab(resourceId,
|
||||
amortizationAllocationResponse.getAmortizationMappings().size(), data.size() - 1))
|
||||
.isEqualTo(data.size() - 1);
|
||||
}
|
||||
|
||||
private List<String> fetchValuesOfAmortizationAllocationMapping(final List<String> header, final AmortizationMappingData t) {
|
||||
final List<String> actualValues = new ArrayList<>();
|
||||
for (String headerName : header) {
|
||||
switch (headerName) {
|
||||
case "Date" -> actualValues.add(t.getDate() == null ? null : FORMATTER.format(t.getDate()));
|
||||
case "Type" -> actualValues.add(t.getType() == null ? null : t.getType());
|
||||
case "Amount" ->
|
||||
actualValues.add(t.getAmount() == null ? new Utils.DoubleFormatter(new BigDecimal("0.0").doubleValue()).format()
|
||||
: new Utils.DoubleFormatter(t.getAmount().doubleValue()).format());
|
||||
default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName));
|
||||
}
|
||||
}
|
||||
return actualValues;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3240,3 +3240,317 @@ Feature:Feature: Buy Down Fees
|
||||
And LoanBuyDownFeeTransactionCreatedBusinessEvent is created on "01 January 2024"
|
||||
And Admin adds buy down fee adjustment with "AUTOPAY" payment type to the loan on "01 January 2024" with "25" EUR transaction amount
|
||||
And Loan Transactions tab has a "Buy Down Fee Adjustment" transaction with date "01 January 2024" which has classification code value "buydown_fee_transaction_classification_value"
|
||||
|
||||
@TestRailId:C4009
|
||||
Scenario: Verify Buy Down Fee amortization allocation mappings
|
||||
When Admin sets the business date to "1 January 2024"
|
||||
And Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_PROGRESSIVE_ADVANCED_PAYMENT_ALLOCATION_BUYDOWN_FEES | 01 January 2024 | 250 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "1 January 2024" with "250" amount and expected disbursement date on "1 January 2024"
|
||||
And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||
Then Loan status will be "ACTIVE"
|
||||
And Admin adds buy down fee with "AUTOPAY" payment type to the loan on "1 January 2024" with "50" EUR transaction amount
|
||||
When Admin sets the business date to "2 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
Then Loan Repayment schedule has 3 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 01 February 2024 | | 66.86 | 33.14 | 0.58 | 0.0 | 0.0 | 33.72 | 0.0 | 0.0 | 0.0 | 33.72 |
|
||||
| 2 | 29 | 01 March 2024 | | 33.53 | 33.33 | 0.39 | 0.0 | 0.0 | 33.72 | 0.0 | 0.0 | 0.0 | 33.72 |
|
||||
| 3 | 31 | 01 April 2024 | | 0.0 | 33.53 | 0.2 | 0.0 | 0.0 | 33.73 | 0.0 | 0.0 | 0.0 | 33.73 |
|
||||
And Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 1.17 | 0.0 | 0.0 | 101.17 | 0.0 | 0.0 | 0.0 | 101.17 |
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan Transactions tab has a "BUY_DOWN_FEE" transaction with date "01 January 2024" which has the following Journal entries:
|
||||
| Type | Account code | Account name | Debit | Credit |
|
||||
| EXPENSE | 450280 | Buy Down Expense | 50.0 | |
|
||||
| LIABILITY | 145024 | Deferred Capitalized Income | | 50.0 |
|
||||
And Admin adds buy down fee with "AUTOPAY" payment type to the loan on "2 January 2024" with "100" EUR transaction amount
|
||||
When Admin sets the business date to "3 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
|
||||
@TestRailId:C4019
|
||||
Scenario: Verify Buy Down Fee amortization allocation mappings when buy down fee transaction is reversed
|
||||
When Admin sets the business date to "1 January 2024"
|
||||
And Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_PROGRESSIVE_ADVANCED_PAYMENT_ALLOCATION_BUYDOWN_FEES | 01 January 2024 | 350 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "1 January 2024" with "350" amount and expected disbursement date on "1 January 2024"
|
||||
And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||
Then Loan status will be "ACTIVE"
|
||||
And Admin adds buy down fee with "AUTOPAY" payment type to the loan on "1 January 2024" with "50" EUR transaction amount
|
||||
When Admin sets the business date to "2 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
Then Loan Repayment schedule has 3 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 01 February 2024 | | 66.86 | 33.14 | 0.58 | 0.0 | 0.0 | 33.72 | 0.0 | 0.0 | 0.0 | 33.72 |
|
||||
| 2 | 29 | 01 March 2024 | | 33.53 | 33.33 | 0.39 | 0.0 | 0.0 | 33.72 | 0.0 | 0.0 | 0.0 | 33.72 |
|
||||
| 3 | 31 | 01 April 2024 | | 0.0 | 33.53 | 0.2 | 0.0 | 0.0 | 33.73 | 0.0 | 0.0 | 0.0 | 33.73 |
|
||||
And Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 1.17 | 0.0 | 0.0 | 101.17 | 0.0 | 0.0 | 0.0 | 101.17 |
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan Transactions tab has a "BUY_DOWN_FEE" transaction with date "01 January 2024" which has the following Journal entries:
|
||||
| Type | Account code | Account name | Debit | Credit |
|
||||
| EXPENSE | 450280 | Buy Down Expense | 50.0 | |
|
||||
| LIABILITY | 145024 | Deferred Capitalized Income | | 50.0 |
|
||||
And Admin adds buy down fee with "AUTOPAY" payment type to the loan on "2 January 2024" with "200" EUR transaction amount
|
||||
When Admin sets the business date to "3 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 2.77 | 0.0 | 2.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 2.22 |
|
||||
When Customer undo "1"th "Buy Down Fee" transaction made on "01 January 2024"
|
||||
When Admin sets the business date to "4 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | true | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 2.77 | 0.0 | 2.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Buy Down Fee Amortization | 1.12 | 0.0 | 1.12 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 1.1 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 2.22 |
|
||||
| 03 January 2024 | AM | 2.22 |
|
||||
When Admin sets the business date to "5 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | true | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 2.77 | 0.0 | 2.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Buy Down Fee Amortization | 1.12 | 0.0 | 1.12 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Buy Down Fee Amortization | 2.23 | 0.0 | 2.23 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 1.1 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 2.22 |
|
||||
| 03 January 2024 | AM | 2.22 |
|
||||
| 04 January 2024 | AM | 2.23 |
|
||||
|
||||
@TestRailId:C4022
|
||||
Scenario: Verify Buy Down Fee amortization allocation mappings when buy down fee adjustment occurs
|
||||
When Admin sets the business date to "1 January 2024"
|
||||
And Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_PROGRESSIVE_ADVANCED_PAYMENT_ALLOCATION_BUYDOWN_FEES | 01 January 2024 | 350 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "1 January 2024" with "350" amount and expected disbursement date on "1 January 2024"
|
||||
And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||
Then Loan status will be "ACTIVE"
|
||||
And Admin adds buy down fee with "AUTOPAY" payment type to the loan on "1 January 2024" with "50" EUR transaction amount
|
||||
When Admin sets the business date to "2 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
Then Loan Repayment schedule has 3 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 01 February 2024 | | 66.86 | 33.14 | 0.58 | 0.0 | 0.0 | 33.72 | 0.0 | 0.0 | 0.0 | 33.72 |
|
||||
| 2 | 29 | 01 March 2024 | | 33.53 | 33.33 | 0.39 | 0.0 | 0.0 | 33.72 | 0.0 | 0.0 | 0.0 | 33.72 |
|
||||
| 3 | 31 | 01 April 2024 | | 0.0 | 33.53 | 0.2 | 0.0 | 0.0 | 33.73 | 0.0 | 0.0 | 0.0 | 33.73 |
|
||||
And Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 1.17 | 0.0 | 0.0 | 101.17 | 0.0 | 0.0 | 0.0 | 101.17 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan Transactions tab has a "BUY_DOWN_FEE" transaction with date "01 January 2024" which has the following Journal entries:
|
||||
| Type | Account code | Account name | Debit | Credit |
|
||||
| EXPENSE | 450280 | Buy Down Expense | 50.0 | |
|
||||
| LIABILITY | 145024 | Deferred Capitalized Income | | 50.0 |
|
||||
And Admin adds buy down fee with "AUTOPAY" payment type to the loan on "2 January 2024" with "100" EUR transaction amount
|
||||
When Admin sets the business date to "3 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
When Admin adds buy down fee adjustment with "AUTOPAY" payment type to the loan on "02 January 2024" with "40" EUR transaction amount
|
||||
When Admin sets the business date to "4 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Adjustment | 40.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Buy Down Fee Amortization | 0.77 | 0.0 | 0.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 0.34 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
| 03 January 2024 | AM | 1.11 |
|
||||
When Admin sets the business date to "5 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Adjustment | 40.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Buy Down Fee Amortization | 0.77 | 0.0 | 0.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Buy Down Fee Amortization | 1.21 | 0.0 | 1.21 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 0.34 |
|
||||
| 04 January 2024 | AM | 0.1 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
| 03 January 2024 | AM | 1.11 |
|
||||
| 04 January 2024 | AM | 1.11 |
|
||||
And Admin adds buy down fee adjustment of buy down fee transaction made on "02 January 2024" with "AUTOPAY" payment type to the loan on "04 January 2024" with "60" EUR transaction amount
|
||||
When Admin sets the business date to "6 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Adjustment | 40.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Buy Down Fee Amortization | 0.77 | 0.0 | 0.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Buy Down Fee Amortization | 1.21 | 0.0 | 1.21 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Buy Down Fee Adjustment | 60.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 05 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 05 January 2024 | Buy Down Fee Amortization Adjustment | 0.14 | 0.0 | 0.14 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 0.34 |
|
||||
| 04 January 2024 | AM | 0.1 |
|
||||
| 05 January 2024 | AM | 0.11 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
| 03 January 2024 | AM | 1.11 |
|
||||
| 04 January 2024 | AM | 1.11 |
|
||||
| 05 January 2024 | AM_ADJ | 0.25 |
|
||||
When Admin adds buy down fee adjustment with "AUTOPAY" payment type to the loan on "05 January 2024" with "10" EUR transaction amount
|
||||
When Admin sets the business date to "7 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee | 50.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 01 January 2024 | Buy Down Fee Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Buy Down Fee Adjustment | 40.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Buy Down Fee Amortization | 0.77 | 0.0 | 0.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Buy Down Fee Amortization | 1.21 | 0.0 | 1.21 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Buy Down Fee Adjustment | 60.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 05 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 05 January 2024 | Buy Down Fee Amortization Adjustment | 0.14 | 0.0 | 0.14 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 05 January 2024 | Buy Down Fee Adjustment | 10.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 06 January 2024 | Accrual | 0.01 | 0.0 | 0.01 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 06 January 2024 | Buy Down Fee Amortization Adjustment | 0.54 | 0.0 | 0.54 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 0.34 |
|
||||
| 04 January 2024 | AM | 0.1 |
|
||||
| 05 January 2024 | AM | 0.11 |
|
||||
| 06 January 2024 | AM_ADJ | 0.97 |
|
||||
And Loan Amortization Allocation Mapping for "BUY_DOWN_FEE" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
| 03 January 2024 | AM | 1.11 |
|
||||
| 04 January 2024 | AM | 1.11 |
|
||||
| 05 January 2024 | AM_ADJ | 0.25 |
|
||||
| 06 January 2024 | AM | 0.43 |
|
||||
@@ -7566,3 +7566,320 @@ Feature: Capitalized Income
|
||||
| 100.0 | 0.0 | 100.0 | 0.0 | 0.0 |
|
||||
And Admin adds capitalized income adjustment with "AUTOPAY" payment type to the loan on "02 January 2024" with "50" EUR transaction amount
|
||||
And Loan Transactions tab has a "Capitalized Income Adjustment" transaction with date "02 January 2024" which has classification code value "capitalized_income_transaction_classification_value"
|
||||
|
||||
@TestRailId:C4008
|
||||
Scenario: Verify Capitalized income amortization allocation mappings
|
||||
When Admin sets the business date to "1 January 2024"
|
||||
And Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CAPITALIZED_INCOME_ADJ_CUSTOM_ALLOC | 01 January 2024 | 250 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "1 January 2024" with "250" amount and expected disbursement date on "1 January 2024"
|
||||
And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||
Then Loan status will be "ACTIVE"
|
||||
And Admin adds capitalized income with "AUTOPAY" payment type to the loan on "1 January 2024" with "50" EUR transaction amount
|
||||
When Admin sets the business date to "2 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
Then Loan Repayment schedule has 3 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| | | 01 January 2024 | | 50.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 01 February 2024 | | 100.29 | 49.71 | 0.87 | 0.0 | 0.0 | 50.58 | 0.0 | 0.0 | 0.0 | 50.58 |
|
||||
| 2 | 29 | 01 March 2024 | | 50.3 | 49.99 | 0.59 | 0.0 | 0.0 | 50.58 | 0.0 | 0.0 | 0.0 | 50.58 |
|
||||
| 3 | 31 | 01 April 2024 | | 0.0 | 50.3 | 0.29 | 0.0 | 0.0 | 50.59 | 0.0 | 0.0 | 0.0 | 50.59 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 150.0 | 1.75 | 0.0 | 0.0 | 151.75 | 0.0 | 0.0 | 0.0 | 151.75 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME" transaction with date "01 January 2024" which has the following Journal entries:
|
||||
| Type | Account code | Account name | Debit | Credit |
|
||||
| ASSET | 112601 | Loans Receivable | 50.0 | |
|
||||
| LIABILITY | 145024 | Deferred Capitalized Income | | 50.0 |
|
||||
And Admin adds capitalized income with "AUTOPAY" payment type to the loan on "2 January 2024" with "100" EUR transaction amount
|
||||
When Admin sets the business date to "3 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
|
||||
@TestRailId:C4020
|
||||
Scenario: Verify Capitalized income amortization allocation mappings when capitalized income transaction is reversed
|
||||
When Admin sets the business date to "1 January 2024"
|
||||
And Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CAPITALIZED_INCOME_ADJ_CUSTOM_ALLOC | 01 January 2024 | 350 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "1 January 2024" with "350" amount and expected disbursement date on "1 January 2024"
|
||||
And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||
Then Loan status will be "ACTIVE"
|
||||
And Admin adds capitalized income with "AUTOPAY" payment type to the loan on "1 January 2024" with "50" EUR transaction amount
|
||||
When Admin sets the business date to "2 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
Then Loan Repayment schedule has 3 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| | | 01 January 2024 | | 50.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 01 February 2024 | | 100.29 | 49.71 | 0.87 | 0.0 | 0.0 | 50.58 | 0.0 | 0.0 | 0.0 | 50.58 |
|
||||
| 2 | 29 | 01 March 2024 | | 50.3 | 49.99 | 0.59 | 0.0 | 0.0 | 50.58 | 0.0 | 0.0 | 0.0 | 50.58 |
|
||||
| 3 | 31 | 01 April 2024 | | 0.0 | 50.3 | 0.29 | 0.0 | 0.0 | 50.59 | 0.0 | 0.0 | 0.0 | 50.59 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 150.0 | 1.75 | 0.0 | 0.0 | 151.75 | 0.0 | 0.0 | 0.0 | 151.75 |
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME" transaction with date "01 January 2024" which has the following Journal entries:
|
||||
| Type | Account code | Account name | Debit | Credit |
|
||||
| ASSET | 112601 | Loans Receivable | 50.0 | |
|
||||
| LIABILITY | 145024 | Deferred Capitalized Income | | 50.0 |
|
||||
And Admin adds capitalized income with "AUTOPAY" payment type to the loan on "2 January 2024" with "200" EUR transaction amount
|
||||
When Admin sets the business date to "3 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 200.0 | 200.0 | 0.0 | 0.0 | 0.0 | 350.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 2.77 | 0.0 | 2.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 2.22 |
|
||||
When Customer undo "1"th "Capitalized Income" transaction made on "01 January 2024"
|
||||
When Admin sets the business date to "4 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | true | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 200.0 | 200.0 | 0.0 | 0.0 | 0.0 | 300.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 2.77 | 0.0 | 2.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.05 | 0.0 | 0.05 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Capitalized Income Amortization | 1.12 | 0.0 | 1.12 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 1.1 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 2.22 |
|
||||
| 03 January 2024 | AM | 2.22 |
|
||||
When Admin sets the business date to "5 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | true | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 200.0 | 200.0 | 0.0 | 0.0 | 0.0 | 300.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 2.77 | 0.0 | 2.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.05 | 0.0 | 0.05 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Capitalized Income Amortization | 1.12 | 0.0 | 1.12 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Accrual | 0.05 | 0.0 | 0.05 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Capitalized Income Amortization | 2.23 | 0.0 | 2.23 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 1.1 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 2.22 |
|
||||
| 03 January 2024 | AM | 2.22 |
|
||||
| 04 January 2024 | AM | 2.23 |
|
||||
|
||||
@TestRailId:C4021
|
||||
Scenario: Verify Capitalized income amortization allocation mappings when capitalized income adjustment occurs
|
||||
When Admin sets the business date to "1 January 2024"
|
||||
And Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CAPITALIZED_INCOME_ADJ_CUSTOM_ALLOC | 01 January 2024 | 250 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "1 January 2024" with "250" amount and expected disbursement date on "1 January 2024"
|
||||
And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||
Then Loan status will be "ACTIVE"
|
||||
And Admin adds capitalized income with "AUTOPAY" payment type to the loan on "1 January 2024" with "50" EUR transaction amount
|
||||
When Admin sets the business date to "2 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
Then Loan Repayment schedule has 3 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| | | 01 January 2024 | | 50.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 01 February 2024 | | 100.29 | 49.71 | 0.87 | 0.0 | 0.0 | 50.58 | 0.0 | 0.0 | 0.0 | 50.58 |
|
||||
| 2 | 29 | 01 March 2024 | | 50.3 | 49.99 | 0.59 | 0.0 | 0.0 | 50.58 | 0.0 | 0.0 | 0.0 | 50.58 |
|
||||
| 3 | 31 | 01 April 2024 | | 0.0 | 50.3 | 0.29 | 0.0 | 0.0 | 50.59 | 0.0 | 0.0 | 0.0 | 50.59 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 150.0 | 1.75 | 0.0 | 0.0 | 151.75 | 0.0 | 0.0 | 0.0 | 151.75 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME" transaction with date "01 January 2024" which has the following Journal entries:
|
||||
| Type | Account code | Account name | Debit | Credit |
|
||||
| ASSET | 112601 | Loans Receivable | 50.0 | |
|
||||
| LIABILITY | 145024 | Deferred Capitalized Income | | 50.0 |
|
||||
And Admin adds capitalized income with "AUTOPAY" payment type to the loan on "2 January 2024" with "100" EUR transaction amount
|
||||
When Admin sets the business date to "3 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
When Admin adds capitalized income adjustment with "AUTOPAY" payment type to the loan on "02 January 2024" with "40" EUR transaction amount
|
||||
When Admin sets the business date to "4 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Adjustment | 40.0 | 40.0 | 0.0 | 0.0 | 0.0 | 210.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.04 | 0.0 | 0.04 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Capitalized Income Amortization | 0.77 | 0.0 | 0.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 0.34 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
| 03 January 2024 | AM | 1.11 |
|
||||
When Admin sets the business date to "5 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Adjustment | 40.0 | 40.0 | 0.0 | 0.0 | 0.0 | 210.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.04 | 0.0 | 0.04 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Capitalized Income Amortization | 0.77 | 0.0 | 0.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Accrual | 0.04 | 0.0 | 0.04 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Capitalized Income Amortization | 1.21 | 0.0 | 1.21 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 0.34 |
|
||||
| 04 January 2024 | AM | 0.1 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
| 03 January 2024 | AM | 1.11 |
|
||||
| 04 January 2024 | AM | 1.11 |
|
||||
And Admin adds capitalized income adjustment of capitalized income transaction made on "02 January 2024" with "AUTOPAY" payment type to the loan on "04 January 2024" with "60" EUR transaction amount
|
||||
When Admin sets the business date to "6 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Adjustment | 40.0 | 40.0 | 0.0 | 0.0 | 0.0 | 210.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.04 | 0.0 | 0.04 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Capitalized Income Amortization | 0.77 | 0.0 | 0.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Accrual | 0.04 | 0.0 | 0.04 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Capitalized Income Amortization | 1.21 | 0.0 | 1.21 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Capitalized Income Adjustment | 60.0 | 59.89 | 0.11 | 0.0 | 0.0 | 150.11 | false | false |
|
||||
| 05 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 05 January 2024 | Capitalized Income Amortization Adjustment | 0.14 | 0.0 | 0.14 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 0.34 |
|
||||
| 04 January 2024 | AM | 0.1 |
|
||||
| 05 January 2024 | AM | 0.11 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
| 03 January 2024 | AM | 1.11 |
|
||||
| 04 January 2024 | AM | 1.11 |
|
||||
| 05 January 2024 | AM_ADJ | 0.25 |
|
||||
When Admin adds capitalized income adjustment with "AUTOPAY" payment type to the loan on "05 January 2024" with "10" EUR transaction amount
|
||||
When Admin sets the business date to "7 January 2024"
|
||||
And Admin runs inline COB job for Loan
|
||||
And Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 150.0 | false | false |
|
||||
| 01 January 2024 | Capitalized Income Amortization | 0.55 | 0.0 | 0.55 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
|
||||
| 02 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Amortization | 1.66 | 0.0 | 1.66 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 02 January 2024 | Capitalized Income Adjustment | 40.0 | 40.0 | 0.0 | 0.0 | 0.0 | 210.0 | false | false |
|
||||
| 03 January 2024 | Accrual | 0.04 | 0.0 | 0.04 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 03 January 2024 | Capitalized Income Amortization | 0.77 | 0.0 | 0.77 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Accrual | 0.04 | 0.0 | 0.04 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Capitalized Income Amortization | 1.21 | 0.0 | 1.21 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 04 January 2024 | Capitalized Income Adjustment | 60.0 | 59.89 | 0.11 | 0.0 | 0.0 | 150.11 | false | false |
|
||||
| 05 January 2024 | Accrual | 0.03 | 0.0 | 0.03 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 05 January 2024 | Capitalized Income Amortization Adjustment | 0.14 | 0.0 | 0.14 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 05 January 2024 | Capitalized Income Adjustment | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 140.11 | false | false |
|
||||
| 06 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 06 January 2024 | Capitalized Income Amortization Adjustment | 0.54 | 0.0 | 0.54 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "01 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 01 January 2024 | AM | 0.55 |
|
||||
| 02 January 2024 | AM | 0.55 |
|
||||
| 03 January 2024 | AM_ADJ | 0.34 |
|
||||
| 04 January 2024 | AM | 0.1 |
|
||||
| 05 January 2024 | AM | 0.11 |
|
||||
| 06 January 2024 | AM_ADJ | 0.97 |
|
||||
And Loan Amortization Allocation Mapping for "CAPITALIZED_INCOME" transaction created on "02 January 2024" contains the following data:
|
||||
| Date | Type | Amount |
|
||||
| 02 January 2024 | AM | 1.11 |
|
||||
| 03 January 2024 | AM | 1.11 |
|
||||
| 04 January 2024 | AM | 1.11 |
|
||||
| 05 January 2024 | AM_ADJ | 0.25 |
|
||||
| 06 January 2024 | AM | 0.43 |
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Swagger documentation for Loan Amortization Allocation API
|
||||
*/
|
||||
final class LoanAmortizationAllocationApiResourceSwagger {
|
||||
|
||||
private LoanAmortizationAllocationApiResourceSwagger() {}
|
||||
|
||||
/**
|
||||
* Common response class for all loan amortization allocation APIs Used for both Capitalized Income and Buydown Fee
|
||||
* endpoints
|
||||
*/
|
||||
@Schema(description = "LoanAmortizationAllocationResponse")
|
||||
public static final class LoanAmortizationAllocationResponse {
|
||||
|
||||
private LoanAmortizationAllocationResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Long loanId;
|
||||
@Schema(example = "loan-ext-123")
|
||||
public String loanExternalId;
|
||||
@Schema(example = "123")
|
||||
public Long baseLoanTransactionId;
|
||||
@Schema(example = "2024-01-15")
|
||||
public LocalDate baseLoanTransactionDate;
|
||||
@Schema(example = "1000.00")
|
||||
public BigDecimal baseLoanTransactionAmount;
|
||||
@Schema(example = "50.00")
|
||||
public BigDecimal unrecognizedAmount;
|
||||
@Schema(example = "0.00")
|
||||
public BigDecimal chargedOffAmount;
|
||||
@Schema(example = "25.00")
|
||||
public BigDecimal adjustmentAmount;
|
||||
public List<AmortizationMappingData> amortizationMappings;
|
||||
|
||||
/**
|
||||
* Data transfer object for amortization mapping details
|
||||
*/
|
||||
static final class AmortizationMappingData {
|
||||
|
||||
private AmortizationMappingData() {}
|
||||
|
||||
@Schema(example = "789")
|
||||
public Long amortizationLoanTransactionId;
|
||||
@Schema(example = "amort-txn-ext-789")
|
||||
public String amortizationLoanTransactionExternalId;
|
||||
@Schema(example = "2024-01-15")
|
||||
public LocalDate date;
|
||||
@Schema(example = "AM", description = "AM for amortization, AM_ADJ for amortization adjustment")
|
||||
public String type;
|
||||
@Schema(example = "10.00")
|
||||
public BigDecimal amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
|
||||
/**
|
||||
* DTO for base transaction information for AmortizationAllocationMapping
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class AmortizationAllocationBaseTransactionDTO {
|
||||
|
||||
private Long loanId;
|
||||
private ExternalId loanExternalId;
|
||||
private Long baseLoanTransactionId;
|
||||
private LocalDate baseLoanTransactionDate;
|
||||
private BigDecimal baseLoanTransactionAmount;
|
||||
private BigDecimal unrecognizedAmount;
|
||||
private BigDecimal chargedOffAmount;
|
||||
private BigDecimal adjustmentAmount;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
|
||||
/**
|
||||
* DTO for amortization allocation mapping data
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class AmortizationAllocationMappingDTO {
|
||||
|
||||
private Long amortizationLoanTransactionId;
|
||||
private ExternalId amortizationLoanTransactionExternalId;
|
||||
private LocalDate amortizationDate;
|
||||
private AmortizationType amortizationType;
|
||||
private BigDecimal amount;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
|
||||
/**
|
||||
* Data transfer object for loan amortization allocation information
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class LoanAmortizationAllocationData {
|
||||
|
||||
private Long loanId;
|
||||
private ExternalId loanExternalId;
|
||||
private Long baseLoanTransactionId;
|
||||
private LocalDate baseLoanTransactionDate;
|
||||
private BigDecimal baseLoanTransactionAmount;
|
||||
private BigDecimal unrecognizedAmount;
|
||||
private BigDecimal chargedOffAmount;
|
||||
private BigDecimal adjustmentAmount;
|
||||
private List<AmortizationMappingData> amortizationMappings;
|
||||
|
||||
/**
|
||||
* Data transfer object for amortization mapping details
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public static class AmortizationMappingData {
|
||||
|
||||
private Long amortizationLoanTransactionId;
|
||||
private ExternalId amortizationLoanTransactionExternalId;
|
||||
private LocalDate date;
|
||||
private AmortizationType type; // AM or AM_ADJ
|
||||
private BigDecimal amount;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiFacingEnum;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum AmortizationType implements ApiFacingEnum<AmortizationType> {
|
||||
|
||||
AM("amortizationTransaction.type.amortization", "Amortization"), //
|
||||
AM_ADJ("amortizationTransaction.type.amortizationAdjustment", "Amortization Adjustment"); //
|
||||
|
||||
private final String code;
|
||||
private final String humanReadableName;
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Entity to store the mapping between base loan transactions (Capitalized Income or Buy Down Fee) and their
|
||||
* amortization allocation schedule. This mapping table stores which base loan transaction amortization occurs, on which
|
||||
* date and what was the calculated amount.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_loan_amortization_allocation_mapping")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class LoanAmortizationAllocationMapping extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@Column(name = "loan_id", nullable = false)
|
||||
private Long loanId;
|
||||
|
||||
@Column(name = "base_loan_transaction_id", nullable = false)
|
||||
private Long baseLoanTransactionId;
|
||||
|
||||
@Column(name = "date", nullable = false)
|
||||
private LocalDate date;
|
||||
|
||||
@Column(name = "amortization_loan_transaction_id", nullable = false)
|
||||
private Long amortizationLoanTransactionId;
|
||||
|
||||
/**
|
||||
* Type of the amortization transaction (AM - amortization, AM_ADJ - amortization adjustment)
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "amortization_type", nullable = false)
|
||||
private AmortizationType amortizationType;
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationMappingDTO;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* Repository for LoanAmortizationAllocationMapping entity
|
||||
*/
|
||||
@Repository
|
||||
public interface LoanAmortizationAllocationMappingRepository
|
||||
extends JpaRepository<LoanAmortizationAllocationMapping, Long>, JpaSpecificationExecutor<LoanAmortizationAllocationMapping> {
|
||||
|
||||
@Query(value = "SELECT new org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationMappingDTO("
|
||||
+ "laam.amortizationLoanTransactionId, at.externalId, laam.date, laam.amortizationType, laam.amount) "
|
||||
+ "FROM LoanAmortizationAllocationMapping laam " + "JOIN LoanTransaction at ON laam.amortizationLoanTransactionId = at.id "
|
||||
+ "WHERE laam.baseLoanTransactionId = :baseLoanTransactionId AND laam.loanId = :loanId "
|
||||
+ "ORDER BY laam.date, laam.amortizationLoanTransactionId")
|
||||
List<AmortizationAllocationMappingDTO> findAmortizationMappingsByBaseTransactionAndLoan(
|
||||
@Param("baseLoanTransactionId") Long baseLoanTransactionId, @Param("loanId") Long loanId);
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
|
||||
public interface LoanAmortizationAllocationService {
|
||||
|
||||
/**
|
||||
* Retrieve amortization allocation data for a specific buy down fee loan transaction
|
||||
*/
|
||||
LoanAmortizationAllocationData retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(Long loanTransactionId, Long loanId);
|
||||
|
||||
/**
|
||||
* Retrieve amortization allocation data for a specific capitalized income loan transaction
|
||||
*/
|
||||
LoanAmortizationAllocationData retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(Long loanTransactionId, Long loanId);
|
||||
|
||||
LoanAmortizationAllocationMapping createAmortizationAllocationMappingWithBaseLoanTransaction(LoanTransaction loanTransaction,
|
||||
BigDecimal amount, AmortizationType amortizationType);
|
||||
|
||||
void setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(LoanAmortizationAllocationMapping amortizationAllocationMapping,
|
||||
LoanTransaction amortizationTransaction);
|
||||
}
|
||||
+2
@@ -163,4 +163,6 @@ public interface LoanReadPlatformService {
|
||||
|
||||
Long getResolvedLoanId(ExternalId loanExternalId);
|
||||
|
||||
Long getResolvedLoanTransactionId(Long transactionId, ExternalId externalTransactionId);
|
||||
|
||||
}
|
||||
|
||||
+108
@@ -31,14 +31,24 @@ import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.BuyDownFeeAmortizationDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.BuyDownFeeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAmortizationAllocationService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -48,9 +58,15 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class LoanBuyDownFeeApiResource {
|
||||
|
||||
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList("loanId", "loanExternalId", "baseLoanTransactionId", "baseLoanTransactionDate", "baseLoanTransactionAmount",
|
||||
"unrecognizedAmount", "chargedOffAmount", "adjustmentAmount", "amortizationMappings"));
|
||||
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
|
||||
private final PlatformSecurityContext context;
|
||||
private final BuyDownFeeReadPlatformService buyDownFeeReadPlatformService;
|
||||
private final LoanAmortizationAllocationService loanAmortizationAllocationService;
|
||||
private final DefaultToApiJsonSerializer<LoanAmortizationAllocationData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
|
||||
@Path("/{loanId}/buydown-fees")
|
||||
@@ -84,4 +100,96 @@ public class LoanBuyDownFeeApiResource {
|
||||
return this.buyDownFeeReadPlatformService.retrieveLoanBuyDownFeeAmortizationDetails(resolvedLoanId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get BuyDown Fees allocation data by loan ID and transaction ID
|
||||
*/
|
||||
@GET
|
||||
@Path("{loanId}/buydown-fees/{loanTransactionId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a BuyDown Fees allocation data", description = "Retrieves BuyDown Fees allocation data according to the Loan ID and Loan Transaction ID"
|
||||
+ "Example Requests:\n" + "\n" + "/loans/1/buydown-fees/1\n" + "\n" + "\n"
|
||||
+ "/loans/1/buydown-fees/1?fields=baseLoanTransaction,unrecognizedAmount")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAmortizationAllocationApiResourceSwagger.LoanAmortizationAllocationResponse.class))) })
|
||||
public String retrieveBuyDownFeesAllocationData(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
|
||||
@PathParam("loanTransactionId") @Parameter(description = "loanTransactionId") final Long loanTransactionId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return retrieveBuyDownFeesAllocationData(loanId, null, loanTransactionId, null, uriInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get BuyDown Fees allocation data by loan external ID and transaction ID
|
||||
*/
|
||||
@GET
|
||||
@Path("external-id/{loanExternalId}/buydown-fees/{loanTransactionId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a BuyDown Fees allocation data", description = "Retrieves BuyDown Fees allocation data according to the Loan external ID and Loan Transaction ID"
|
||||
+ "Example Requests:\n" + "\n" + "/loans/external-id/1/buydown-fees/1\n" + "\n" + "\n"
|
||||
+ "/loans/external-id/1/buydown-fees/1?fields=baseLoanTransaction,unrecognizedAmount")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAmortizationAllocationApiResourceSwagger.LoanAmortizationAllocationResponse.class))) })
|
||||
public String getBuyDownFeesAllocationDataByLoanExternalId(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId") final String loanExternalId,
|
||||
@PathParam("loanTransactionId") @Parameter(description = "loanTransactionId") final Long loanTransactionId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return retrieveBuyDownFeesAllocationData(null, loanExternalId, loanTransactionId, null, uriInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get BuyDown Fees allocation data by loan ID and transaction external ID
|
||||
*/
|
||||
@GET
|
||||
@Path("{loanId}/buydown-fees/external-id/{loanTransactionExternalId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a BuyDown Fees allocation data", description = "Retrieves BuyDown Fees allocation data according to the Loan ID and Loan Transaction external ID"
|
||||
+ "Example Requests:\n" + "\n" + "/loans/1/buydown-fees/external-id/1\n" + "\n" + "\n"
|
||||
+ "/loans/1/buydown-fees/external-id/1?fields=baseLoanTransaction,unrecognizedAmount")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAmortizationAllocationApiResourceSwagger.LoanAmortizationAllocationResponse.class))) })
|
||||
public String getBuyDownFeesAllocationDataByTransactionExternalId(
|
||||
@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
|
||||
@PathParam("loanTransactionExternalId") @Parameter(description = "loanTransactionExternalId") final String loanTransactionExternalId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return retrieveBuyDownFeesAllocationData(loanId, null, null, loanTransactionExternalId, uriInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get BuyDown Fees allocation data by loan external ID and transaction external ID
|
||||
*/
|
||||
@GET
|
||||
@Path("external-id/{loanExternalId}/buydown-fees/external-id/{loanTransactionExternalId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a BuyDown Fees allocation data", description = "Retrieves BuyDown Fees allocation data according to the Loan external ID and Loan Transaction external ID"
|
||||
+ "Example Requests:\n" + "\n" + "/loans/external-id/1/buydown-fees/1\n" + "\n" + "\n"
|
||||
+ "/loans/external-id/1/buydown-fees/1?fields=baseLoanTransaction,unrecognizedAmount")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAmortizationAllocationApiResourceSwagger.LoanAmortizationAllocationResponse.class))) })
|
||||
public String getBuyDownFeesAllocationDataByExternalIds(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId") final String loanExternalId,
|
||||
@PathParam("loanTransactionExternalId") @Parameter(description = "loanTransactionExternalId") final String loanTransactionExternalId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return retrieveBuyDownFeesAllocationData(null, loanExternalId, null, loanTransactionExternalId, uriInfo);
|
||||
}
|
||||
|
||||
private String retrieveBuyDownFeesAllocationData(final Long loanId, final String loanExternalIdStr, final Long loanTransactionId,
|
||||
final String loanTransactionExternalIdStr, final UriInfo uriInfo) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr);
|
||||
final ExternalId loanTransactionExternalId = ExternalIdFactory.produce(loanTransactionExternalIdStr);
|
||||
|
||||
final Long resolvedLoanId = loanId == null ? loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
|
||||
final Long resolvedLoanTransactionId = loanReadPlatformService.getResolvedLoanTransactionId(loanTransactionId,
|
||||
loanTransactionExternalId);
|
||||
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(resolvedLoanTransactionId, resolvedLoanId);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, loanAmortizationAllocationData, RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
}
|
||||
|
||||
+108
@@ -30,15 +30,25 @@ import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.CapitalizedIncomeDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanCapitalizedIncomeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.CapitalizedIncomeBalanceReadService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAmortizationAllocationService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -48,9 +58,15 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class LoanCapitalizedIncomeApiResource {
|
||||
|
||||
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList("loanId", "loanExternalId", "baseLoanTransactionId", "baseLoanTransactionDate", "baseLoanTransactionAmount",
|
||||
"unrecognizedAmount", "chargedOffAmount", "adjustmentAmount", "amortizationMappings"));
|
||||
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
|
||||
private final PlatformSecurityContext context;
|
||||
private final CapitalizedIncomeBalanceReadService capitalizedIncomeBalanceReadService;
|
||||
private final LoanAmortizationAllocationService loanAmortizationAllocationService;
|
||||
private final DefaultToApiJsonSerializer<LoanAmortizationAllocationData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
|
||||
@Path("/{loanId}/deferredincome")
|
||||
@@ -109,4 +125,96 @@ public class LoanCapitalizedIncomeApiResource {
|
||||
return this.capitalizedIncomeBalanceReadService.fetchLoanCapitalizedIncomeDetails(resolvedLoanId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capitalized income allocation data by loan ID and transaction ID
|
||||
*/
|
||||
@GET
|
||||
@Path("{loanId}/capitalized-incomes/{loanTransactionId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a capitalized income allocation data", description = "Retrieves capitalized income allocation data according to the Loan ID and Loan Transaction ID"
|
||||
+ "Example Requests:\n" + "\n" + "/loans/1/capitalized-incomes/1\n" + "\n" + "\n"
|
||||
+ "/loans/1/capitalized-incomes/1?fields=baseLoanTransaction,unrecognizedAmount")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAmortizationAllocationApiResourceSwagger.LoanAmortizationAllocationResponse.class))) })
|
||||
public String retrieveCapitalizedIncomeAllocationData(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
|
||||
@PathParam("loanTransactionId") @Parameter(description = "loanTransactionId") final Long loanTransactionId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return retrieveCapitalizedIncomeAllocationData(loanId, null, loanTransactionId, null, uriInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capitalized income allocation data by loan external ID and transaction ID
|
||||
*/
|
||||
@GET
|
||||
@Path("external-id/{loanExternalId}/capitalized-incomes/{loanTransactionId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a capitalized income allocation data", description = "Retrieves capitalized income allocation data according to the Loan external ID and Loan Transaction ID"
|
||||
+ "Example Requests:\n" + "\n" + "/loans/external-id/1/capitalized-incomes/1\n" + "\n" + "\n"
|
||||
+ "/loans/external-id/1/capitalized-incomes/1?fields=baseLoanTransaction,unrecognizedAmount")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAmortizationAllocationApiResourceSwagger.LoanAmortizationAllocationResponse.class))) })
|
||||
public String getCapitalizedIncomeAllocationDataByLoanExternalId(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId") final String loanExternalId,
|
||||
@PathParam("loanTransactionId") @Parameter(description = "loanTransactionId") final Long loanTransactionId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return retrieveCapitalizedIncomeAllocationData(null, loanExternalId, loanTransactionId, null, uriInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capitalized income allocation data by loan ID and transaction external ID
|
||||
*/
|
||||
@GET
|
||||
@Path("{loanId}/capitalized-incomes/external-id/{loanTransactionExternalId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a capitalized income allocation data", description = "Retrieves capitalized income allocation data according to the Loan ID and Loan Transaction external ID"
|
||||
+ "Example Requests:\n" + "\n" + "/loans/1/capitalized-incomes/external-id/1\n" + "\n" + "\n"
|
||||
+ "/loans/1/capitalized-incomes/external-id/1?fields=baseLoanTransaction,unrecognizedAmount")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAmortizationAllocationApiResourceSwagger.LoanAmortizationAllocationResponse.class))) })
|
||||
public String getCapitalizedIncomeAllocationDataByTransactionExternalId(
|
||||
@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
|
||||
@PathParam("loanTransactionExternalId") @Parameter(description = "loanTransactionExternalId") final String loanTransactionExternalId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return retrieveCapitalizedIncomeAllocationData(loanId, null, null, loanTransactionExternalId, uriInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capitalized income allocation data by loan external ID and transaction external ID
|
||||
*/
|
||||
@GET
|
||||
@Path("external-id/{loanExternalId}/capitalized-incomes/external-id/{loanTransactionExternalId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a capitalized income allocation data", description = "Retrieves capitalized income allocation data according to the Loan external ID and Loan Transaction external ID"
|
||||
+ "Example Requests:\n" + "\n" + "/loans/external-id/1/capitalized-incomes/1\n" + "\n" + "\n"
|
||||
+ "/loans/external-id/1/capitalized-incomes/1?fields=baseLoanTransaction,unrecognizedAmount")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanAmortizationAllocationApiResourceSwagger.LoanAmortizationAllocationResponse.class))) })
|
||||
public String getCapitalizedIncomeAllocationDataByExternalIds(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId") final String loanExternalId,
|
||||
@PathParam("loanTransactionExternalId") @Parameter(description = "loanTransactionExternalId") final String loanTransactionExternalId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return retrieveCapitalizedIncomeAllocationData(null, loanExternalId, null, loanTransactionExternalId, uriInfo);
|
||||
}
|
||||
|
||||
private String retrieveCapitalizedIncomeAllocationData(final Long loanId, final String loanExternalIdStr, final Long loanTransactionId,
|
||||
final String loanTransactionExternalIdStr, final UriInfo uriInfo) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr);
|
||||
final ExternalId loanTransactionExternalId = ExternalIdFactory.produce(loanTransactionExternalIdStr);
|
||||
|
||||
final Long resolvedLoanId = loanId == null ? loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
|
||||
final Long resolvedLoanTransactionId = loanReadPlatformService.getResolvedLoanTransactionId(loanTransactionId,
|
||||
loanTransactionExternalId);
|
||||
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(resolvedLoanTransactionId, resolvedLoanId);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, loanAmortizationAllocationData, RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -62,4 +62,9 @@ public class LoanBuyDownFeeBalance extends AbstractAuditableWithUTCDateTimeCusto
|
||||
@Column(name = "amount_adjustment", scale = 6, precision = 19)
|
||||
private BigDecimal amountAdjustment;
|
||||
|
||||
@Column(name = "is_deleted", nullable = false)
|
||||
private boolean deleted = false;
|
||||
|
||||
@Column(name = "is_closed", nullable = false)
|
||||
private boolean closed = false;
|
||||
}
|
||||
|
||||
+6
@@ -61,4 +61,10 @@ public class LoanCapitalizedIncomeBalance extends AbstractAuditableWithUTCDateTi
|
||||
|
||||
@Column(name = "amount_adjustment", scale = 6, precision = 19)
|
||||
private BigDecimal amountAdjustment;
|
||||
|
||||
@Column(name = "is_deleted", nullable = false)
|
||||
private boolean deleted = false;
|
||||
|
||||
@Column(name = "is_closed", nullable = false)
|
||||
private boolean closed = false;
|
||||
}
|
||||
|
||||
+20
-4
@@ -19,18 +19,34 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.repository;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface LoanBuyDownFeeBalanceRepository
|
||||
extends JpaRepository<LoanBuyDownFeeBalance, Long>, JpaSpecificationExecutor<LoanBuyDownFeeBalance> {
|
||||
|
||||
List<LoanBuyDownFeeBalance> findAllByLoanId(Long loanId);
|
||||
@Query("SELECT lbdfb FROM LoanBuyDownFeeBalance lbdfb WHERE lbdfb.loan.id=:loanId AND lbdfb.closed = FALSE ORDER BY lbdfb.date, lbdfb.createdDate")
|
||||
List<LoanBuyDownFeeBalance> findAllByLoanIdAndClosedFalse(@Param("loanId") Long loanId);
|
||||
|
||||
LoanBuyDownFeeBalance findByLoanIdAndLoanTransactionId(Long loanId, Long transactionId);
|
||||
@Query("SELECT lbdfb FROM LoanBuyDownFeeBalance lbdfb WHERE lbdfb.loan.id=:loanId AND lbdfb.closed = FALSE AND lbdfb.deleted = FALSE ORDER BY lbdfb.date, lbdfb.createdDate")
|
||||
List<LoanBuyDownFeeBalance> findAllByLoanIdAndDeletedFalseAndClosedFalse(@Param("loanId") Long loanId);
|
||||
|
||||
@Query("SELECT lbdfb FROM LoanBuyDownFeeBalance lbdfb, LoanTransaction lt, LoanTransactionRelation ltr WHERE lt.loan.id = lbdfb.loan.id AND ltr.fromTransaction.id =:transactionId AND ltr.toTransaction.id=lt.id AND lbdfb.loanTransaction.id = lt.id")
|
||||
LoanBuyDownFeeBalance findBalanceForAdjustment(Long transactionId);
|
||||
LoanBuyDownFeeBalance findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(Long loanId, Long transactionId);
|
||||
|
||||
@Query("SELECT lbdfb FROM LoanBuyDownFeeBalance lbdfb, LoanTransaction lt, LoanTransactionRelation ltr WHERE lt.loan.id = lbdfb.loan.id AND ltr.fromTransaction.id =:transactionId AND ltr.toTransaction.id=lt.id AND lbdfb.loanTransaction.id = lt.id AND lbdfb.deleted = false AND lbdfb.closed = false")
|
||||
LoanBuyDownFeeBalance findBalanceForAdjustment(@Param("transactionId") Long transactionId);
|
||||
|
||||
@Query("""
|
||||
SELECT new org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO(
|
||||
l.id, l.externalId, lbdfb.loanTransaction.id, lbdfb.date, lbdfb.amount,
|
||||
lbdfb.unrecognizedAmount, lbdfb.chargedOffAmount, lbdfb.amountAdjustment
|
||||
) FROM LoanBuyDownFeeBalance lbdfb JOIN lbdfb.loan l JOIN lbdfb.loanTransaction bt
|
||||
WHERE lbdfb.loanTransaction.id = :loanTransactionId AND l.id = :loanId
|
||||
""")
|
||||
AmortizationAllocationBaseTransactionDTO findBaseTransactionInfo(@Param("loanTransactionId") Long loanTransactionId,
|
||||
@Param("loanId") Long loanId);
|
||||
}
|
||||
|
||||
+23
-6
@@ -20,30 +20,47 @@ package org.apache.fineract.portfolio.loanaccount.repository;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface LoanCapitalizedIncomeBalanceRepository extends JpaRepository<LoanCapitalizedIncomeBalance, Long>,
|
||||
JpaSpecificationExecutor<LoanCapitalizedIncomeBalance>, CustomizedLoanCapitalizedIncomeBalanceRepository {
|
||||
|
||||
String FIND_BALANCE_REPAYMENT_SCHEDULE_DATA = "SELECT new org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData(lcib.loanTransaction.id, lcib.loan.id, lcib.loanTransaction.dateOf, lcib.loanTransaction.reversed, lcib.amount, lcib.unrecognizedAmount, lcib.loanTransaction.feeChargesPortion) FROM LoanCapitalizedIncomeBalance lcib ";
|
||||
|
||||
List<LoanCapitalizedIncomeBalance> findAllByLoanId(Long loanId);
|
||||
@Query("SELECT lcib FROM LoanCapitalizedIncomeBalance lcib WHERE lcib.loan.id=:loanId AND lcib.closed = FALSE ORDER BY lcib.date, lcib.createdDate")
|
||||
List<LoanCapitalizedIncomeBalance> findAllByLoanIdAndClosedFalse(@Param("loanId") Long loanId);
|
||||
|
||||
LoanCapitalizedIncomeBalance findByLoanIdAndLoanTransactionId(Long loanId, Long transactionId);
|
||||
@Query("SELECT lcib FROM LoanCapitalizedIncomeBalance lcib WHERE lcib.loan.id=:loanId AND lcib.closed = FALSE AND lcib.deleted = FALSE ORDER BY lcib.date, lcib.createdDate")
|
||||
List<LoanCapitalizedIncomeBalance> findAllByLoanIdAndDeletedFalseAndClosedFalse(@Param("loanId") Long loanId);
|
||||
|
||||
@Query(FIND_BALANCE_REPAYMENT_SCHEDULE_DATA + " WHERE lcib.loan.id = :loanId")
|
||||
LoanCapitalizedIncomeBalance findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(Long loanId, Long transactionId);
|
||||
|
||||
@Query(FIND_BALANCE_REPAYMENT_SCHEDULE_DATA
|
||||
+ " WHERE lcib.loan.id = :loanId AND lcib.deleted = false AND lcib.closed = false ORDER BY lcib.date, lcib.createdDate")
|
||||
List<LoanTransactionRepaymentPeriodData> findRepaymentPeriodDataByLoanId(Long loanId);
|
||||
|
||||
@Query("SELECT SUM(lcib.amount) FROM LoanCapitalizedIncomeBalance lcib WHERE lcib.loan.id = :loanId")
|
||||
@Query("SELECT SUM(lcib.amount) FROM LoanCapitalizedIncomeBalance lcib WHERE lcib.loan.id = :loanId AND lcib.deleted = false AND lcib.closed = false")
|
||||
BigDecimal calculateCapitalizedIncome(Long loanId);
|
||||
|
||||
@Query("SELECT SUM(lcib.amountAdjustment) FROM LoanCapitalizedIncomeBalance lcib WHERE lcib.loan.id = :loanId")
|
||||
@Query("SELECT SUM(lcib.amountAdjustment) FROM LoanCapitalizedIncomeBalance lcib WHERE lcib.loan.id = :loanId AND lcib.deleted = false AND lcib.closed = false")
|
||||
BigDecimal calculateCapitalizedIncomeAdjustment(Long loanId);
|
||||
|
||||
@Query("SELECT lcib FROM LoanCapitalizedIncomeBalance lcib, LoanTransaction lt, LoanTransactionRelation ltr WHERE lt.loan.id = lcib.loan.id AND ltr.fromTransaction.id =:transactionId AND ltr.toTransaction.id=lt.id AND lcib.loanTransaction.id = lt.id")
|
||||
@Query("SELECT lcib FROM LoanCapitalizedIncomeBalance lcib, LoanTransaction lt, LoanTransactionRelation ltr WHERE lt.loan.id = lcib.loan.id AND ltr.fromTransaction.id =:transactionId AND ltr.toTransaction.id=lt.id AND lcib.loanTransaction.id = lt.id AND lcib.deleted = false AND lcib.closed = false")
|
||||
LoanCapitalizedIncomeBalance findBalanceForAdjustment(Long transactionId);
|
||||
|
||||
@Query("""
|
||||
SELECT new org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO(
|
||||
l.id, l.externalId, lcib.loanTransaction.id, lcib.date, lcib.amount,
|
||||
lcib.unrecognizedAmount, lcib.chargedOffAmount, lcib.amountAdjustment
|
||||
) FROM LoanCapitalizedIncomeBalance lcib JOIN lcib.loan l JOIN lcib.loanTransaction bt
|
||||
WHERE lcib.loanTransaction.id = :loanTransactionId AND l.id = :loanId
|
||||
""")
|
||||
AmortizationAllocationBaseTransactionDTO findBaseTransactionInfo(@Param("loanTransactionId") Long loanTransactionId,
|
||||
@Param("loanId") Long loanId);
|
||||
}
|
||||
|
||||
+2
-1
@@ -45,7 +45,8 @@ public class BuyDownFeeReadPlatformServiceImpl implements BuyDownFeeReadPlatform
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.is.not.enabled.buydown.fee",
|
||||
"Loan: %s is not enabled Buydown fee feature".formatted(loanId), loanId);
|
||||
}
|
||||
final List<LoanBuyDownFeeBalance> buyDownFeeBalances = loanBuyDownFeeBalanceRepository.findAllByLoanId(loanId);
|
||||
final List<LoanBuyDownFeeBalance> buyDownFeeBalances = loanBuyDownFeeBalanceRepository
|
||||
.findAllByLoanIdAndDeletedFalseAndClosedFalse(loanId);
|
||||
|
||||
return buyDownFeeBalances.stream().map(this::mapToLoanBuyDownFeeAmortizationData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
+2
-2
@@ -165,8 +165,8 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
|
||||
LoanTransaction savedBuyDownFeeAdjustment = loanTransactionRepository.saveAndFlush(buyDownFeeAdjustment);
|
||||
|
||||
// Update buy down fee balance
|
||||
LoanBuyDownFeeBalance buydownFeeBalance = loanBuyDownFeeBalanceRepository.findByLoanIdAndLoanTransactionId(loanId,
|
||||
buyDownFeeTransactionId);
|
||||
LoanBuyDownFeeBalance buydownFeeBalance = loanBuyDownFeeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loanId, buyDownFeeTransactionId);
|
||||
if (buydownFeeBalance != null) {
|
||||
buydownFeeBalance.setAmountAdjustment(MathUtil.nullToZero(buydownFeeBalance.getAmountAdjustment()).add(transactionAmount));
|
||||
buydownFeeBalance
|
||||
|
||||
+4
-2
@@ -43,7 +43,8 @@ public class CapitalizedIncomeBalanceReadServiceImpl implements CapitalizedIncom
|
||||
if (loanRepository.isEnabledCapitalizedIncome(loanId)) {
|
||||
|
||||
List<CapitalizedIncomeDetails> capitalizedIncomeData = new ArrayList<>();
|
||||
List<LoanCapitalizedIncomeBalance> capitalizedIncomeBalances = capitalizedIncomeBalanceRepository.findAllByLoanId(loanId);
|
||||
List<LoanCapitalizedIncomeBalance> capitalizedIncomeBalances = capitalizedIncomeBalanceRepository
|
||||
.findAllByLoanIdAndDeletedFalseAndClosedFalse(loanId);
|
||||
for (final LoanCapitalizedIncomeBalance capitalizedIncomeBalance : capitalizedIncomeBalances) {
|
||||
final BigDecimal amortizedAmount = capitalizedIncomeBalance.getAmount() //
|
||||
.subtract(MathUtil.nullToZero(capitalizedIncomeBalance.getUnrecognizedAmount())) //
|
||||
@@ -67,7 +68,8 @@ public class CapitalizedIncomeBalanceReadServiceImpl implements CapitalizedIncom
|
||||
if (loanRepository.isEnabledCapitalizedIncome(loanId)) {
|
||||
|
||||
List<CapitalizedIncomeDetails> capitalizedIncomeData = new ArrayList<>();
|
||||
List<LoanCapitalizedIncomeBalance> capitalizedIncomeBalances = capitalizedIncomeBalanceRepository.findAllByLoanId(loanId);
|
||||
List<LoanCapitalizedIncomeBalance> capitalizedIncomeBalances = capitalizedIncomeBalanceRepository
|
||||
.findAllByLoanIdAndDeletedFalseAndClosedFalse(loanId);
|
||||
for (final LoanCapitalizedIncomeBalance capitalizedIncomeBalance : capitalizedIncomeBalances) {
|
||||
final BigDecimal amortizedAmount = capitalizedIncomeBalance.getAmount() //
|
||||
.subtract(MathUtil.nullToZero(capitalizedIncomeBalance.getUnrecognizedAmount())) //
|
||||
|
||||
+2
-2
@@ -159,8 +159,8 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
||||
// Create journal entries immediately for this transaction
|
||||
journalEntryPoster.postJournalEntriesForLoanTransaction(savedCapitalizedIncomeAdjustment, false, false);
|
||||
|
||||
LoanCapitalizedIncomeBalance capitalizedIncomeBalance = capitalizedIncomeBalanceRepository.findByLoanIdAndLoanTransactionId(loanId,
|
||||
capitalizedIncomeTransactionId);
|
||||
LoanCapitalizedIncomeBalance capitalizedIncomeBalance = capitalizedIncomeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loanId, capitalizedIncomeTransactionId);
|
||||
capitalizedIncomeBalance
|
||||
.setAmountAdjustment(MathUtil.nullToZero(capitalizedIncomeBalance.getAmountAdjustment()).add(transactionAmount));
|
||||
capitalizedIncomeBalance.setUnrecognizedAmount(
|
||||
|
||||
+3
-3
@@ -224,7 +224,7 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
|
||||
}
|
||||
if (transactionAmount != null) {
|
||||
LoanCapitalizedIncomeBalance capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionId(loanId, capitalizedIncomeTransactionId);
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loanId, capitalizedIncomeTransactionId);
|
||||
if (MathUtil.isLessThan(capitalizedIncomeBalance.getAmount()
|
||||
.subtract(MathUtil.nullToZero(capitalizedIncomeBalance.getAmountAdjustment())), transactionAmount)) {
|
||||
baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).failWithCode(
|
||||
@@ -401,8 +401,8 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
|
||||
|
||||
}
|
||||
if (transactionAmount != null) {
|
||||
LoanBuyDownFeeBalance buydownFeeBalance = loanBuydownFeeBalanceRepository.findByLoanIdAndLoanTransactionId(loanId,
|
||||
buyDownFeeTransactionId);
|
||||
LoanBuyDownFeeBalance buydownFeeBalance = loanBuydownFeeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loanId, buyDownFeeTransactionId);
|
||||
if (buydownFeeBalance == null) {
|
||||
baseDataValidator.reset().parameter("buyDownFeeTransactionId").failWithCode("buydown.fee.balance.not.found",
|
||||
"Buy down fee balance not found for the specified transaction.");
|
||||
|
||||
+1
@@ -123,6 +123,7 @@
|
||||
<class>org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement</class>
|
||||
<class>org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementDomain</class>
|
||||
<class>org.apache.fineract.portfolio.collateral.domain.LoanCollateral</class>
|
||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping</class>
|
||||
|
||||
<!-- Loan Module Converters -->
|
||||
<class>org.apache.fineract.portfolio.loanproduct.domain.AllocationTypeListConverter</class>
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationMappingDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData.AmortizationMappingData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeeBalanceRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoanAmortizationAllocationServiceImpl implements LoanAmortizationAllocationService {
|
||||
|
||||
private final LoanAmortizationAllocationMappingRepository loanAmortizationAllocationMappingRepository;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository;
|
||||
private final LoanBuyDownFeeBalanceRepository buyDownFeeBalanceRepository;
|
||||
|
||||
@Override
|
||||
public LoanAmortizationAllocationData retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(final Long loanTransactionId,
|
||||
final Long loanId) {
|
||||
final LoanTransaction loanTransaction = this.loanTransactionRepository.findByIdAndLoanId(loanTransactionId, loanId)
|
||||
.orElseThrow(() -> new LoanTransactionNotFoundException(loanTransactionId));
|
||||
if (!LoanTransactionType.BUY_DOWN_FEE.equals(loanTransaction.getTypeOf())) {
|
||||
throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.buydown.fee.transaction",
|
||||
"Transaction with ID " + loanTransactionId + " is not a Buy Down Fee transaction");
|
||||
}
|
||||
return retrieveLoanAmortizationAllocationData(loanTransaction, loanId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanAmortizationAllocationData retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(final Long loanTransactionId,
|
||||
final Long loanId) {
|
||||
final LoanTransaction loanTransaction = this.loanTransactionRepository.findByIdAndLoanId(loanTransactionId, loanId)
|
||||
.orElseThrow(() -> new LoanTransactionNotFoundException(loanTransactionId));
|
||||
if (!LoanTransactionType.CAPITALIZED_INCOME.equals(loanTransaction.getTypeOf())) {
|
||||
throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.capitalized.income.transaction",
|
||||
"Transaction with ID " + loanTransactionId + " is not a Capitalized Income transaction");
|
||||
}
|
||||
return retrieveLoanAmortizationAllocationData(loanTransaction, loanId);
|
||||
}
|
||||
|
||||
private LoanAmortizationAllocationData retrieveLoanAmortizationAllocationData(final LoanTransaction loanTransaction,
|
||||
final Long loanId) {
|
||||
try {
|
||||
final Long loanTransactionId = loanTransaction.getId();
|
||||
final AmortizationAllocationBaseTransactionDTO baseTransactionInfo = getBaseTransactionInfo(loanTransaction, loanId);
|
||||
if (baseTransactionInfo == null) {
|
||||
throw new LoanTransactionNotFoundException(loanTransactionId);
|
||||
}
|
||||
|
||||
final List<AmortizationAllocationMappingDTO> amortizationMappings = loanAmortizationAllocationMappingRepository
|
||||
.findAmortizationMappingsByBaseTransactionAndLoan(loanTransactionId, loanId);
|
||||
|
||||
final List<AmortizationMappingData> mappings = amortizationMappings.stream()
|
||||
.map(dto -> AmortizationMappingData.builder().amortizationLoanTransactionId(dto.getAmortizationLoanTransactionId())
|
||||
.amortizationLoanTransactionExternalId(dto.getAmortizationLoanTransactionExternalId())
|
||||
.date(dto.getAmortizationDate()).type(dto.getAmortizationType()).amount(dto.getAmount()).build())
|
||||
.toList();
|
||||
|
||||
return new LoanAmortizationAllocationData(baseTransactionInfo.getLoanId(), baseTransactionInfo.getLoanExternalId(),
|
||||
baseTransactionInfo.getBaseLoanTransactionId(), baseTransactionInfo.getBaseLoanTransactionDate(),
|
||||
baseTransactionInfo.getBaseLoanTransactionAmount(), baseTransactionInfo.getUnrecognizedAmount(),
|
||||
baseTransactionInfo.getChargedOffAmount(), baseTransactionInfo.getAdjustmentAmount(), mappings);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new LoanTransactionNotFoundException(loanTransaction.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private AmortizationAllocationBaseTransactionDTO getBaseTransactionInfo(final LoanTransaction loanTransaction, final Long loanId) {
|
||||
if (loanTransaction.isBuyDownFee()) {
|
||||
return buyDownFeeBalanceRepository.findBaseTransactionInfo(loanTransaction.getId(), loanId);
|
||||
} else {
|
||||
return capitalizedIncomeBalanceRepository.findBaseTransactionInfo(loanTransaction.getId(), loanId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanAmortizationAllocationMapping createAmortizationAllocationMappingWithBaseLoanTransaction(
|
||||
final LoanTransaction loanTransaction, final BigDecimal amount, final AmortizationType amortizationType) {
|
||||
return new LoanAmortizationAllocationMapping(loanTransaction.getLoan().getId(), loanTransaction.getId(), null, null,
|
||||
amortizationType, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(
|
||||
final LoanAmortizationAllocationMapping amortizationAllocationMapping, final LoanTransaction amortizationTransaction) {
|
||||
final LoanAmortizationAllocationMapping updatedMapping = new LoanAmortizationAllocationMapping(
|
||||
amortizationAllocationMapping.getLoanId(), amortizationAllocationMapping.getBaseLoanTransactionId(),
|
||||
amortizationTransaction.getTransactionDate(), amortizationTransaction.getId(),
|
||||
amortizationAllocationMapping.getAmortizationType(), amortizationAllocationMapping.getAmount());
|
||||
loanAmortizationAllocationMappingRepository.save(updatedMapping);
|
||||
}
|
||||
|
||||
}
|
||||
+138
-29
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -33,7 +34,10 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||
@@ -54,34 +58,83 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanJournalEntryPoster journalEntryPoster;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final LoanAmortizationAllocationService loanAmortizationAllocationService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void processBuyDownFeeAmortizationTillDate(@NonNull Loan loan, @NonNull LocalDate tillDate, boolean addJournal) {
|
||||
List<LoanBuyDownFeeBalance> balances = loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId());
|
||||
final List<LoanBuyDownFeeBalance> balances = loanBuyDownFeeBalanceRepository.findAllByLoanIdAndClosedFalse(loan.getId());
|
||||
|
||||
LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate()
|
||||
final LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate()
|
||||
: getFinalBuyDownFeeAmortizationTransactionDate(loan);
|
||||
LocalDate tillDatePlusOne = tillDate.plusDays(1);
|
||||
if (tillDatePlusOne.isAfter(maturityDate)) {
|
||||
tillDatePlusOne = maturityDate;
|
||||
}
|
||||
|
||||
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
|
||||
Money totalAmortization = Money.zero(loan.getCurrency());
|
||||
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
|
||||
BigDecimal totalAmortizedAccumulator = totalAmortized;
|
||||
for (LoanBuyDownFeeBalance balance : balances) {
|
||||
List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments, maturityDate,
|
||||
loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), tillDatePlusOne, loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate);
|
||||
|
||||
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(amortizationTillDate.getAmount()));
|
||||
BigDecimal amortizationAmount;
|
||||
AmortizationType amortizationType;
|
||||
if (!balance.isDeleted()) {
|
||||
final List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
final Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), tillDatePlusOne, loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate);
|
||||
if (!adjustments.isEmpty()) {
|
||||
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
|
||||
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(balance.getLoanTransaction().getId(),
|
||||
loan.getId());
|
||||
BigDecimal amortizedAmount = BigDecimal.ZERO;
|
||||
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
|
||||
.getAmortizationMappings()) {
|
||||
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
|
||||
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
|
||||
}
|
||||
}
|
||||
amortizationAmount = amortizedAmount;
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
|
||||
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else {
|
||||
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
} else {
|
||||
amortizationAmount = totalAmortization.getAmount().subtract(totalAmortizedAccumulator);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(amortizationTillDate.getAmount()));
|
||||
} else {
|
||||
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
balance.setClosed(true);
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
}
|
||||
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
|
||||
.createAmortizationAllocationMappingWithBaseLoanTransaction(balance.getLoanTransaction(), amortizationAmount,
|
||||
amortizationType);
|
||||
loanAmortizationAllocationMappings.add(loanAmortizationAllocationMapping);
|
||||
}
|
||||
}
|
||||
|
||||
loanBuyDownFeeBalanceRepository.saveAll(balances);
|
||||
|
||||
BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
|
||||
BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized);
|
||||
final BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized);
|
||||
|
||||
if (!MathUtil.isZero(totalAmortizationAmount)) {
|
||||
LoanTransaction transaction = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
@@ -91,14 +144,17 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
Money.of(loan.getCurrency(), MathUtil.negate(totalAmortizationAmount)), tillDate, externalIdFactory.create());
|
||||
loan.addLoanTransaction(transaction);
|
||||
|
||||
transaction = loanTransactionRepository.save(transaction);
|
||||
loanTransactionRepository.flush();
|
||||
transaction = loanTransactionRepository.saveAndFlush(transaction);
|
||||
final LoanTransaction finalTransaction = transaction;
|
||||
loanAmortizationAllocationMappings.forEach(loanAmortizationAllocationMapping -> loanAmortizationAllocationService
|
||||
.setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(loanAmortizationAllocationMapping,
|
||||
finalTransaction));
|
||||
|
||||
if (addJournal) {
|
||||
journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false);
|
||||
}
|
||||
|
||||
BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
final BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
? new LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent(transaction)
|
||||
: new LoanBuyDownFeeAmortizationAdjustmentTransactionCreatedBusinessEvent(transaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(event);
|
||||
@@ -165,7 +221,7 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
|
||||
});
|
||||
|
||||
for (LoanBuyDownFeeBalance balance : loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId())) {
|
||||
for (LoanBuyDownFeeBalance balance : loanBuyDownFeeBalanceRepository.findAllByLoanIdAndDeletedFalseAndClosedFalse(loan.getId())) {
|
||||
balance.setUnrecognizedAmount(balance.getChargedOffAmount());
|
||||
balance.setChargedOffAmount(BigDecimal.ZERO);
|
||||
}
|
||||
@@ -173,25 +229,75 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
|
||||
private Optional<LoanTransaction> createBuyDownFeeAmortizationTransaction(final Loan loan, final LocalDate transactionDate,
|
||||
final boolean isChargeOff, final LoanTransaction chargeOffTransaction) {
|
||||
ExternalId externalId = externalIdFactory.create();
|
||||
final ExternalId externalId = externalIdFactory.create();
|
||||
|
||||
final List<LoanBuyDownFeeBalance> balances = loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId());
|
||||
final List<LoanBuyDownFeeBalance> balances = loanBuyDownFeeBalanceRepository.findAllByLoanIdAndClosedFalse(loan.getId());
|
||||
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
|
||||
|
||||
BigDecimal totalAmortizationAmount = BigDecimal.ZERO;
|
||||
BigDecimal totalAmortization = BigDecimal.ZERO;
|
||||
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
|
||||
BigDecimal totalAmortizedAccumulator = totalAmortized;
|
||||
for (LoanBuyDownFeeBalance balance : balances) {
|
||||
List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : transactionDate;
|
||||
final Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), maturityDate, loan.getCurrency());
|
||||
totalAmortizationAmount = totalAmortizationAmount.add(amortizationTillDate.getAmount());
|
||||
if (isChargeOff) {
|
||||
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
|
||||
BigDecimal amortizationAmount;
|
||||
AmortizationType amortizationType;
|
||||
if (!balance.isDeleted()) {
|
||||
final List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
final LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : transactionDate;
|
||||
final Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), maturityDate, loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate.getAmount());
|
||||
if (!adjustments.isEmpty()) {
|
||||
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
|
||||
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(balance.getLoanTransaction().getId(),
|
||||
loan.getId());
|
||||
BigDecimal amortizedAmount = BigDecimal.ZERO;
|
||||
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
|
||||
.getAmortizationMappings()) {
|
||||
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
|
||||
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
|
||||
}
|
||||
}
|
||||
amortizationAmount = amortizedAmount;
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
|
||||
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else {
|
||||
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
} else {
|
||||
amortizationAmount = totalAmortization.subtract(totalAmortizedAccumulator);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
if (isChargeOff) {
|
||||
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
|
||||
}
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
} else {
|
||||
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
balance.setClosed(true);
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
}
|
||||
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
|
||||
.createAmortizationAllocationMappingWithBaseLoanTransaction(balance.getLoanTransaction(), amortizationAmount,
|
||||
amortizationType);
|
||||
loanAmortizationAllocationMappings.add(loanAmortizationAllocationMapping);
|
||||
}
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
BigDecimal amortizedAmount = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
|
||||
BigDecimal totalUnrecognizedAmount = totalAmortizationAmount.subtract(amortizedAmount);
|
||||
final BigDecimal totalUnrecognizedAmount = totalAmortization.subtract(totalAmortized);
|
||||
if (MathUtil.isZero(totalUnrecognizedAmount)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -207,6 +313,9 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
|
||||
|
||||
loan.addLoanTransaction(amortizationTransaction);
|
||||
loanTransactionRepository.saveAndFlush(amortizationTransaction);
|
||||
loanAmortizationAllocationMappings.forEach(loanAmortizationAllocationMapping -> loanAmortizationAllocationService
|
||||
.setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(loanAmortizationAllocationMapping,
|
||||
amortizationTransaction));
|
||||
|
||||
return Optional.of(amortizationTransaction);
|
||||
}
|
||||
|
||||
+142
-28
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -34,7 +35,10 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||
@@ -56,6 +60,7 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanJournalEntryPoster journalEntryPoster;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final LoanAmortizationAllocationService loanAmortizationAllocationService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@@ -108,23 +113,75 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
|
||||
final List<LoanCapitalizedIncomeBalance> balances = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loan.getId());
|
||||
final List<LoanCapitalizedIncomeBalance> balances = loanCapitalizedIncomeBalanceRepository
|
||||
.findAllByLoanIdAndClosedFalse(loan.getId());
|
||||
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
|
||||
|
||||
BigDecimal totalAmortizationAmount = BigDecimal.ZERO;
|
||||
BigDecimal totalAmortization = BigDecimal.ZERO;
|
||||
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
|
||||
BigDecimal totalAmortizedAccumulator = totalAmortized;
|
||||
for (LoanCapitalizedIncomeBalance balance : balances) {
|
||||
List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : transactionDate;
|
||||
final Money amortizationTillDate = CapitalizedIncomeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), maturityDate, loan.getCurrency());
|
||||
totalAmortizationAmount = totalAmortizationAmount.add(amortizationTillDate.getAmount());
|
||||
if (isChargeOff) {
|
||||
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
|
||||
BigDecimal amortizationAmount;
|
||||
AmortizationType amortizationType;
|
||||
if (!balance.isDeleted()) {
|
||||
final List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
final LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : transactionDate;
|
||||
final Money amortizationTillDate = CapitalizedIncomeAmortizationUtil.calculateTotalAmortizationTillDate(balance,
|
||||
adjustments, maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), maturityDate,
|
||||
loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate.getAmount());
|
||||
if (!adjustments.isEmpty()) {
|
||||
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
|
||||
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(balance.getLoanTransaction().getId(),
|
||||
loan.getId());
|
||||
BigDecimal amortizedAmount = BigDecimal.ZERO;
|
||||
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
|
||||
.getAmortizationMappings()) {
|
||||
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
|
||||
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
|
||||
}
|
||||
}
|
||||
amortizationAmount = amortizedAmount;
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
|
||||
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else {
|
||||
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
} else {
|
||||
amortizationAmount = totalAmortization.subtract(totalAmortizedAccumulator);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
if (isChargeOff) {
|
||||
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
|
||||
}
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
} else {
|
||||
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
balance.setClosed(true);
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
}
|
||||
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
|
||||
.createAmortizationAllocationMappingWithBaseLoanTransaction(balance.getLoanTransaction(), amortizationAmount,
|
||||
amortizationType);
|
||||
loanAmortizationAllocationMappings.add(loanAmortizationAllocationMapping);
|
||||
}
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
BigDecimal amortizedAmount = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
|
||||
BigDecimal totalUnrecognizedAmount = totalAmortizationAmount.subtract(amortizedAmount);
|
||||
final BigDecimal totalUnrecognizedAmount = totalAmortization.subtract(totalAmortized);
|
||||
if (MathUtil.isZero(totalUnrecognizedAmount)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -141,6 +198,9 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
|
||||
loan.addLoanTransaction(amortizationTransaction);
|
||||
loanTransactionRepository.saveAndFlush(amortizationTransaction);
|
||||
loanAmortizationAllocationMappings.forEach(loanAmortizationAllocationMapping -> loanAmortizationAllocationService
|
||||
.setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(loanAmortizationAllocationMapping,
|
||||
amortizationTransaction));
|
||||
|
||||
return Optional.of(amortizationTransaction);
|
||||
}
|
||||
@@ -162,7 +222,8 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false);
|
||||
});
|
||||
|
||||
for (LoanCapitalizedIncomeBalance balance : loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loan.getId())) {
|
||||
for (LoanCapitalizedIncomeBalance balance : loanCapitalizedIncomeBalanceRepository
|
||||
.findAllByLoanIdAndDeletedFalseAndClosedFalse(loan.getId())) {
|
||||
balance.setUnrecognizedAmount(balance.getChargedOffAmount());
|
||||
balance.setChargedOffAmount(BigDecimal.ZERO);
|
||||
}
|
||||
@@ -172,30 +233,80 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
@Transactional
|
||||
public void processCapitalizedIncomeAmortizationTillDate(@NonNull final Loan loan, @NonNull final LocalDate tillDate,
|
||||
final boolean addJournal) {
|
||||
List<LoanCapitalizedIncomeBalance> balances = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loan.getId());
|
||||
final List<LoanCapitalizedIncomeBalance> balances = loanCapitalizedIncomeBalanceRepository
|
||||
.findAllByLoanIdAndClosedFalse(loan.getId());
|
||||
|
||||
LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate()
|
||||
final LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate()
|
||||
: getFinalCapitalizedIncomeAmortizationTransactionDate(loan);
|
||||
LocalDate tillDatePlusOne = tillDate.plusDays(1);
|
||||
if (tillDatePlusOne.isAfter(maturityDate)) {
|
||||
tillDatePlusOne = maturityDate;
|
||||
}
|
||||
|
||||
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
|
||||
Money totalAmortization = Money.zero(loan.getCurrency());
|
||||
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
|
||||
BigDecimal totalAmortizedAccumulator = totalAmortized;
|
||||
for (LoanCapitalizedIncomeBalance balance : balances) {
|
||||
List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
Money amortizationTillDate = CapitalizedIncomeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
|
||||
maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), tillDatePlusOne, loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate);
|
||||
|
||||
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(amortizationTillDate.getAmount()));
|
||||
BigDecimal amortizationAmount;
|
||||
AmortizationType amortizationType;
|
||||
if (!balance.isDeleted()) {
|
||||
final List<LoanTransaction> adjustments = loanTransactionRepository.findAdjustments(balance.getLoanTransaction());
|
||||
final Money amortizationTillDate = CapitalizedIncomeAmortizationUtil.calculateTotalAmortizationTillDate(balance,
|
||||
adjustments, maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), tillDatePlusOne,
|
||||
loan.getCurrency());
|
||||
totalAmortization = totalAmortization.add(amortizationTillDate);
|
||||
if (!adjustments.isEmpty()) {
|
||||
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
|
||||
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
|
||||
.retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(balance.getLoanTransaction().getId(),
|
||||
loan.getId());
|
||||
BigDecimal amortizedAmount = BigDecimal.ZERO;
|
||||
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
|
||||
.getAmortizationMappings()) {
|
||||
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
|
||||
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
|
||||
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
|
||||
}
|
||||
}
|
||||
amortizationAmount = amortizedAmount;
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
|
||||
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
} else {
|
||||
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
} else {
|
||||
amortizationAmount = totalAmortization.getAmount().subtract(totalAmortizedAccumulator);
|
||||
amortizationType = AmortizationType.AM;
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
|
||||
}
|
||||
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
|
||||
.subtract(amortizationTillDate.getAmount()));
|
||||
} else {
|
||||
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
|
||||
amortizationType = AmortizationType.AM_ADJ;
|
||||
balance.setClosed(true);
|
||||
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
|
||||
}
|
||||
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
|
||||
.createAmortizationAllocationMappingWithBaseLoanTransaction(balance.getLoanTransaction(), amortizationAmount,
|
||||
amortizationType);
|
||||
loanAmortizationAllocationMappings.add(loanAmortizationAllocationMapping);
|
||||
}
|
||||
}
|
||||
|
||||
loanCapitalizedIncomeBalanceRepository.saveAll(balances);
|
||||
|
||||
BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
|
||||
BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized);
|
||||
final BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized);
|
||||
|
||||
if (!MathUtil.isZero(totalAmortizationAmount)) {
|
||||
LoanTransaction transaction = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
@@ -205,14 +316,17 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
Money.of(loan.getCurrency(), MathUtil.negate(totalAmortizationAmount)), tillDate, externalIdFactory.create());
|
||||
loan.addLoanTransaction(transaction);
|
||||
|
||||
transaction = loanTransactionRepository.save(transaction);
|
||||
loanTransactionRepository.flush();
|
||||
transaction = loanTransactionRepository.saveAndFlush(transaction);
|
||||
final LoanTransaction finalTransaction = transaction;
|
||||
loanAmortizationAllocationMappings.forEach(loanAmortizationAllocationMapping -> loanAmortizationAllocationService
|
||||
.setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(loanAmortizationAllocationMapping,
|
||||
finalTransaction));
|
||||
|
||||
if (addJournal) {
|
||||
journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false);
|
||||
}
|
||||
|
||||
BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
final BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
? new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(transaction)
|
||||
: new LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent(transaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(event);
|
||||
|
||||
+18
-5
@@ -493,8 +493,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
final Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
||||
BigDecimal capitalizedIncomeBalance = BigDecimal.ZERO;
|
||||
if (loan.getLoanProduct().getLoanProductRelatedDetail().isEnableIncomeCapitalization()) {
|
||||
capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loanId).stream()
|
||||
.map(LoanCapitalizedIncomeBalance::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository.findAllByLoanIdAndDeletedFalseAndClosedFalse(loanId)
|
||||
.stream().map(LoanCapitalizedIncomeBalance::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
transactionAmount = loan.getLoanProduct().isAllowApprovedDisbursedAmountsOverApplied()
|
||||
? loanMaximumAmountCalculator.getOverAppliedMax(loan)
|
||||
@@ -517,7 +517,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
break;
|
||||
case CAPITALIZED_INCOME_ADJUSTMENT:
|
||||
final LoanCapitalizedIncomeBalance loanCapitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionId(loanId, transactionId);
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loanId, transactionId);
|
||||
|
||||
transactionAmount = (loanCapitalizedIncomeBalance == null) ? BigDecimal.ZERO
|
||||
: loanCapitalizedIncomeBalance.getAmount()
|
||||
@@ -527,8 +527,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
paymentOptions, retriveLoanCurrencyData(loanId), classificationOptions);
|
||||
break;
|
||||
case BUY_DOWN_FEE_ADJUSTMENT:
|
||||
final LoanBuyDownFeeBalance loanBuyDownFeeBalance = loanBuyDownFeeBalanceRepository.findByLoanIdAndLoanTransactionId(loanId,
|
||||
transactionId);
|
||||
final LoanBuyDownFeeBalance loanBuyDownFeeBalance = loanBuyDownFeeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loanId, transactionId);
|
||||
|
||||
transactionAmount = (loanBuyDownFeeBalance == null) ? BigDecimal.ZERO
|
||||
: loanBuyDownFeeBalance.getAmount().subtract(MathUtil.nullToZero(loanBuyDownFeeBalance.getAmountAdjustment()));
|
||||
@@ -2522,6 +2522,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
return resolvedLoanId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getResolvedLoanTransactionId(final Long transactionId, final ExternalId externalTransactionId) {
|
||||
Long resolvedLoanTransactionId = transactionId;
|
||||
if (resolvedLoanTransactionId == null) {
|
||||
externalTransactionId.throwExceptionIfEmpty();
|
||||
resolvedLoanTransactionId = retrieveLoanTransactionIdByExternalId(externalTransactionId);
|
||||
if (resolvedLoanTransactionId == null) {
|
||||
throw new LoanTransactionNotFoundException(externalTransactionId);
|
||||
}
|
||||
}
|
||||
return resolvedLoanTransactionId;
|
||||
}
|
||||
|
||||
private LoanTransaction deriveDefaultInterestWaiverTransaction(final Loan loan) {
|
||||
final Money totalInterestOutstanding = loan.getTotalInterestOutstandingOnLoan();
|
||||
Money possibleInterestToWaive = totalInterestOutstanding.copy();
|
||||
|
||||
+7
-5
@@ -147,12 +147,13 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
}
|
||||
|
||||
LoanCapitalizedIncomeBalance capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionId(loan.getId(), transactionToAdjust.getId());
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loan.getId(), transactionToAdjust.getId());
|
||||
if (MathUtil.isGreaterThanZero(capitalizedIncomeBalance.getAmountAdjustment())) {
|
||||
throw new InvalidLoanTransactionTypeException("transaction", "capitalizedIncome.cannot.be.reversed.when.adjusted",
|
||||
"Capitalized income transaction cannot be reversed when non-reversed adjustment exists for it.");
|
||||
}
|
||||
loanCapitalizedIncomeBalanceRepository.delete(capitalizedIncomeBalance);
|
||||
capitalizedIncomeBalance.setDeleted(true);
|
||||
loanCapitalizedIncomeBalanceRepository.saveAndFlush(capitalizedIncomeBalance);
|
||||
}
|
||||
if (transactionToAdjust.isCapitalizedIncomeAdjustment()) {
|
||||
if (newTransactionDetail.isNotZero()) {
|
||||
@@ -174,14 +175,15 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
"Buy down fee transaction cannot be adjusted");
|
||||
}
|
||||
|
||||
LoanBuyDownFeeBalance buyDownFeeBalance = loanBuyDownFeeBalanceRepository.findByLoanIdAndLoanTransactionId(loan.getId(),
|
||||
transactionToAdjust.getId());
|
||||
LoanBuyDownFeeBalance buyDownFeeBalance = loanBuyDownFeeBalanceRepository
|
||||
.findByLoanIdAndLoanTransactionIdAndDeletedFalseAndClosedFalse(loan.getId(), transactionToAdjust.getId());
|
||||
|
||||
if (MathUtil.isGreaterThanZero(buyDownFeeBalance.getAmountAdjustment())) {
|
||||
throw new InvalidLoanTransactionTypeException("transaction", "buy.down.fee.cannot.be.reversed.when.adjusted",
|
||||
"Buy down fee transaction cannot be reversed when non-reversed adjustment exists for it.");
|
||||
}
|
||||
loanBuyDownFeeBalanceRepository.delete(buyDownFeeBalance);
|
||||
buyDownFeeBalance.setDeleted(true);
|
||||
loanBuyDownFeeBalanceRepository.saveAndFlush(buyDownFeeBalance);
|
||||
}
|
||||
if (transactionToAdjust.isBuyDownFeeAdjustment()) {
|
||||
if (newTransactionDetail.isNotZero()) {
|
||||
|
||||
+19
-4
@@ -70,6 +70,7 @@ import org.apache.fineract.portfolio.interestpauses.service.InterestPauseWritePl
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
@@ -118,6 +119,8 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualEventService
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAmortizationAllocationService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAmortizationAllocationServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanApplicationWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanApplicationWritePlatformServiceJpaRepositoryImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanArrearsAgingService;
|
||||
@@ -566,9 +569,10 @@ public class LoanAccountConfiguration {
|
||||
final ConfigurationDomainService configurationDomainService, final LoanTransactionRepository loanTransactionRepository,
|
||||
final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository,
|
||||
final BusinessEventNotifierService businessEventNotifierService, final LoanJournalEntryPoster journalEntryPoster,
|
||||
final ExternalIdFactory externalIdFactory) {
|
||||
final ExternalIdFactory externalIdFactory, final LoanAmortizationAllocationService loanAmortizationAllocationService) {
|
||||
return new LoanCapitalizedIncomeAmortizationProcessingServiceImpl(configurationDomainService, loanTransactionRepository,
|
||||
loanCapitalizedIncomeBalanceRepository, businessEventNotifierService, journalEntryPoster, externalIdFactory);
|
||||
loanCapitalizedIncomeBalanceRepository, businessEventNotifierService, journalEntryPoster, externalIdFactory,
|
||||
loanAmortizationAllocationService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -577,9 +581,9 @@ public class LoanAccountConfiguration {
|
||||
final LoanTransactionRepository loanTransactionRepository,
|
||||
final LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository,
|
||||
final BusinessEventNotifierService businessEventNotifierService, final LoanJournalEntryPoster journalEntryPoster,
|
||||
final ExternalIdFactory externalIdFactory) {
|
||||
final ExternalIdFactory externalIdFactory, final LoanAmortizationAllocationService loanAmortizationAllocationService) {
|
||||
return new LoanBuyDownFeeAmortizationProcessingServiceImpl(loanTransactionRepository, loanBuyDownFeeBalanceRepository,
|
||||
businessEventNotifierService, journalEntryPoster, externalIdFactory);
|
||||
businessEventNotifierService, journalEntryPoster, externalIdFactory, loanAmortizationAllocationService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -589,4 +593,15 @@ public class LoanAccountConfiguration {
|
||||
LoanBuyDownFeeAmortizationProcessingService loanBuyDownFeeAmortizationProcessingService) {
|
||||
return new LoanBuyDownFeeAmortizationEventService(businessEventNotifierService, loanBuyDownFeeAmortizationProcessingService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanAmortizationAllocationService.class)
|
||||
public LoanAmortizationAllocationService loanAmortizationAllocationService(
|
||||
final LoanAmortizationAllocationMappingRepository loanAmortizationAllocationMappingRepository,
|
||||
final LoanTransactionRepository loanTransactionRepository,
|
||||
final LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
|
||||
final LoanBuyDownFeeBalanceRepository buyDownFeeBalanceRepository) {
|
||||
return new LoanAmortizationAllocationServiceImpl(loanAmortizationAllocationMappingRepository, loanTransactionRepository,
|
||||
capitalizedIncomeBalanceRepository, buyDownFeeBalanceRepository);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,4 +213,7 @@
|
||||
<include file="parts/0192_create_loan_approved_amount_history.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0193_add_column_ip.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0194_fix_missing_permission.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0195_create_loan_amortization_allocation_mapping.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0196_add_deleted_and_closed_to_buy_down_fee_balance.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0197_add_deleted_and_closed_to_capitalized_income_balance.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||
<changeSet author="fineract" id="1" context="postgresql">
|
||||
<createTable tableName="m_loan_amortization_allocation_mapping">
|
||||
<column autoIncrement="true" name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_m_loan_amortization_allocation_mapping"/>
|
||||
</column>
|
||||
|
||||
<column name="loan_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="base_loan_transaction_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amortization_loan_transaction_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amortization_type" type="VARCHAR(20)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amount" type="DECIMAL(19, 6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="timestamp with time zone"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="timestamp with time zone"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
<changeSet author="fineract" id="2" context="mysql">
|
||||
<createTable tableName="m_loan_amortization_allocation_mapping">
|
||||
<column autoIncrement="true" name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_m_loan_amortization_allocation_mapping"/>
|
||||
</column>
|
||||
|
||||
<column name="loan_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="base_loan_transaction_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amortization_loan_transaction_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amortization_type" type="VARCHAR(20)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="amount" type="DECIMAL(19, 6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="DATETIME"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="DATETIME"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
<changeSet author="fineract" id="3">
|
||||
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_created_by" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_appuser" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_last_modified_by" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_appuser" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="loan_id" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_loan_id" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_loan" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="base_loan_transaction_id" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_base_transaction_id" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_loan_transaction" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="amortization_loan_transaction_id" baseTableName="m_loan_amortization_allocation_mapping"
|
||||
constraintName="FK_laam_amortization_transaction_id" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_loan_transaction" validate="true"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addColumn tableName="m_loan_buy_down_fee_balance">
|
||||
<column defaultValueBoolean="false" name="is_deleted" type="boolean">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column defaultValueBoolean="false" name="is_closed" type="boolean">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addColumn tableName="m_loan_capitalized_income_balance">
|
||||
<column defaultValueBoolean="false" name="is_deleted" type="boolean">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column defaultValueBoolean="false" name="is_closed" type="boolean">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user