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]

Reply via email to