# App Integration Guide — Account-wise Approval Flow

This document explains **what changed** from the old project-level approval model, and **how the Flutter (or any) mobile app should integrate** with the updated Mobile API (`/api/v1`).

Companion specs:

- Full endpoint reference: [`MOBILE_API.md`](MOBILE_API.md)
- Product requirement: [`new-project-flow.md`](new-project-flow.md)
- Admin setup (Phase 1): accounts / incharges / levels are configured in the web admin under **Projects → Accounts**

---

## 1. What changed (summary)

| Area | Before | After |
|------|--------|--------|
| Who raises a letter | Project incharge | **Account incharge** (per department under a project) |
| Approval chain source | `project_approval_levels` (1 user / level) | **Account** approval levels with **many users / level** |
| Create payload | `project_id` + vendor + invoice | Same + required **`account_id`** |
| Level completion | Single assigned user | **Any one** of the level’s assignees; peers become `SKIPPED` |
| Project detail API | Flat `incharges` + `approval_levels` | Nested **`accounts[]`** with incharges + levels + `approvers[]` |
| Inbox / notifications | One L1 user | **All** current-level assignees |

The rest of the lifecycle is unchanged: `DRAFT` → `UNDER_APPROVAL` → `APPROVED` / `REJECTED` / `AMENDMENT_REQUIRED` → accounting disbursement.

```text
Project
  └── Account (Diesel, Labour, Stationery, …)
        ├── Account Incharges  → create / submit letters
        └── Approval Levels
              ├── L1 → User A, User B   (any one)
              └── L2 → User C           (any one)
```

---

## 2. Mental model for the app

1. User logs in → JWT as before.
2. To **raise** a payment letter:
   - Load projects with `role=incharge`.
   - Open project detail → pick an account where `can_raise == true`.
   - Create request with `project_id` + `account_id` + vendor + invoice.
3. To **approve**:
   - Use `/approvals/inbox` (unchanged filter shape; now includes account fields).
   - Any assignee currently `PENDING` + `is_current` may act.
   - After one approve, other assignees at that level disappear from inbox (`SKIPPED`).

---

## 3. Breaking API changes (must update client)

### 3.1 Create / update payment request

**Required new field:** `account_id`

```http
POST /api/v1/payment-requests
Authorization: Bearer <token>
Content-Type: application/json

{
  "project_id": 2,
  "account_id": 1,
  "vendor_id": 2,
  "invoice_no": "INV-100",
  "invoice_date": "2026-08-01",
  "invoice_amount": 50000,
  "action": "draft"
}
```

| Error | Meaning |
|-------|---------|
| `422` `account_id` | Missing, or account does not belong to `project_id` |
| `403` | User is not an active incharge of that account |
| `409` | Account inactive / no approval levels / level has no approvers (on submit) |

### 3.2 Project detail shape

`GET /projects/{id}` **no longer** returns top-level `incharges` / `approval_levels`.

Use:

```json
data.accounts[].id
data.accounts[].account_code
data.accounts[].account_name
data.accounts[].can_raise          // true → show in create picker
data.accounts[].i_am_incharge
data.accounts[].i_am_approver
data.accounts[].incharges[]
data.accounts[].approval_levels[].approvers[]   // multiple users
```

List endpoint adds: `my_account_ids`, `account_count`, `my_levels` (may span accounts).

### 3.3 Approvals bundle

`GET /payment-requests/{id}` → `approvals[]` can have **multiple rows with the same `level_number`**.

UI tip: group by `level_number` and show each assignee’s status (`PENDING` / `APPROVED` / `SKIPPED` / …).

### 3.4 Approve behaviour

No request-body change. Behaviour change:

- Only **your** pending current row is approved.
- Peers at the same level → `SKIPPED` (they leave the inbox).
- Next level activates **all** of its assignees (each gets a push).

---

## 4. Recommended Flutter screens / flows

### 4.1 Create payment letter

```
Login
 → Projects (role=incharge)
 → Project detail
 → Account picker (filter accounts where can_raise == true)
 → Vendor + invoice form
 → POST /payment-requests { project_id, account_id, ... }
 → Upload attachments
 → POST .../submit
```

Suggested local model:

```dart
class AccountOption {
  final int id;
  final String code;
  final String name;
  final bool canRaise;
}
```

Store `accountId` on the draft screen; never create without it.

### 4.2 My requests list

`GET /payment-requests?scope=mine`  
Show `account_name` / `account_code` beside project.

