# Create / Pending Applications

Gold Evaluation and Loan Offer movement are documented in [GOLD_EVALUATION.md](GOLD_EVALUATION.md).

The CRM page at `atd-gold-loan/src/components/crm/create-customer/CreateCustomer.jsx` reads the backend Pending queue. Website and staff submissions share `applications`. Source, Pending status, application number, and staff attribution are assigned by the backend; the CRM form does not ask staff to choose a source.

## Data

- `applications.application_no` is the decimal application ID (`4`, `5`, and so on), unique and required. Existing rows were converted to this format.
- New submissions reject a mobile or nonempty email already used by any application, including website submissions. The check runs inside a serializable transaction. Four preexisting records share the same mobile and email, so a database unique constraint cannot be added until those records are resolved. They were preserved.
- `applications.application_status` starts as `PENDING`. Allowed first moves are `FOLLOW_UP`, `REJECTED`, and `GOLD_EVALUATION`.
- `application_status_history` records each move with the admin and remark in the same database transaction as the status update.
- `application_kyc.application_id` is unique. Missing KYC is interpreted as `is_verified = 0`. Saving changed details or documents clears verification; an unchanged save preserves it. The staff-only Verify action requires complete saved data and does not change the application status.
- `kyc_documents` stores document metadata and private file keys. The API uploads KYC files to Firebase Storage using the Firebase Admin SDK when `KYC_STORAGE_PROVIDER=firebase`. Local storage remains available only when explicitly selected or Firebase is not configured in development.
- CRM applications use the authenticated admin's branch. The website loads its branch choices from the backend and submits `branch_id`; submissions with no selected branch remain unassigned and are visible in each branch's Pending queue. Legacy unmatched branch text is displayed as Unassigned.

## API

All CRM endpoints require an admin Bearer token.

CRM login issues a 15-minute access token and a private HttpOnly refresh cookie. The frontend renews access on a 401 and retries the request once. The server keeps the session for at most 24 hours, with an 8-hour inactivity limit; logout revokes it. The refresh cookie is never stored in browser JavaScript storage. Configure `CORS_ORIGINS` with the deployed frontend origin so the browser can send credentialed refresh requests.

| Method | Route | Purpose |
| --- | --- | --- |
| POST | `/api/public/applications` | Website submission. |
| GET | `/api/public/branches` | Real branch choices for the website. |
| POST | `/api/crm/applications` | Staff submission. Required: `name`, `mobile`; branch comes from admin token. Optional: `requested_amount`, `email`, `purpose`. |
| GET | `/api/crm/applications?status=PENDING&page=1&per_page=10&search=` | Paginated list. Also accepts `FOLLOW_UP`, `REJECTED`, `GOLD_EVALUATION`, date filters, and `branch_id` set to the staff branch ID or `unassigned`. |
| PATCH | `/api/crm/applications/status/:id` | Body `{ "status": "FOLLOW_UP", "remark": "Call tomorrow" }`. Remark is required for Follow-up and Reject. Gold Evaluation requires verified KYC. |
| GET | `/api/crm/applications/kyc/:id` | Load KYC page data from any queue. |
| POST | `/api/crm/applications/kyc/documents/:id?document_type=aadhaarFront` | Upload one PDF, PNG, or JPEG as `application/octet-stream`, max 5 MB. Returns `file_key`. `document_type` is required. |
| PUT | `/api/crm/applications/kyc/:id` | Save a draft with `details` and `documents: [{ document_type, file_key }]`; missing fields are allowed. Changed data clears verification. |
| POST | `/api/crm/applications/kyc/verify/:id` | Requires complete saved fields and all five documents. Application status is unchanged. |
| GET | `/api/crm/applications/kyc/document/:documentType/:id` | Authenticated preview of a document saved on this application's KYC record. |

The Pending and KYC pages use these APIs. The Pending page has Refresh, date, and branch filters, plus field-specific search by application number, name, mobile, or email. Field search sends `search_by` and `search_value`; the existing `search` parameter still searches all fields. Refresh reloads the current page with its active filters. KYC loading includes submitted email, date of birth, state, city, and PIN. A successful status change updates the same `applications` row and writes `application_status_history`. Pending no longer selects that row; the Follow-up, Rejected, or Gold Evaluation page selects it by its new status. There is no copying between queue tables.

The Follow-up page uses `GET /api/crm/applications?status=FOLLOW_UP` with the same search, date, branch, pagination, and Refresh controls. Each row includes the KYC flag (false when no KYC row exists), saved current address, and the latest remark and date from a move into Follow-up. The KYC action opens the existing application KYC page and returns to Follow-up. Staff may move a Follow-up application to Rejected with a required reason, or to Gold Evaluation after KYC verification. The frontend sends `EVALUATING` for that choice; the API accepts it and stores `GOLD_EVALUATION`. The backend checks branch access, current status, and current saved KYC before atomically updating the application and history. No new tables are used.

## Firebase Storage setup

The frontend `.env.local` contains Firebase **web** configuration and declares the Storage bucket. Those `NEXT_PUBLIC_` values are not backend upload credentials. The API server uses its own Firebase Admin credential.

1. Keep the Firebase service-account JSON for the `atd-goldloan` project outside version control; never put it in the frontend or a `NEXT_PUBLIC_` variable. The provided Admin SDK JSON is ignored by `.gitignore`.
2. The backend `.env.local` now sets `FIREBASE_STORAGE_BUCKET=atd-goldloan.firebasestorage.app`, `GOOGLE_APPLICATION_CREDENTIALS` to the provided JSON's absolute path, and `KYC_STORAGE_PROVIDER=firebase`.
3. Restart the backend. New KYC uploads go to `<document-folder>/<applicationId>/<uuid>.<extension>`. The database stores that private path as `file_key`; it does not store a public download URL.

KYC document folders: `aadhaarFront → aadharfront`, `aadhaarBack → aadharback`, `panCard → pancard`, `photo → photo`, and `addressProof → addressproof`. Each folder contains an application ID folder, which contains the uploaded file. Existing keys under `kyc/<applicationId>/` remain valid, including the earlier document subfolders.

The live smoke flow uploaded a PNG to Firebase Storage, checked its existence, saved and verified KYC, and deleted the test object. At configuration time there were no existing local KYC files to migrate. Switching providers does not automatically migrate local files if any are added later.

The frontend follows the `Atd-app` page/service pattern: each destination page has its own component and service (`followUpApplicationService.js`, `rejectedApplicationService.js`, `goldEvaluationService.js`). `pendingApplicationService.js`, `applicationKycService.js`, and `publicApplicationService.js` own their respective API calls. Reusable components are limited to table/filter controls, the KYC badge, the Pending status button/modal, and the KYC action button. The KYC page loads the application by ID, so the Pending action does not copy application data into the local loan store.

## Verification

`node scripts/number-applications.mjs` converts existing application numbers to decimal IDs and is safe to rerun when upgrading an existing database.

After changing the Prisma schema or running `db:push`, restart an already-running backend process. An older Node process keeps its previously loaded Prisma Client and may return HTTP 500 for the Pending list even while `/health` still returns HTTP 200.
