Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Feature: Working Capital Loan Details
| balance.principal | 100.0 |
| balance.principalPaid | 0.0 |
| balance.principalOutstanding | 100.0 |
| balance.totalDisbursement | 0.0 |
| balance.totalDisbursement | 100.0 |
| balance.totalRepayment | 0.0 |
| balance.totalOutstanding | 100.0 |
| balance.totalExpectedRepayment | 100.0 |
Expand All @@ -100,7 +100,7 @@ Feature: Working Capital Loan Details
| balance.totalDiscountFeeAdjustment | 0.0 |
| summary.principal | 100.0 |
| summary.principalOutstanding | 100.0 |
| summary.totalDisbursement | 0.0 |
| summary.totalDisbursement | 100.0 |
| summary.totalOutstanding | 100.0 |
| summary.currency.code | EUR |
| summary.currency.name | Euro |
Expand Down Expand Up @@ -247,3 +247,139 @@ Feature: Working Capital Loan Details
| nearBreach.frequencyType.code | WEEKS |
| nearBreach.frequencyType.value | WEEKS |
| nearBreach.threshold | present |

@TestRailId:C98207
Scenario: Verify loan details returns the active principal following the reduced approve and disburse amounts
When Admin sets the business date to "01 January 2026"
And Admin creates a client with random data
And Admin creates a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount |
| WCLP | 01 January 2026 | 01 January 2026 | 500.0 | 500.0 | 1.0 | 100.0 |
Then Working capital loan creation was successful
Then Working capital loan details has the following field values:
| status.value | Submitted and pending approval |
| proposedPrincipal | 500.0 |
| approvedPrincipal | 0.0 |
| principal | 500.0 |
| netDisbursalAmount | null |
| balance.principal | 0.0 |
| balance.totalDisbursement | 0.0 |
| summary.principalOutstanding | 0.0 |
| summary.totalDisbursement | 0.0 |
When Admin successfully approves the working capital loan on "01 January 2026" with "450" amount and "45" discount amount and expected disbursement date on "01 January 2026"
Then Working capital loan approval was successful
Then Working capital loan details has the following field values:
| status.value | Approved |
| proposedPrincipal | 500.0 |
| approvedPrincipal | 450.0 |
| principal | 450.0 |
| approvedDiscountFee | 45.0 |
| discountFee | null |
| netDisbursalAmount | 450.0 |
| balance.principal | 0.0 |
| balance.totalDisbursement | 0.0 |
| summary.totalDisbursement | 0.0 |
And Admin successfully disburse the Working Capital loan on "01 January 2026" with "250" EUR transaction amount and "25" discount amount
Then Verify Working Capital loan disbursement was successful
Then Working capital loan details has the following field values:
| status.value | Active |
| proposedPrincipal | 500.0 |
| approvedPrincipal | 450.0 |
| principal | 250.0 |
| discountFee | 25.0 |
| netDisbursalAmount | 250.0 |
| balance.principal | 275.0 |
| balance.totalDiscountFee | 25.0 |
| balance.totalDisbursement | 250.0 |
| summary.principal | 275.0 |
| summary.principalOutstanding | 275.0 |
| summary.totalDisbursement | 250.0 |
Then Customer makes repayment on "01 January 2026" with 100.0 transaction amount on Working Capital loan
Then Working capital loan details has the following field values:
| principal | 250.0 |
| balance.principal | 275.0 |
| balance.principalPaid | 100.0 |
| balance.totalDisbursement | 250.0 |
| summary.principalOutstanding | 175.0 |

@TestRailId:C98208
Scenario: Verify loan details returns the modified principal as both proposed and active principal
When Admin sets the business date to "01 January 2026"
And Admin creates a client with random data
And Admin creates a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount |
| WCLP | 01 January 2026 | 01 January 2026 | 500.0 | 500.0 | 1.0 | 100.0 |
Then Working capital loan creation was successful
Then Working capital loan details has the following field values:
| proposedPrincipal | 500.0 |
| principal | 500.0 |
| disbursementDetails.0.principal | 500.0 |
When Admin modifies the working capital loan with the following data:
| submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount |
| | | 400.0 | | | |
Then Working capital loan details has the following field values:
| status.value | Submitted and pending approval |
| proposedPrincipal | 400.0 |
| approvedPrincipal | 0.0 |
| principal | 400.0 |
| disbursementDetails.0.principal | 400.0 |

