KRYSTALM7 opened a new pull request, #38: URL: https://github.com/apache/fineract-loan-origination/pull/38
## Description This PR implements the initial end-to-end Loan Origination System (LOS) for Apache Fineract as part of **FINERACT-2442**. The implementation introduces a modular architecture that separates REST APIs, the service layer, workflow management, credit scoring, and Fineract integration through well-defined interfaces. **Overall workflow implemented:** ``` Loan Application → Submit → Under Review → Credit Scoring → Multi-stage Approval Workflow → Approved → Disbursement → Fineract Loan Creation (Mock Adapter) ``` ### Major Features **REST API** : - Create loan application - Retrieve loan application - Submit application - Start review - Retrieve credit score - Record approval decisions - Disburse approved application **Service Layer** : - Loan application lifecycle - Approval workflow - Credit scoring - Disbursement orchestration Business rules are isolated from the REST layer. **Credit Scoring** — configurable credit scoring engine using strategy-based scoring factors (debt burden, employment, income ratio, loan purpose, repayment history). The score is persisted once an application enters the `UNDER_REVIEW` state. **Approval Workflow** — configurable multi-stage approval process. Current default workflow: 1. Loan Officer 2. Branch Manager 3. Credit Committee Workflow stages are configuration-driven to support institution-specific approval chains. Reject decisions require comments and immediately move the application to `REJECTED`. **Fineract Integration** — bridge layer that decouples Loan Origination from Apache Fineract: - Mock adapter - REST adapter - Configuration-driven adapter selection This allows local development without requiring a running Fineract instance. **Security** : Spring Security configuration, centralized exception handling, and request correlation support. **Persistence** : Support for loan applications, applicant profiles, credit scores, and approval stages, with Flyway migration updates required for Fineract integration. **Error Handling** : Centralized exception handling returns structured error responses (`error`, `message`, `status`, `timestamp`) for conflicts, not-found, and validation failures — see negative test scenarios below. **Testing** : Unit tests covering credit scoring, approval workflow, and the disbursement bridge. The complete workflow, including negative/edge cases, has been validated manually using Postman and PostgreSQL (details below). --- ## Related Jira - [FINERACT-2442](https://issues.apache.org/jira/browse/FINERACT-2442) --- ## Checklist *(official Apache Fineract PR checklist — only tick what is genuinely true for this PR)* - [x] Write the commit message as per [our guidelines](https://github.com/apache/fineract/blob/develop/CONTRIBUTING.md#pull-requests) - [x] Acknowledge that we will not review PRs that are not passing the build ("green") — it is your responsibility to get a proposed PR to pass the build, not primarily the project's maintainers. - [x] Create/update [unit or integration tests](https://fineract.apache.org/docs/current/#_testing) for verifying the changes made. - [x] Follow our [coding conventions](https://cwiki.apache.org/confluence/display/FINERACT/Coding+Conventions). - [ ] Add required Swagger annotation and update API documentation at `fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm` with details of any API changes. - [x] [This PR must not be a "code dump"](https://cwiki.apache.org/confluence/display/FINERACT/Pull+Request+Size+Limit). Large changes can be made in a branch, with assistance. --- ## How to Test ### 1. Start PostgreSQL and create the DB ```bash psql -U postgres -c "CREATE DATABASE los_db;" ``` ### 2. Build and run (Maven) ```bash mvn clean install mvn spring-boot:run ``` ### 3. Open Swagger UI ``` http://localhost:8082/swagger-ui/index.html ``` <img width="1920" height="964" alt="Screenshot (1368)" src="https://github.com/user-attachments/assets/33b82714-b62f-4d59-be49-60a7971b37bd" /> ### 4. Check the DB anytime with ```bash psql -U postgres -d los_db ``` --- ## Test Flow ### Step 1 — Create a loan application ``` POST http://localhost:8082/api/v1/loan-applications ``` ```json { "requestedAmount": 10000, "currency": "USD", "loanPurpose": "BUSINESS", "tenorMonths": 12, "fineractLoanProductId": 1, "applicant": { "fullName": "Sujan Kumar", "monthlyIncome": 3000, "employmentStatus": "EMPLOYED", "employmentDurationMonths": 36, "existingLoanObligations": 0, "fineractClientId": 999 } } ``` → creates `LOS-2026-00001` with status `DRAFT`. ### Step 2 — Submit ``` POST http://localhost:8082/api/v1/loan-applications/LOS-2026-00001/submit ``` ```sql SELECT application_ref, status FROM loan_application; ``` ### Step 3 — Start Review ``` POST http://localhost:8082/api/v1/loan-applications/LOS-2026-00001/start-review ``` ### Step 4 — Verify credit score was generated ```sql SELECT * FROM credit_score; ``` ### Step 5 — Loan Officer approval ``` POST http://localhost:8082/api/v1/loan-applications/LOS-2026-00001/approval-decisions ``` ```json { "stageName": "LOAN_OFFICER", "assignedOfficer": "officer-A", "decision": "APPROVE" } ``` ### Step 6 — Branch Manager approval ``` POST http://localhost:8082/api/v1/loan-applications/LOS-2026-00001/approval-decisions Content-Type: application/json ``` ```json { "stageName": "BRANCH_MANAGER", "assignedOfficer": "officer-B", "decision": "APPROVE" } ``` ```sql SELECT COUNT(*) FROM approval_stage; ``` ### Step 7 — Credit Committee approval ``` POST http://localhost:8082/api/v1/loan-applications/LOS-2026-00001/approval-decisions ``` ```json { "stageName": "CREDIT_COMMITTEE", "assignedOfficer": "officer-C", "decision": "APPROVE" } ``` ```sql SELECT COUNT(*) FROM approval_stage; SELECT application_ref, status FROM loan_application; ``` → status should now be `APPROVED`. ### Step 8 — Disburse ``` POST http://localhost:8082/api/v1/loan-applications/LOS-2026-00001/disburse ``` Response: ```json { "officeId": 1, "clientId": 999, "loanId": 100001, "resourceId": 100001 } ``` ### Step 9 — Confirm final state ``` GET http://localhost:8082/api/v1/loan-applications/LOS-2026-00001 ``` ```json { "applicationRef": "LOS-2026-00001", "createdAt": "2026-07-07T21:43:28.522894", "currency": "USD", "fineractLoanId": 100001, "fineractLoanProductId": 1, "loanPurpose": "BUSINESS", "requestedAmount": 10000.00, "status": "DISBURSED", "tenorMonths": 12, "updatedAt": "2026-07-07T23:21:35.919756" } ``` ``` GET http://localhost:8082/api/v1/loan-applications/LOS-2026-00001/credit-score ``` ```json { "debtBurdenScore": 25, "employmentScore": 20, "incomeRatioScore": 30, "loanPurposeScore": 8, "repaymentHistoryScore": 8, "riskCategory": "LOW", "score": 91, "scoredAt": "2026-07-07T23:09:07.408691" } ``` ``` GET http://localhost:8082/api/v1/loan-applications ``` Returns the list including the disbursed application above. <img width="1436" height="1035" alt="image" src="https://github.com/user-attachments/assets/dd1c087c-2f7c-45eb-8460-a55388945e7c" /> --- ## Negative / Edge Case Tests ### 1. Duplicate approval after disbursement ```http POST .../LOS-2026-00001/approval-decisions ``` ```json { "stageName": "LOAN_OFFICER", "assignedOfficer": "officer-A", "decision": "APPROVE" } ``` <img width="1437" height="762" alt="image" src="https://github.com/user-attachments/assets/95d90b47-9dbf-4710-b3a1-5a08bca0aaf2" /> ### 2. Disburse an already-disbursed application ```http POST .../LOS-2026-00001/disburse ``` <img width="1432" height="720" alt="image" src="https://github.com/user-attachments/assets/d4613460-05bb-4ea5-a5a6-978dfb2d65fc" /> ### 3. Unknown application reference ```http GET .../LOS-999999 ``` <img width="1436" height="657" alt="image" src="https://github.com/user-attachments/assets/99689674-661e-4781-be73-abf2e8ff838c" /> ### 4. Unknown Approval Stage ```http POST .../LOS-2026-00001/approval-decisions ``` ```json { "stageName": "CEO", "assignedOfficer": "officer-X", "decision": "APPROVE" } ``` <img width="1437" height="717" alt="Unknown approval stage response" src="https://github.com/user-attachments/assets/5c3de170-513c-4e8c-9a8e-ea726afc8b0b" /> ## Notes for Reviewers - This is the **initial** implementation of the LOS module; Fineract integration currently defaults to the **Mock Adapter** for local development, with a REST adapter available via configuration for connecting to a real Fineract instance. - Flyway migrations are additive only, scoped to the new LOS tables (`loan_application`, `applicant_profile`, `credit_score`, `approval_stage`) — no changes to existing Fineract core schema. - Error handling returns structured `409`/`404`/`400` responses for invalid state transitions, unknown references, and unknown approval stages (see negative tests above). - Suggested follow-up JIRA tickets: withdrawal flow, configurable scoring-weight persistence via admin API, hardening the real Fineract REST adapter, integration tests against a live Fineract instance. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
