-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Fineract-2460: Add Client Performance API for real-time financial metrics #5410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
|
@nidhiii128 Apache Fineract != Mifos please join the Apache Fineract list, involve with the Apache Fineract community. If you are interested please open an account in Jira, then review if the item has not been previously reported if not then you can open a new ticket https://selfserve.apache.org/jira-account.html Read and understand how to submit contibutions to Apache Fineract |
IOhacker
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kindly see my comments
Really sorry to misunderstand things, will understand more about the fineract project and help to contribute. Thank you for your the information. |
|
Don worry, your effort can be applied using the proper Jira ticket also Apache Fineract will be a GSOC participant. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nidhiii128 the PR and the commit must follow the title conventions, please use "Fineract-2460: Add Client Performance API for real-time financial metrics"
Squash and commit your changes because only 1 commit per PR is required.
Why not to use the https://{URL_BASE}/fineract-provider/api/v1/clients/{CLIENT_ID}/accounts endpoint ?
/accounts returns a large array of objects (full account summaries for every loan). my peformance endpoint returns exactly two numbers. Calculating the total on the database side (via SUM and COALESCE query) is significantly faster than fetching 50 loan objects and summing them in the Java frontend or mobile app. By returning exactly two numbers instead of a large object tree, we reduce the time-to-first-render for the Client Profile summary, which is critical for performance in low-bandwidth environments.
The endpoint returns groupLoanIndividualMonitoringAccounts and guarantorAccounts. If the user just wants to see their "Total Balance," they don't need to know about guarantor status. Even if these arrays were full of loans, the user would still be seeing 0.00$ for "Performance" because this endpoint doesn't sum up the totals for them. |
| @Consumes({ MediaType.APPLICATION_JSON }) | ||
| @Produces({ MediaType.APPLICATION_JSON }) | ||
| @Operation(summary = "Retrieve client performance metrics", description = "Returns a summary of active loans and outstanding balances for a specific client.") | ||
| @ApiResponses({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove ApiResponses anotation, It is not needed in newer java versions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only keep ApiResponse
| final String sql = "SELECT " + "(SELECT COUNT(*) FROM m_loan WHERE client_id = c.id AND loan_status_id = 300) as activeLoans, " | ||
| + "(SELECT COALESCE(SUM(principal_outstanding_derived + interest_outstanding_derived + fee_charges_outstanding_derived + penalty_charges_outstanding_derived), 0) " | ||
| + " FROM m_loan WHERE client_id = c.id AND loan_status_id = 300) as totalOutstanding " | ||
| + "FROM m_client c WHERE c.id = ?"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its a pure string concatenaton java 15 + can use textblock as it willl make SQL more readable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the use of StringBuilder?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StringBuilder will help if the sql written here was dynamic but i can see there is a simple static string. In that case there is no performance gain using this. This will make it harder to review lateron
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like this will be much better as it will compile only once
`
final String sql = """
SELECT
(SELECT COUNT(*)
FROM m_loan
WHERE client_id = c.id
AND loan_status_id = 300) AS activeLoans,
(SELECT COALESCE(
SUM(principal_outstanding_derived
+ interest_outstanding_derived
+ fee_charges_outstanding_derived
+ penalty_charges_outstanding_derived), 0)
FROM m_loan
WHERE client_id = c.id
AND loan_status_id = 300) AS totalOutstanding
FROM m_client c
WHERE c.id = ?
""";
`
| final String sql = "SELECT " + "(SELECT COUNT(*) FROM m_loan WHERE client_id = c.id AND loan_status_id = 300) as activeLoans, " | ||
| + "(SELECT COALESCE(SUM(principal_outstanding_derived + interest_outstanding_derived + fee_charges_outstanding_derived + penalty_charges_outstanding_derived), 0) " | ||
| + " FROM m_loan WHERE client_id = c.id AND loan_status_id = 300) as totalOutstanding " | ||
| + "FROM m_client c WHERE c.id = ?"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the use of StringBuilder?
|
@IOhacker, regarding the StringBuilder, as it is traditionally used in Fineract to manage multi-line SQL. However, I believe Java 15 Text Blocks are the more technically sound choice here. |
|
@nidhiii128 why use Sql and why not JPQL/JQL? |
|
|
@Aman-Mittal I have updated the ClientsApiResource.java to remove the redundant @ApiResponses wrappers for methods with a single response code. I've also converted the SQL query in ClientReadPlatformServiceImpl.java into a Java 15 Text Block for better readability and verified the output locally via Swagger/curl. Ready for review! |

Description
This project implements a robust API endpoint and service layer to retrieve and display performance-related data for a client within the Apache Fineract ecosystem. The implementation focuses on aggregating real-time financial metrics, including active loan counts and a comprehensive "Total Outstanding Balance" (Principal + Interest + Fees + Penalties) for active loan accounts.
Acceptance Criteria & Implementation Proof
What I Did: Developed a RESTful endpoint GET /clients/{clientId}/performance within the ClientsApiResource class.
What I Did: Implemented the retrieveClientPerformance method in the service layer to fetch real-time data from the database using an optimized SQL query.
What I Did: Utilized Fineract's JdbcTemplate to perform direct reads on the m_loan and m_client tables, ensuring the metrics reflect the current state of the database immediately upon request.
What I Did: Implemented error handling to detect non-existent client IDs, throwing a ClientNotFoundException which results in a standard Fineract 404 error response.
What I Did: Created ClientPerformanceApiIntegrationTest.java using the RestAssured framework to automate the verification of the API.
Proof: The test suite executed with a 100% success rate.
What I Did: Defined a dedicated Data Transfer Object (DTO), ClientPerformanceData, to ensure the response structure is consistent and types match the expected financial precision.
Supporting Evidence:
[Image 1: DTO Structure]: ClientPerformanceData.java showing the defined fields.
Technical Summary of Financial Logic
The balance is aggregated using the following formula for all loans with Status 300 (Active):
TotalOutstanding = Principal_{derived} + Interest_{derived} + Fees_{derived} + Penalties_{derived}
Using the _derived columns from the m_loan table ensures that we are using pre-calculated, high-performance data rather than re-calculating the entire schedule on every API call.
Checklist
Please make sure these boxes are checked before submitting your pull request - thanks!
Your assigned reviewer(s) will follow our guidelines for code reviews.