# Settlement & Closure Letter Reference Numbers

---

## PROD-settlement-reference-number — 2026-09-04

### Overview

Customer-facing settlement and closure letters now carry a **unique reference number** so an issued letter can be cited and traced. Three documents are covered:

| Document | View | Reference stored in |
|----------|------|---------------------|
| Settlement **proposal** letter | `admin/accounting/settlement_letter.blade.php` | `settlement_maker_checker.proposal_reference_no` |
| Final **settlement** letter | `admin/documents/settlement_letter.blade.php` | `consumer_application_files.reference_number` (`file_type = settlement_letter`) |
| **Closure** letter / no-due certificate | `admin/documents/closure_letter.blade.php` | `consumer_application_files.reference_number` (`file_type = closure_letter`) |

Before this change neither letter carried a reference, and neither was recorded anywhere — both were rendered in memory on every send or download.

---

### Business Rules

- **The reference on an issued letter never changes.** It is minted once on first generation and reused on every later re-send or re-download. This matters because the generated value embeds a timestamp.
- **The proposal reference is per settlement request.** Raising a new settlement request clears it; approving that request mints a new one. The final settlement letter reference, by contrast, is permanent once issued.
- **Settlement and closure letters are now recorded** in `consumer_application_files` on first generation. Previously no entry existed for either.

### Reference composition

All three references are built by concatenating, with **no separator**:

| Document | Composition |
|----------|-------------|
| Settlement **proposal** letter | merchant id + first & last alphabet of name + last 4 of PAN + unix timestamp |
| Final **settlement** letter, **closure** letter | first 4 digits of application id + merchant id + first & last alphabet of name + last 4 of PAN + unix timestamp |

The letter reference is therefore the proposal composition prefixed with the application id; both share the `referenceBody()` builder.

Worked examples:

| Application | Merchant | Name | PAN | Proposal | Settlement / Closure |
|---|---|---|---|---|---|
| 181787 | 26 | Rahul Sharma | ABCDE1234F | `26RA234F1791902978` | `181726RA234F1791902978` |
| 742 | 6478 | A. B. Khan | XYZPQ9876K | `6478AN876K1791902978` | `7426478AN876K1791902978` |
| 5001 | 88 | Li | AAAAA0001Z | `88LI001Z1791902978` | `500188LI001Z1791902978` |
| 99812345 | — | — | — | `1791902978` | `99811791902978` |

Notes on the component rules:
- Components that cannot be derived (missing PAN, nameless record, no merchant) are omitted rather than contributing an empty segment.
- An application id shorter than 4 digits contributes only the digits it has — no padding.
- "Last 4 of PAN" is the last 4 **characters** of the PAN (`ABCDE1234F` → `234F`).
- The name contributes the first and last alphabetic character of the full name, uppercased, ignoring spaces and punctuation (`A. B. Khan` → `AN`). A single-letter name repeats it.
- `pan_card` is decrypted by the `Encryptable` trait on attribute access.
- Because the value embeds a timestamp, the same application generates a different reference on each call — which is why every reference is minted once and stored.

---

### Logic Added / Changed

#### Reference generation and persistence
`SettlementController` (`app/Http/Controllers/Admin/Project/SettlementController.php`)
- `generateProposalReference($application_id, $application = null)` (new) — builds the proposal reference. Loads the application record itself unless the caller already holds one.
- `generateLetterReference($application)` (new) — builds the settlement/closure letter reference (proposal composition prefixed with the application id).
- `referenceBody($application)` (new, private) — the merchant id + name initials + PAN segment shared by both.
- `referenceApplication($application_id, $application = null)` (new, private) — resolves the application record a reference is built from.
- `resolveLetterReference($application_id, $file_type, $application = null)` (new) — returns the reference already issued for that letter type, otherwise builds a fresh one. Backfills an existing entry that somehow carries no reference, so the number stays stable.
- `hasLetter($application_id, $file_type)` (new) — lets callers skip rendering a PDF purely to record it.
- `persistLetter($application_id, $file_type, $reference, $pdf, $document = null)` (new) — uploads the PDF via `ConsumerDocument::uploadToS3()` and records the reference on the resulting `consumer_application_files` row. Idempotent: re-sends and re-downloads neither duplicate rows nor renumber the letter. Failures are logged to CloudWatch and swallowed, so a letter is never blocked from being delivered.

#### Reference reaching the views
`ConsumerDocument`
- New public property `$letter_reference`, threaded into the `$data` array in `uploadToS3()` as `reference_number` (same convention as `docRefId`).
- `getDocument()` injects `$process['letter_reference']` for types `Settlement Letters` and `Closures Letters`, which covers every render path for those documents in one place (including the `downloadDocument()` branch). The loaded application model is passed through, so no extra query is made.

`ConsumerApplicationController`
- `getUploadedFileNameMultiple()` threads `reference_number` from `$data` onto the `consumer_application_files` row, in both insert branches.
- `sendSettlementLetter()` — PDF construction moved ahead of the `$fetchPDF` branch so both the download and email paths record the letter; records on first issue.
- `sendClosureLetter()` — new private `recordClosureLetter()` helper records the closure letter on first issue.
- `downloadClosureLetter()` — records the closure letter on first issue.

`AccountingController`
- `triggerSettlementMakerChecker()` — clears `proposal_reference_no` on the upsert, so a new request cannot inherit the previous cycle's reference.
- `approveSettlementMakerChecker()` — mints and stores the proposal reference on approval.
- `sendSettlementProposalLetter()` — prints the stored reference; generates and stores one on the fly for records approved before this change.
- `emailSettlementLetter()` — records the settlement letter on first issue.

---

### Impact — Files Changed / Added

| File | Type | Change |
|------|------|--------|
| `database/migrations/2026_09_04_100000_add_proposal_reference_no_to_settlement_maker_checker_table.php` | Added | Nullable `proposal_reference_no` on `settlement_maker_checker` |
| `database/migrations/2026_09_04_100100_add_reference_number_to_consumer_application_files_table.php` | Added | Nullable `reference_number` on `consumer_application_files` |
| `app/Http/Controllers/Admin/Project/SettlementController.php` | Modified | Reference builder, resolver, existence check and persistence helpers |
| `app/Http/Controllers/Admin/ConsumerDocument.php` | Modified | `$letter_reference` property; threads `reference_number` through `uploadToS3()`; injects `letter_reference` for settlement/closure letter types |
| `app/Http/Controllers/Admin/ConsumerApplicationController.php` | Modified | `reference_number` threaded through `getUploadedFileNameMultiple()`; settlement and closure letters recorded on first issue |
| `app/Http/Controllers/Admin/AccountingController.php` | Modified | Proposal reference minted on approval and cleared on new request; printed on the proposal letter; settlement letter recorded on first issue |
| `app/SettlementMakerChecker.php` | Modified | `proposal_reference_no` added to `$fillable` |
| `app/ConsumerApplicationFiles.php` | Modified | `reference_number` added to `$fillable` |
| `resources/views/admin/accounting/settlement_letter.blade.php` | Modified | Prints `Reference No.` |
| `resources/views/admin/documents/settlement_letter.blade.php` | Modified | Prints `Reference No.` |
| `resources/views/admin/documents/closure_letter.blade.php` | Modified | Prints `Reference No.` |

### Deployment note

Both migrations must be run before the letters are generated:

```
php artisan migrate
```

Until they are, letters render `-` in place of the reference and nothing is recorded.