Optional filter: `account_id=`.

### 4.3 Approver inbox

`GET /approvals/inbox`  
Rows include `account_name`, `my_level`.

Detail → approve / reject / return (multipart still supported for optional file).

If two users share L1 and one approves first, the other will get **403** on approve (no longer current) — refresh inbox after FCM or on resume.

### 4.4 Project / account labels

Always display:

`{project_name} · {account_name}`  
e.g. `Rajaji Nagar · Diesel`

---

## 5. Push notifications

Unchanged channel (FCM). Payload `data` may include:

| Key | When |
|-----|------|
| `level_number` | Approval pending / progressed |
| `account_id` | On new L1 assignment / forward (when present) |
| `payment_request_id` | Always for request-related pushes |

On `APPROVAL_PENDING`, open inbox or request detail.  
On `REQUEST_REJECTED` / `AMENDMENT_REQUIRED` / `REQUEST_APPROVED`, open the requester’s request detail.

---

## 6. Status & approval row cheat sheet

### Request status

Same as before: `DRAFT`, `UNDER_APPROVAL`, `APPROVED`, `REJECTED`, `AMENDMENT_REQUIRED`, `CANCELLED`, payment statuses…

### RA bill number (`ra_bill_number`)

| State | Value |
|-------|--------|
| Draft | `null` |
| After first submit | Next integer for that **project + vendor** (starts at 1) |
| Amendment re-submit | **Unchanged** |
| Cancelled / rejected | Number stays consumed (sequence may have gaps) |

Do not send `ra_bill_number` from the client — server assigns it.

### Assignee row status (`payment_request_approvals`)

| Status | Meaning for UI |
|--------|----------------|
| `PENDING` + `is_current=1` | Show in inbox; user may act |
| `APPROVED` | This user completed the level |
| `SKIPPED` | Peer completed the level; hide from inbox |
| `REJECTED` | This user rejected |
| `RETURNED_FOR_AMENDMENT` | This user returned for fix |

---

## 7. Migration checklist for the app team

1. [ ] Bump API client: add `account_id` to create/update DTOs.
2. [ ] Replace project incharge/level UI with **account picker** from `GET /projects/{id}`.
3. [ ] Filter raiseable accounts with `can_raise`.
4. [ ] Show account on list/detail/inbox cards.
5. [ ] Show `ra_bill_number` on submitted requests (`null` on drafts; set after first submit; unchanged on amendment resubmit).
6. [ ] Group `approvals[]` by `level_number` (multi-assignee).
7. [ ] Handle `SKIPPED` in timeline (label: “Completed by another approver”).
8. [ ] Refresh inbox after approve (or on FCM) so peer devices drop the item.
9. [ ] Regression: reject / return / amendment resubmit still work.
10. [ ] Do **not** call removed concepts (project-level incharges API fields).

---

## 8. Quick E2E (Postman / curl)

```bash
# 1) Login as account incharge
TOKEN=...

# 2) Projects I can raise for
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/v1/projects?role=incharge"

# 3) Accounts under project
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/v1/projects/2"

# 4) Create draft
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"project_id":2,"account_id":1,"vendor_id":2,"invoice_amount":1000,"action":"draft"}' \
  "$BASE/api/v1/payment-requests"

# 5) Submit
curl -X POST -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/v1/payment-requests/{id}/submit"

# 6) Login as L1 approver → inbox → approve
curl -H "Authorization: Bearer $APPROVER_TOKEN" \
  "$BASE/api/v1/approvals/inbox"
curl -X POST -H "Authorization: Bearer $APPROVER_TOKEN" -H "Content-Type: application/json" \
  -d '{"remarks":"OK"}' \
  "$BASE/api/v1/payment-requests/{id}/approve"
```

---

## 9. Admin prerequisites (before app testing)

In **Admin → Projects → View → Accounts**:

1. Create at least one active account (`DIESEL`, etc.).
2. Add ≥1 active **Account Incharge**.
3. Add ≥1 approval level with ≥1 approver.
4. Ensure the incharge user is **not** also an approver on the same account (server rejects that config).

Without levels/approvers, submit returns **409**.

---

## 10. Out of scope / unchanged

- Auth, profile, devices, vendors, notifications list/read, accounting disbursement APIs.
- Optional approval attachment on approve/reject/return (still supported).
- Unanimous multi-approver (all must approve) — **not** in v1; any-one only.
