[
https://issues.apache.org/jira/browse/FINERACT-2476?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18057592#comment-18057592
]
Ralph Hopman commented on FINERACT-2476:
----------------------------------------
Of course [~abdelrahman], go ahead!
> Enable Group Savings Accounts as Guarantors with Guarantee Percentage
> Requirements
> ----------------------------------------------------------------------------------
>
> Key: FINERACT-2476
> URL: https://issues.apache.org/jira/browse/FINERACT-2476
> Project: Apache Fineract
> Issue Type: Improvement
> Components: Loan, Savings
> Reporter: Ralph Hopman
> Priority: Minor
>
> Group savings accounts cannot be used as guarantors when loan products have
> non-zero guarantee percentage requirements (mandatoryGuarantee,
> minimumGuaranteeFromGuarantor, minimumGuaranteeFromOwnFunds > 0), limiting
> valid business scenarios for group lending.
> h2. Problem Statement
> Group savings accounts are currently blocked from serving as guarantors when
> loan products enforce minimum guarantee percentages. This creates an
> artificial limitation that prevents valid business scenarios, particularly in
> group-based lending models where group savings serve as collective collateral.
> h2. Current Behavior
> # *With zero guarantee requirements*
> ({{{}withOnHoldFundDetails("0","0","0"){}}}):
> ** Group accounts work as guarantors
> ** Holds are placed correctly on loan approval
> # *With non-zero guarantee requirements* (e.g.,
> {{{}withOnHoldFundDetails("20","10","10"){}}}):
> ** Group accounts fail validation during loan approval
> ** Error: Self-guarantee validation expects {{guarantor.entityId}} to match
> {{loan.clientId}}
> ** Group accounts have {{{}client_id = NULL{}}}, causing validation failure
> h2. Expected Behavior
> Group savings accounts should be usable as guarantors regardless of guarantee
> percentage requirements, as long as:
> * The guarantor account is active
> * Sufficient funds are available
> * The account is not in a restricted state
> * Standard guarantor business rules are met (excluding self-guarantee check
> for group accounts)
> h1. Technical Analysis
> h2. Root Cause
> {*}File{*}: {{GuarantorDomainServiceImpl.java}}
> {*}Method{*}: {{validateGuarantorBusinessRules(Loan loan)}}
> {code:java}
> if (loanProduct.isHoldGuaranteeFunds()) {
> // Validation logic runs here
> // Self-guarantee check assumes guarantor.entityId matches loan.clientId
> // For group accounts: client_id = NULL → validation fails
> }
> {code}
> The validation logic was designed with individual client accounts in mind and
> does not account for group-owned accounts as valid guarantors.
> h2. Database Schema Context
> {*}Table{*}: {{m_savings_account}}
> * Individual client accounts: {{{}client_id = [client_id]{}}}, {{group_id =
> NULL}}
> * Group accounts: {{{}client_id = NULL{}}}, {{group_id = [group_id]}}
> The self-guarantee validation checks if the savings account's {{client_id}}
> matches the loan borrower's {{{}client_id{}}}, which always fails for group
> accounts.
> h2. Proposed Solution
> Modify {{validateGuarantorBusinessRules()}} to:
> # Detect when a guarantor account is a group account ({{{}client_id =
> NULL{}}}, {{{}group_id != NULL{}}})
> # For group accounts, skip or adapt the self-guarantee validation
> # Optionally check if the borrower is a member of the guarantor group
> # Apply all other validation rules normally
> h2. Functional Requirements
> * Group savings accounts can be added as guarantors for loans with non-zero
> guarantee requirements
> * Automatic holds are placed on group guarantor accounts equal to the
> guarantee amount
> * Holds are placed on loan approval (existing timing)
> * Validation enforces guarantee percentage requirements correctly
> * Individual client accounts continue to work exactly as before (no
> regression)
> * Self-guarantee validation still applies to individual client accounts
> h2. Business Rules
> # Group account must be active
> # Group account must have sufficient available balance
> # Guarantee amount must meet minimum percentage requirements
> # Standard guarantor validation rules apply (excluding problematic
> self-guarantee check)
> h2. Technical Requirements
> # No changes to database schema required
> # Backward compatible with existing guarantor records
> # API responses unchanged (maintaining contract)
> # Existing tests continue to pass
> h1. Test Scenarios
> h2. Test Case 1: Group Account as Guarantor with 20% Minimum Guarantee
> {*}Setup{*}:
> * Loan product with {{withOnHoldFundDetails("20", "10", "10")}}
> * Loan amount: 10,000
> * Group savings account with balance: 10,000
> * Guarantee amount: 2,500 (25% of loan)
> {*}Steps{*}:
> # Create loan with group guarantor
> # Approve loan
> {*}Expected{*}:
> * Loan approval succeeds
> * Hold of 2,500 placed on group account
> * {{onHoldFunds = 2,500}}
> * {{availableBalance = 7,500}}
> h2. Test Case 2: Insufficient Guarantee Percentage
> {*}Setup{*}:
> * Loan product with {{withOnHoldFundDetails("20", "10", "10")}} (20%
> mandatory minimum)
> * Loan amount: 10,000
> * Group savings account with balance: 10,000
> * Guarantee amount: 1,000 (10% of loan)
> {*}Expected{*}:
> * Validation error: Guarantee amount below minimum requirement
> h2. Test Case 3: Mixed Guarantors
> {*}Setup{*}:
> * Loan with multiple guarantors:
> ** Guarantor 1: Individual client account (3,000)
> ** Guarantor 2: Group account (2,000)
> * Loan amount: 10,000 with 50% guarantee requirement
> {*}Expected{*}:
> * Total guarantee: 5,000 (50%) meets requirement
> * Holds placed on both accounts correctly
> h1. Code References
> h2. Files to Modify
> # *{{GuarantorDomainServiceImpl.java}}* (primary changes)
> ** Location:
> {{fineract-core/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/}}
> ** Method: {{validateGuarantorBusinessRules(Loan loan)}}
> ** Line: ~112 (where {{if (loanProduct.isHoldGuaranteeFunds())}} check
> occurs)
> # *Related Validation Logic*
> ** Self-guarantee checking logic
> ** Account ownership verification
> ** Minimum percentage validation
> h2. Relevant Database Tables
> * {{m_savings_account}} (guarantor source accounts)
> * {{m_guarantor}} (guarantor relationships)
> * {{m_loan}} (borrower information)
> * {{m_portfolio_account_associations}} (account associations)
> h2. Test Files to Update
> * {{GuarantorTest.java}} - Add group account scenarios
> * {{GroupSavingsIntegrationTest.java}} - Expand guarantor tests with
> non-zero percentages
> h1. Example Validation Logic Modification
> h2. Current Logic (Pseudocode)
> {code:java}
> if (loanProduct.isHoldGuaranteeFunds()) {
> for (Guarantor guarantor : loan.getGuarantors()) {
> // Validate account belongs to client
> if (!isAccountBelongsToClient(guarantor.savingsAccount,
> guarantor.entityId)) {
> throw new ValidationError("Account must belong to guarantor");
> }
>
> // Check self-guarantee
> if (guarantor.entityId.equals(loan.clientId)) {
> throw new ValidationError("Cannot self-guarantee");
> }
>
> // Other validations...
> }
> }
> {code}
> h2. Proposed Logic (Pseudocode)
> {code:java}
> if (loanProduct.isHoldGuaranteeFunds()) {
> for (Guarantor guarantor : loan.getGuarantors()) {
> SavingsAccount savingsAccount = guarantor.getSavingsAccount();
>
> // Determine account type
> boolean isGroupAccount = (savingsAccount.getClientId() == null &&
> savingsAccount.getGroupId() != null);
>
> if (isGroupAccount) {
> // Group account validation path
> validateGroupGuarantorAccount(savingsAccount, loan);
> } else {
> // Individual account validation path (existing logic)
> if (!isAccountBelongsToClient(savingsAccount,
> guarantor.entityId)) {
> throw new ValidationError("Account must belong to guarantor");
> }
>
> // Check self-guarantee (only for individual accounts)
> if (guarantor.entityId.equals(loan.clientId)) {
> throw new ValidationError("Cannot self-guarantee");
> }
> }
>
> // Common validations for both account types
> validateAccountStatus(savingsAccount);
> validateSufficientFunds(savingsAccount, guarantor.amount);
> // Other validations...
> }
> }
> {code}
> h1. Impact Assessment
> h2. Business Impact
> * {*}High positive{*}: Enables valid group lending scenarios
> * {*}Low risk{*}: Only affects guarantor validation logic
> * {*}Backward compatible{*}: No impact on existing functionality
> h2. Technical Impact
> * {*}Code changes{*}: Isolated to guarantor validation service
> * {*}Database{*}: No schema changes required
> * {*}API{*}: No breaking changes
> * {*}Performance{*}: Negligible (adds conditional check in validation)
> h1. Additional Notes
> h2. Workaround (Current)
> Loan products can use {{.withOnHoldFundDetails("0","0","0")}} to enable group
> guarantors, but this:
> * Disables all guarantee percentage enforcement
> * Cannot enforce business rules requiring minimum guarantees
> * Proves the technical capability exists
> h2. User Impact
> Financial institutions using group-based lending models (common in
> microfinance) currently cannot:
> * Use group savings as collateral when guarantee percentages are required
> * Enforce minimum guarantee amounts with group accounts
> * Fully leverage group solidarity savings for loan security
--
This message was sent by Atlassian Jira
(v8.20.10#820010)