@TestRailId:C98209
Scenario: Verify active principal and balances after undo disbursal, undo approval and re-disbursement
When Admin sets the business date to "01 January 2026"
And Admin creates a client with random data
And Admin creates a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount |
| WCLP | 01 January 2026 | 01 January 2026 | 500.0 | 500.0 | 1.0 | 100.0 |
Then Working capital loan creation was successful
When Admin successfully approves the working capital loan on "01 January 2026" with "450" amount and "45" discount amount and expected disbursement date on "01 January 2026"
Then Working capital loan approval was successful
And Admin successfully disburse the Working Capital loan on "01 January 2026" with "250" EUR transaction amount and "25" discount amount
Then Verify Working Capital loan disbursement was successful
Then Admin successfully undo Working Capital disbursal
Then Working capital loan details has the following field values:
| status.value | Approved |
| proposedPrincipal | 500.0 |
| approvedPrincipal | 450.0 |
| principal | 450.0 |
| discountFee | null |
| netDisbursalAmount | 450.0 |
| balance.principal | 0.0 |
| balance.principalPaid | 0.0 |
| balance.principalOutstanding | 0.0 |
| balance.totalOutstanding | 0.0 |
| balance.totalExpectedRepayment | 0.0 |
| balance.totalDisbursement | 0.0 |
| balance.totalDiscountFee | 0.0 |
| balance.unrealizedIncomeFromDiscountFee | 0.0 |
| summary.principal | 0.0 |
| summary.principalOutstanding | 0.0 |
| summary.totalDisbursement | 0.0 |
| summary.totalDiscountFee | 0.0 |
When Admin makes undo approval on the working capital loan
Then Working capital loan undo approval was successful
Then Working capital loan details has the following field values:
| status.value | Submitted and pending approval |
| proposedPrincipal | 500.0 |
| approvedPrincipal | 0.0 |
| principal | 500.0 |
| approvedDiscountFee | null |
When Admin successfully approves the working capital loan on "01 January 2026" with "450" amount and "45" discount amount and expected disbursement date on "01 January 2026"
Then Working capital loan approval was successful
And Admin successfully disburse the Working Capital loan on "01 January 2026" with "300" EUR transaction amount and "30" discount amount
Then Verify Working Capital loan disbursement was successful
Then Working capital loan details has the following field values:
| status.value | Active |
| proposedPrincipal | 500.0 |
| approvedPrincipal | 450.0 |
| principal | 300.0 |
| discountFee | 30.0 |
| netDisbursalAmount | 300.0 |
| disbursementDetails.0.principal | 450.0 |
| disbursementDetails.0.actualAmount | 300.0 |
| balance.principal | 330.0 |
| balance.principalOutstanding | 330.0 |
| balance.totalDisbursement | 300.0 |
| balance.totalDiscountFee | 30.0 |
| summary.principalOutstanding | 330.0 |
| summary.totalDisbursement | 300.0 |
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,15 @@ private GetWorkingCapitalLoansLoanIdResponse() {}
public String loanProductDescription;
public GetWorkingCapitalLoansLoanIdStatus status;
public GetWorkingCapitalLoansLoanIdTimeline timeline;
@Schema(example = "10000.00", description = "Principal requested at submission; never changes afterwards")
public BigDecimal proposedPrincipal;
@Schema(example = "10000.00", description = "Principal granted at approval; zero before approval and after undoing it "
+ "(deliberate Working Capital divergence from classic loans)")
public BigDecimal approvedPrincipal;
@Schema(example = "10000.00", description = "Active principal (loanProductRelatedDetails.principal)")
@Schema(example = "10000.00", description = "Active principal: the requested amount while the application is "
+ "pending, the granted amount from approval, the actually disbursed amount from disbursement. Undoing "
+ "approval or disbursal restores the previous stage's value. This is the contractual principal, not "
+ "the outstanding balance - see summary.principalOutstanding for that.")
public BigDecimal principal;
@Schema(example = "10000.00", description = "Net disbursal amount from the amortization schedule; null if schedule not yet generated")
public BigDecimal netDisbursalAmount;
Expand Down Expand Up @@ -334,6 +340,7 @@ private GetWorkingCapitalLoanSummary() {}
public BigDecimal realizedIncomeFromDiscountFee;
public BigDecimal unrealizedIncomeFromDiscountFee;
public BigDecimal overpayment;
@Schema(description = "Total amount actually disbursed")
public BigDecimal totalDisbursement;
public BigDecimal totalDiscountFee;
public BigDecimal totalDiscountFeeAdjustment;
Expand Down Expand Up @@ -389,7 +396,8 @@ private GetBalance() {}

@Schema(example = "1")
public Long id;
@Schema(example = "10000.00")
@Schema(example = "10000.00", description = "Total repayable principal: the disbursed amount plus the discount fee. "
+ "Zero until disbursement, and zero again once a disbursal is undone.")
public BigDecimal principal;
@Schema(example = "10000.00")
public BigDecimal principalPaid;
Expand Down Expand Up @@ -421,7 +429,7 @@ private GetBalance() {}
public BigDecimal totalRepayment;
@Schema(example = "10000.00")
public BigDecimal totalOutstanding;
@Schema(example = "10000.00")
@Schema(example = "10000.00", description = "Total amount actually disbursed")
public BigDecimal totalDisbursement;
@Schema(example = "10000.00")
public BigDecimal totalDiscountFee;
Expand All @@ -441,7 +449,8 @@ private GetDisbursementDetail() {}
@Schema(example = "1")
public Long loanId;
public LocalDate expectedDisbursementDate;
@Schema(example = "10000.00", description = "Expected (planned) disbursement principal")
@Schema(example = "10000.00", description = "Expected (planned) disbursement principal. Tracks the active "
+ "principal, so modifying a pending application or undoing an approval moves it too.")
public BigDecimal principal;
public LocalDate expectedMaturityDate;
public LocalDate actualDisbursementDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,16 @@ public class WorkingCapitalLoan extends AbstractAuditableWithUTCDateTimeCustom<L
@Column(name = "maturedon_date")
private LocalDate maturedOnDate;

/** Principal requested on the application. Set at submission, updated on modification, never changed afterwards. */
@Setter
@Column(name = "principal_amount_proposed", scale = 6, precision = 19, nullable = false)
private BigDecimal proposedPrincipal;

/**
* Principal granted at approval, defaulting to the proposed one. Zero before approval and after undoing it, which
* is a deliberate divergence from classic Fineract, where the approved principal mirrors the proposed one until it
* is actually approved.
*/
@Setter
@Column(name = "approved_principal", scale = 6, precision = 19, nullable = false)
private BigDecimal approvedPrincipal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public class WorkingCapitalLoanBalance extends AbstractAuditableWithUTCDateTimeC
@JoinColumn(name = "wc_loan_id", nullable = false, unique = true)
private WorkingCapitalLoan wcLoan;

/**
* Total repayable principal: the disbursed amount plus the discount fee, adjusted by later discount changes. Zero
* until disbursement, and zero again once a disbursal is undone - a loan whose disbursal was undone is
* indistinguishable from one that was only ever approved, because in both cases nothing has been paid out.
*/
@Column(name = "principal", scale = 6, precision = 19, nullable = false)
@Setter
private BigDecimal principal = BigDecimal.ZERO;
Expand Down Expand Up @@ -100,6 +105,7 @@ public class WorkingCapitalLoanBalance extends AbstractAuditableWithUTCDateTimeC
@Setter
private BigDecimal overpaymentAmount = BigDecimal.ZERO;

/** Total amount disbursed on the loan, excluding the discount fee. Reset to zero when the disbursal is undone. */
@Column(name = "total_disbursement", scale = 6, precision = 19, nullable = false)
@Setter
private BigDecimal totalDisbursement = BigDecimal.ZERO;
Expand Down Expand Up @@ -133,6 +139,7 @@ public void applyDisbursement(final BigDecimal disbursedAmount) {
.map(WorkingCapitalLoanProductRelatedDetails::getDiscount).orElse(BigDecimal.ZERO);
this.totalDiscountFee = discount;
this.principal = disbursedAmount.add(discount);
this.totalDisbursement = disbursedAmount;
this.overpaymentAmount = BigDecimal.ZERO;
}

Expand Down
Loading
Loading