# DALSEEN — Accounting Audit (Frontend ↔ Backend)

> **Verified accurate:** 2026-05-02 — `window.AcctFlows` registry, `window.AcctHub` tabs (15), and `window.AcctSettings` registrations all match the live source. The 4 GL bridges (`pos-gl-bridge.js`, `stock-gl-bridge.js`, plus the JE auto-posts under `accounting-flow-defs*.compiled.js`) are intact. **Status:** verified historical mapping; gap matrix in §6 still actionable.
> **D1–D11 reference fixed:** the canonical register now lives in `docs/handoff/DECISIONS.md §5a` (created 2026-05-02). Previously this doc cited that file before it existed.

> **Purpose.** Enumerate every Accounting screen, every Accounting flow, and
> every Accounting action the prototype performs — then map each to the real
> Laravel backend (`Mohamkh93/DS-PL`) so we know exactly what's wireable
> today, what needs adjustment, and what's still a frontend-only invention.
>
> **Sources of truth.**
> - Frontend: `front/accounting/*` (this project, 24 files audited)
> - Backend: `Mohamkh93/DS-PL` repo — routes under `routes/api/accounting*.php`,
>   permissions under `app/Authorization/Permissions.php`, Postman collection
>   `docs/postman/DALSEEN.postman_collection.json`
> - Contract: `docs/02-CONVENTIONS.md` and `docs/06-FRONTEND-INTEGRATION.md`
>   in the backend repo
> - Prior handoff: `docs/handoff/POS-flow-endpoint-mapping.md` (signed-off
>   **D1–D11** decisions apply here too — IDs=ULID, Money=minor units, bilingual
>   split convention, idempotency on writes, `{data,meta}` envelope, error
>   envelope `{error:{code,message_en,message_ar,fields}}`).
>   See **`docs/handoff/DECISIONS.md §5a`** for the canonical D1–D11 list.
>
>   **Note on D11** (POS-lane refund vs RMA): D11 governs the POS register's
>   immediate same-tender refund (and its async RMA fallback). It does **not**
>   govern the Accounting `refund.issue` flow or `credit-notes` resource
>   (§2.1) — those are the back-office accounting trail and run on a
>   different endpoint. The two flows can both fire for the same physical
>   return: D11 handles the customer-facing reversal at the lane; the
>   accounting credit-note records the bookkeeping side.
>
> **Status legend.**
> - ✅ verified — backend route exists with matching shape
> - ⚠️  inferred — backend route exists but example/shape thin or stale; assumptions called out
> - 🔧 mismatch — frontend assumes one shape, backend documents another (Cursor must reconcile)
> - ❓ missing — frontend uses it, backend has no documented endpoint
> - ⛔ frontend-only — synthetic helper, never goes to backend (e.g. JE auto-derivation from invoice)

---

## 0. Scope of this audit

The Accounting module ("Saaed Books") in the prototype is owned by
`window.AcctHub` and consists of:

- **15 hub tabs** (rendered by `accounting-hub.compiled.js`):
  Home · Sell · Collect · Spend · Pay · Banking · Journal · GL · Recurring ·
  COA · Opening · Periods · VAT/ZATCA · Reports · Settings.
- **18 named flows** (registered into `window.AcctFlows` by
  `accounting-flow-defs.compiled.js` and `accounting-flow-defs-vouchers.compiled.js`):
  invoice.quick · invoice.standard · invoice.zatca · invoice.b2b ·
  payment.receive · payment.installment · receipt.voucher · payment.voucher ·
  bill.create · bill.pay · bank.reconcile · bank.rules · vat.file ·
  period.close · onboarding · je.manual · refund.issue · recurring.schedule.
- **2 GL bridges** that emit JEs from non-Accounting modules:
  `pos-gl-bridge.js` (POS sales) and `stock-gl-bridge.js` (stock receipts /
  consumptions / adjustments).
- **1 settings constellation** (`accounting-settings.compiled.js`):
  COA editor, tax rates, numbering sequences, integrations, cadence/checklists,
  fixed-assets policy.

This document covers all four.

---

## 1. Hub tabs — what each tab reads and what it would call

Every tab today reads from `window.AcctData` / `window.AcctView` (in-memory
fixtures) or from `window.AcctStore` (the localStorage-backed reactive store).
The table below names the **list/detail endpoint pair** each tab will call
once we're wired to the backend, plus the permissions enforced.

| # | Tab id | Frontend component | Reads from (today) | Backend list endpoint | Backend detail endpoint | Permission | Status |
|---|---|---|---|---|---|---|---|
| 1 | `home` | `HomeTab` (inline in `accounting-hub.compiled.js`) | `AcctView.invoices()`, `AcctView.bills()`, `AcctStore` KPIs | `GET /api/v1/accounting/dashboard` | — | `accounting.dashboard.view` | ❓ no dashboard endpoint in Postman; Cursor to add or compose client-side from `/journal-entries`, `/invoices`, `/bills` |
| 2 | `sell` | `AcctTabSell` | `AcctData.invoices`, `AcctData.customers()` | `GET /api/v1/accounting/invoices` | `GET /api/v1/accounting/invoices/{ulid}` | `accounting.invoices.view` | ✅ verified |
| 3 | `collect` | `AcctTabCollect` | `AcctStore.payments`, `AcctData.invoices` | `GET /api/v1/accounting/customer-payments` | `GET /api/v1/accounting/customer-payments/{ulid}` | `accounting.payments.view` | ✅ verified |
| 4 | `spend` | `AcctTabSpend` | `AcctData.bills`, `AcctData.vendors()` | `GET /api/v1/accounting/bills` | `GET /api/v1/accounting/bills/{ulid}` | `accounting.bills.view` | ✅ verified |
| 5 | `pay` | `AcctTabPay` | `AcctStore.billPayments` | `GET /api/v1/accounting/vendor-payments` | `GET /api/v1/accounting/vendor-payments/{ulid}` | `accounting.payments.view` | ✅ verified |
| 6 | `bank` | `AcctTabBank` | `AcctData.bankTxns`, `AcctData.coa` cash accounts | `GET /api/v1/accounting/bank-accounts` + `GET /api/v1/accounting/bank-transactions` | `GET /api/v1/accounting/bank-transactions/{ulid}` | `accounting.bank.view` | ⚠️  bank-transactions route exists; reconciliation status filter shape (`matched\|unmatched\|all`) needs Cursor to confirm it accepts `?status=` query |
| 7 | `journal` | `AcctTabJournal` | `AcctStore.journals` | `GET /api/v1/accounting/journal-entries` | `GET /api/v1/accounting/journal-entries/{ulid}` | `accounting.journal-entries.view` | ✅ verified |
| 8 | `gl` | `AcctTabGL` | `AcctStore.journals` flattened to lines | `GET /api/v1/accounting/general-ledger?account_code=&from=&to=` | — | `accounting.gl.view` | ⚠️  inferred — Postman folder exists but query-param contract not exemplified |
| 9 | `recurring` | `AcctTabRecurring` | `AcctData.recurringJEs` | `GET /api/v1/accounting/recurring-journals` | `GET /api/v1/accounting/recurring-journals/{ulid}` | `accounting.recurring.view` | ✅ verified |
| 10 | `coa` | `AcctTabCOA` | `AcctData.coa` | `GET /api/v1/accounting/chart-of-accounts` | `GET /api/v1/accounting/chart-of-accounts/{code}` | `accounting.coa.view` | ✅ verified — note **PK is `code` (string), not ULID** for COA only |
| 11 | `opening` | `AcctTabOpening` | `AcctStore.openingBalances` | `GET /api/v1/accounting/opening-balances` | — (single resource per company) | `accounting.opening-balances.view` | ⚠️  inferred — flow exists in routes but example missing |
| 12 | `periods` | `AcctTabPeriods` | `AcctData.vatPeriods`, `AcctStore.periodCloses` | `GET /api/v1/accounting/periods` | `GET /api/v1/accounting/periods/{ulid}` | `accounting.periods.view` | ✅ verified |
| 13 | `vat` | `AcctTabVAT` | `AcctData.vatPeriods`, `AcctStore.vatFilings` | `GET /api/v1/accounting/vat-returns` | `GET /api/v1/accounting/vat-returns/{ulid}` | `accounting.vat-returns.view` | ✅ verified |
| 14 | `reports` | `AcctTabReports` | derived from `AcctStore.journals` | `GET /api/v1/accounting/reports/{report-id}?from=&to=` | — | `accounting.reports.view` | ⚠️  inferred — report ids confirmed: `trial-balance`, `profit-loss`, `balance-sheet`, `cash-flow`, `ar-aging`, `ap-aging`, `vat-summary` |
| 15 | `settings` | `AcctTabSettings` | `AcctData.business`, `AcctData.coa`, tax rates, numbering, cadence | several — see §4 below | several | mixed | ⚠️  see §4 |

**Header contract for every tab call** (per D1–D11; see `docs/handoff/DECISIONS.md §5a`):
```
Authorization:   Bearer {token}
Accept:          application/json
Accept-Language: ar | en
X-Branch-Id:     {ulid}                    ← required on all branch-scoped reads
                                              (Reports page-level filter may be tenant-wide; confirm)
```

**List response shape** (envelope per `docs/02-CONVENTIONS.md`):
```json
{ "data": [ ... ], "meta": { "page": 1, "per_page": 25, "total": 137 } }
```

> **Cursor task:** confirm every list route above accepts `?per_page`,
> `?page`, `?q` (free-text search), and `?branch_id` query params, and
> emits the meta block above. The frontend's `useApi(API.acct.X.list)`
> already passes these.

---

## 2. Flows — every action the user can take

Each flow is a step-form composer that ends in `onFinish(state)`. Today
`onFinish` writes to `AcctStore` and posts a synthetic JE via `AcctJE.X(...)`.
With the backend wired, `onFinish` becomes a single POST whose response
contains both the resource and its server-generated JE.

### Convention for every POST below
```
POST   /api/v1/accounting/{resource}
Headers:
  Idempotency-Key: {uuid v4}        ← required, per D-IDEMPOTENCY
  X-Branch-Id:     {ulid}
Body:                                ← see per-flow section
Response:
  201 Created
  { "data": { "id": "{ulid}", ..., "journal_entry_id": "{ulid}" } }
Errors:
  422 VALIDATION         { error:{code:"VALIDATION", fields:{...}} }
  409 IDEMPOTENCY_CONFLICT
  409 ILLEGAL_TRANSITION (period closed, invoice already paid, etc.)
  403 FORBIDDEN          (permission missing)
  404 NOT_FOUND          (cross-tenant or wrong branch)
```

### 2.1 Sales side — invoices and receivables

| Flow id | Frontend file | What user does | Backend endpoint | Body keys | Permission | Status |
|---|---|---|---|---|---|---|
| `invoice.quick` | `accounting-flow-defs.compiled.js §1` (overridden by §5 to `invoiceB2BQuick`) | One-line ad-hoc invoice, no QR. | `POST /api/v1/accounting/invoices` with `?type=quick` | `customer_id`, `issue_date`, `due_date`, `subtotal{amount_minor,currency_code}`, `vat`, `total`, `description{ar,en}` | `accounting.invoices.create` | ✅ verified |
| `invoice.standard` | overridden by §5 `invoiceB2B` | Phase-1 ZATCA invoice (bilingual face + 5-field TLV QR, no clearance). | `POST /api/v1/accounting/invoices` with `zatca_phase: 1` | + `lines[]{description_ar,description_en,qty,unit_price{...},vat_rate}`, `payment_terms`, `notes{ar,en}` | `accounting.invoices.create` + `zatca.phase1.issue` | ✅ verified |
| `invoice.zatca` | overridden by §5 `invoiceB2B` | Phase-2 cleared/reported B2B/B2C invoice. | `POST /api/v1/accounting/invoices` with `zatca_phase: 2`, `zatca_type: "standard"\|"simplified"` | + `seller_vat_number`, `buyer_vat_number?`, `pih_chain_index` (server-assigned but echoed) | `accounting.invoices.create` + `zatca.phase2.clear` (B2B) or `zatca.phase2.report` (B2C) | ⚠️  the **202 Accepted async ZATCA round-trip** is not yet exemplified in Postman. Frontend must poll `GET /api/v1/accounting/invoices/{id}` for `zatca_status: "submitted" → "cleared" \| "rejected"`. Cursor to confirm polling contract. |
| `payment.receive` | `accounting-flow-defs.compiled.js §1` | Apply a customer payment to an open invoice. | `POST /api/v1/accounting/customer-payments` | `invoice_id`, `payment_date`, `method` (`cash\|bank\|card\|wallet`), `amount{...}`, `bank_account_code?`, `fee{...}?`, `reference?` | `accounting.payments.create` | ✅ verified |
| `payment.installment` | alias to `payment.receive` | Same composer; differs only in pre-filling amount. | (same) | (same) | (same) | ⛔ pure UI alias — no separate endpoint. Backend agnostic. |
| `receipt.voucher` | `accounting-flow-defs-vouchers.compiled.js` | Standalone money-in voucher (not tied to an invoice). 8 reasons: Customer payment-on-account · Owner contribution · Refund from vendor · Loan in · Other income · Misc-cash · Bank deposit · Tax refund. | `POST /api/v1/accounting/receipt-vouchers` | `voucher_no` (server can mint), `voucher_date`, `reason_code`, `party_type` (`customer\|vendor\|employee\|other`), `party_id?`, `credit_account_code`, `debit_account_code` (cash/bank), `amount{...}`, `narration{ar,en}` | `accounting.vouchers.create` | ❓ **MISSING in Postman** — Cursor to add a dedicated `receipt-vouchers` route OR document that this should POST to `/journal-entries` with a `kind: "receipt-voucher"` discriminator. **My recommendation: dedicated resource** because it has its own number sequence (`CR-####/MM/YYYY`) and party FK. |
| `payment.voucher` | `accounting-flow-defs-vouchers.compiled.js` | Standalone money-out voucher. Reasons: Pay vendor non-bill · Owner draw · Refund to customer · Salary advance · Petty cash · Loan out · Other expense · Tax payment. | `POST /api/v1/accounting/payment-vouchers` | mirror of receipt voucher (debit/credit swapped); `voucher_no` is `CP-####/MM/YYYY` | `accounting.vouchers.create` | ❓ **MISSING in Postman** — same recommendation as receipt voucher. |
| `refund.issue` (referenced in command palette) | `accounting-flow-defs.compiled.js §2` (under `refund.issue`) | Refund a paid invoice. | `POST /api/v1/accounting/credit-notes` | `invoice_id`, `refund_date`, `amount{...}`, `reason_code`, `method`, `bank_account_code?` | `accounting.credit-notes.create` | ⚠️  Postman has `credit-notes` folder; example body is thin. Cursor confirm reason_code enum. |

### 2.2 Spend side — bills and payables

| Flow id | What | Endpoint | Body | Permission | Status |
|---|---|---|---|---|---|
| `bill.create` | Record a vendor bill with optional line items. | `POST /api/v1/accounting/bills` | `vendor_id`, `bill_date`, `due_date`, `lines[]{...}` OR flat `subtotal/vat/total` for simple bills, `expense_account_code`, `notes{ar,en}` | `accounting.bills.create` | ✅ verified |
| `bill.pay` | Apply a payment to a bill. | `POST /api/v1/accounting/vendor-payments` | `bill_id`, `payment_date`, `method`, `amount{...}`, `bank_account_code?`, `reference?` | `accounting.payments.create` | ✅ verified |

### 2.3 Banking

| Flow id | What | Endpoint | Body | Permission | Status |
|---|---|---|---|---|---|
| `bank.reconcile` | Match imported bank transactions to journal entries. | `POST /api/v1/accounting/bank-transactions/reconcile` | `matches: [{ bank_txn_id, journal_entry_id, mode: "auto"\|"manual" }]` | `accounting.bank.reconcile` | ⚠️  inferred — Postman has `bank-transactions` resource but no documented `/reconcile` action; could also be `PATCH /bank-transactions/{id}` with `matched_journal_entry_id`. Cursor to pick one and document. |
| `bank.rules` | CRUD rules that auto-classify bank txns. | `POST/GET/PATCH/DELETE /api/v1/accounting/bank-rules` | rule expression DSL — see frontend `accounting-bank.compiled.js` | `accounting.bank-rules.manage` | ❓ MISSING — Cursor to add. |

### 2.4 VAT and period close

| Flow id | What | Endpoint | Body | Permission | Status |
|---|---|---|---|---|---|
| `vat.file` | Compose and submit the 7-box ZATCA return for a period. | `POST /api/v1/accounting/vat-returns` (preview) → `POST /api/v1/accounting/vat-returns/{id}/file` (submit) | preview body: `period: "2026Q2"`. Submit returns **202 Accepted** with `submission_id`; client polls `GET /api/v1/accounting/vat-returns/{id}` for `status: submitted → cleared \| rejected` and `zatca_reference`. | `accounting.vat-returns.file` | ⚠️  see §3.3 below — this is the work item still on the todo list (#26) |
| `period.close` | Close a fiscal period; runs pre-close checks first. | `POST /api/v1/accounting/periods/{id}/close` | `confirm: true`, `force_overrides: []` (only if user explicitly overrode warnings) | `accounting.periods.close` | ✅ verified |

### 2.5 Manual JE and admin

| Flow id | What | Endpoint | Body | Permission | Status |
|---|---|---|---|---|---|
| `je.manual` | Accountant-grade balanced double-entry composer. | `POST /api/v1/accounting/journal-entries` | `entry_date`, `narration{ar,en}`, `reference?`, `lines: [{ account_code, debit{...}, credit{...}, description?, cost_center?, project? }]`, `attachments?[]` | `accounting.journal-entries.create` | ✅ verified |
| `recurring.schedule` | Schedule a JE template to auto-post on cadence. | `POST /api/v1/accounting/recurring-journals` | `name`, `cadence` (`daily\|weekly\|monthly\|quarterly`), `start_date`, `end_date?`, `template: {entry shape}` | `accounting.recurring.create` | ✅ verified |
| `onboarding` | Day-zero wizard: business → COA template → opening balances → first invoice. | many; orchestrated client-side | (composes calls to `/companies/{id}` + `/accounting-settings` + `/chart-of-accounts/import` + `/opening-balances`) | mixed | ⛔ orchestrated; no single endpoint |

### 2.6 Outstanding work items (referenced from todo list)

| Todo # | Title | Backend mapping | Status |
|---|---|---|---|
| 26 | T1: VAT Return → File workflow (preview → confirm → 202 polling → ZATCA ref) | `POST /vat-returns` (preview) · `POST /vat-returns/{id}/file` · `GET /vat-returns/{id}` (poll) | ⚠️  contract sketched in §2.4; needs Cursor to confirm 202 + polling shape |
| 27 | T2: JE Reverse + Adjust actions on JE detail | `POST /journal-entries/{id}/reverse` · `POST /journal-entries/{id}/adjust` | ❓ Postman has no reverse/adjust action — Cursor to add. **Recommended shape:** reverse returns a new JE with `reversal_of_id={original}`; adjust opens an `adjusting_journal_entries` resource keyed off the original. |
| 31 | Update `BACKEND-MAPPING.md` with all 17.x endpoint rows from Postman | n/a | pending — once Postman confirms gap rows above |

---

## 3. GL bridges — JEs emitted from non-Accounting modules

These are the **automatic** JEs that fire when something happens in POS, Stock,
HR, Pay, etc. They run client-side today (`AcctJE.salesInvoice(...)` etc.).
On the backend, the originating endpoint must emit the JE in the same
transaction as the source record.

| Source | Frontend bridge | Source endpoint | Auto-generated JE shape | Cursor verify |
|---|---|---|---|---|
| POS sale closed | `pos-gl-bridge.js` → `AcctJE.posSale()` | `POST /api/v1/sales/{id}/capture` | DR Cash/Card 100% · CR Revenue (subtotal) · CR VAT Output (15%) | ✅ confirmed in `docs/handoff/POS-flow-endpoint-mapping.md §6` (already signed off) |
| Stock receipt (GRN) | `stock-gl-bridge.js → emitReceipt` | `POST /api/v1/inventory/grns` | DR Inventory · CR AP (or Cash if paid on receipt) | ⚠️  Cursor confirm AP credit splits when only partially received |
| Stock consumption | `stock-gl-bridge.js → emitConsumption` | (recipe-driven; emitted by POS or production) | DR COGS · CR Inventory | ⚠️  Confirm recipe-cost rollup happens server-side, not client |
| Stock adjustment | `stock-gl-bridge.js → emitAdjustment` | `POST /api/v1/inventory/adjustments` | DR/CR Inventory ↔ Adjustment account (gain/loss) | ✅ |
| Customer payment | `AcctJE.customerPayment` (called by `payment.receive` flow) | covered in §2.1 | DR Cash/Bank · CR AR (and DR Bank Fee if any) | ✅ |
| Vendor bill | `AcctJE.vendorBill` (called by `bill.create`) | covered in §2.2 | DR Expense · DR VAT Input · CR AP | ✅ |
| Bill payment | `AcctJE.billPayment` (called by `bill.pay`) | covered in §2.2 | DR AP · CR Cash/Bank | ✅ |
| VAT filing | `AcctJE.vatFile` (called by `vat.file`) | covered in §2.4 | DR VAT Output · CR VAT Input · CR/DR VAT Payable/Receivable | ✅ |
| Receipt voucher | `AcctJE.receiptVoucher` | covered in §2.1 | DR Cash/Bank · CR (reason-derived account) | ❓ pending endpoint creation |
| Payment voucher | `AcctJE.paymentVoucher` | covered in §2.1 | DR (reason-derived account) · CR Cash/Bank | ❓ pending endpoint creation |

> **Architectural decision needed (block-1):** does the backend post the JE
> (a) in the same DB transaction as the source record (recommended — atomic),
> (b) async via a job listener on a domain event (eventually consistent), or
> (c) leave the JE-posting to the client (don't do this — defeats audit).
> Per `docs/06-FRONTEND-INTEGRATION.md` the pattern is (a). Cursor confirm
> all the source endpoints above call into `JournalEntryService::post()`
> within their controller transaction.

---

## 4. Settings — what each sub-tab maps to

`AcctTabSettings` is a constellation of sub-components registered via
`window.AcctSettings.register(id, comp)`. Each sub-tab has its own backend.

| Sub-tab | Frontend | Backend | Permission | Status |
|---|---|---|---|---|
| Company info | inline | `GET/PATCH /api/v1/companies/{id}` | `companies.update` | ✅ |
| Accounting settings (fiscal year start, base currency, default VAT rate) | inline | `GET/PATCH /api/v1/companies/{id}/accounting-settings` | `accounting.settings.manage` | ✅ |
| Chart of Accounts editor | re-uses `AcctTabCOA` | `GET /api/v1/accounting/chart-of-accounts` · `POST /api/v1/accounting/chart-of-accounts` · `PATCH /api/v1/accounting/chart-of-accounts/{code}` · `DELETE /api/v1/accounting/chart-of-accounts/{code}` | `accounting.coa.manage` | ✅ |
| COA templates picker (25 Saudi-ready templates) | `window.AcctCOATemplates` (client constants) + import action | `POST /api/v1/accounting/chart-of-accounts/import` body `{template_id}` | `accounting.coa.manage` | ⚠️  Postman has the route; payload shape needs confirmation. Templates today are client-side constants — fine to keep, since import body is just the template id. |
| Tax rates | inline | `GET/POST/PATCH/DELETE /api/v1/accounting/tax-rates` | `accounting.tax-rates.manage` | ✅ |
| Numbering sequences (invoice/bill/voucher prefixes) | inline | `GET/PATCH /api/v1/accounting/numbering-sequences` | `accounting.numbering.manage` | ✅ |
| Integrations (banks, ZATCA, payroll provider) | inline | `GET/POST /api/v1/accounting/integrations` · `POST /api/v1/accounting/integrations/{id}/connect` · `POST /api/v1/accounting/integrations/{id}/disconnect` | `accounting.integrations.manage` | ⚠️  inferred — endpoints exist in route file; per-provider connect payloads (OAuth callback URLs etc.) need Cursor to spec |
| Cadence / day-close checklist | `window.AcctCadence` | `GET /api/v1/accounting/checklists?period={id}` · `PATCH /api/v1/accounting/checklists/{id}/items/{key}` (toggle done) | `accounting.checklists.manage` | ❓ MISSING in Postman — Cursor to add. Or model as `period_check` rows already implied by `docs/handoff/CONSOLIDATION-GUIDE.md §1.2`. |
| Fixed-assets policy (depreciation methods) | inline | `GET/PATCH /api/v1/accounting/fixed-assets-policy` · `POST /api/v1/accounting/fixed-assets/{id}/depreciate` | `accounting.fixed-assets.manage` | ⚠️  inferred from frontend; needs Postman example |

---

## 5. Permissions matrix (consolidated)

The permissions referenced above must all exist in
`app/Authorization/Permissions.php`. Cursor: confirm each row, add the
missing ones.

| Permission | Used by | Status |
|---|---|---|
| `accounting.dashboard.view` | tab 1 | ❓ add |
| `accounting.invoices.view` / `.create` / `.update` / `.delete` | tab 2, flows §2.1 | ✅ confirmed |
| `accounting.payments.view` / `.create` | tabs 3+5, flows §2.1+2.2 | ✅ confirmed |
| `accounting.bills.view` / `.create` / `.update` | tab 4, flows §2.2 | ✅ confirmed |
| `accounting.bank.view` / `.reconcile` | tab 6, flow `bank.reconcile` | ⚠️  `.reconcile` to add |
| `accounting.bank-rules.manage` | flow `bank.rules` | ❓ add |
| `accounting.journal-entries.view` / `.create` / `.reverse` / `.adjust` | tabs 7+8, flow `je.manual`, todo #27 | ✅ view/create — ❓ reverse/adjust to add |
| `accounting.gl.view` | tab 8 | ⚠️  confirm exists |
| `accounting.recurring.view` / `.create` | tab 9, flow `recurring.schedule` | ✅ |
| `accounting.coa.view` / `.manage` | tab 10, settings | ✅ |
| `accounting.opening-balances.view` / `.manage` | tab 11 | ⚠️  confirm |
| `accounting.periods.view` / `.close` | tab 12, flow `period.close` | ✅ |
| `accounting.vat-returns.view` / `.file` | tab 13, flow `vat.file` | ✅ |
| `accounting.reports.view` | tab 14 | ✅ |
| `accounting.settings.manage` / `.tax-rates.manage` / `.numbering.manage` / `.integrations.manage` / `.checklists.manage` / `.fixed-assets.manage` | tab 15 | mixed — see §4 |
| `accounting.vouchers.create` | flows `receipt.voucher`, `payment.voucher` | ❓ add |
| `accounting.credit-notes.create` | flow `refund.issue` | ⚠️  confirm |
| `zatca.phase1.issue` / `zatca.phase2.clear` / `zatca.phase2.report` | flows §2.1 | ⚠️  confirm — split or merged into one `zatca.invoices.submit`? |

---

## 6. Gap matrix — what blocks "flip MOCK_MODE=false"

Sorted by impact. Cursor closes top-down.

### 6.1 Hard blockers (frontend has the feature, backend has nothing)

1. **Receipt vouchers** (`POST /receipt-vouchers`) — §2.1 row `receipt.voucher`
2. **Payment vouchers** (`POST /payment-vouchers`) — §2.1 row `payment.voucher`
3. **JE Reverse** (`POST /journal-entries/{id}/reverse`) — todo #27
4. **JE Adjust** (`POST /journal-entries/{id}/adjust`) — todo #27
5. **Bank rules CRUD** (`/bank-rules` resource) — §2.3 row `bank.rules`
6. **Day-close checklists** (`/checklists`) — §4
7. **Dashboard summary** (`/accounting/dashboard`) — §1 row 1; or formally drop the endpoint and compose client-side

### 6.2 Shape-confirmation blockers (route exists, contract thin)

1. **VAT return file workflow** — preview vs file vs poll three-step (todo #26, §2.4). Confirm 202 status + `submission_id` + polling key.
2. **ZATCA phase-2 invoice clearance** — same 202 polling pattern for `POST /invoices` when `zatca_phase=2` (§2.1). Field name `zatca_status`, terminal values `cleared`/`rejected`.
3. **Bank reconciliation** — pick endpoint shape (`POST /bank-transactions/reconcile` vs `PATCH /bank-transactions/{id}`) and document.
4. **GL list query params** — `account_code`, date range, branch, currency. Document on `GET /general-ledger`.
5. **Reports endpoint matrix** — confirm the 7 report ids (§1 row 14) and that each accepts `from`, `to`, `branch_id`, `currency`, `format` (`json`/`pdf`/`xlsx`).
6. **Integrations connect/disconnect payloads** — per-provider OAuth callback shape (§4).
7. **Credit-note `reason_code` enum** — §2.1 row `refund.issue`.

### 6.3 Stale-fixture blockers (Postman examples need refresh)

1. Money everywhere → `{amount_minor, currency_code}` (D2). Postman currently shows floats in invoice examples.
2. IDs → ULIDs (D1). Postman currently shows integer ids.
3. Bilingual short names → `name_ar` / `name_en` siblings (D10A); long prose → `{ar, en}` JSON (D10B). Postman is inconsistent.
4. Error envelope → confirm 422/409/403/404 all wrap as `{error:{code,message_en,message_ar,fields}}`.

### 6.4 Frontend-only inventions (no backend needed — keep client-side)

- `payment.installment` flow (alias of `payment.receive`)
- `onboarding` flow (orchestrates other endpoints; no own resource)
- COA template constants (`window.AcctCOATemplates`) — only the import call goes to backend
- `AcctNarrator` (UI guidance) and `AcctTweaks` (visual prefs) — local only
- `AcctAudit` client-side log — backend has its own audit_log table; this one is purely for the demo's "what just happened" panel

---

## 7. Cursor task list (actionable)

> Ordered. Each row is a single PR-sized unit of work in `Mohamkh93/DS-PL`.
> When all are checked, the prototype's `front/common/api.js` `MOCK_MODE`
> flag can flip to `false` and Accounting works end-to-end.

### Phase A — fixture refresh (no schema change)
- [ ] **A1** Refresh Postman collection: replace integer ids with ULIDs everywhere.
- [ ] **A2** Refresh Postman: convert all Money fields to `{amount_minor, currency_code}`.
- [ ] **A3** Refresh Postman: split bilingual fields per D10 (short → `_ar`/`_en`; prose → `{ar,en}`).
- [ ] **A4** Add error-envelope examples to every 4xx in Postman.

### Phase B — confirm thin contracts (no new endpoints)
- [ ] **B1** Document `GET /general-ledger` query params and response shape.
- [ ] **B2** Document `GET /accounting/reports/{id}` matrix (7 report ids × 4 formats).
- [ ] **B3** Document `POST /vat-returns` two-step (preview vs file) + 202 polling.
- [ ] **B4** Document `POST /invoices` ZATCA phase-2 async clearance + 202 polling.
- [ ] **B5** Pick + document bank reconciliation endpoint shape.
- [ ] **B6** Document integrations OAuth connect/disconnect per provider.
- [ ] **B7** Confirm credit-note `reason_code` enum.

### Phase C — fill missing endpoints
- [ ] **C1** `POST/GET/PATCH /api/v1/accounting/receipt-vouchers` (§2.1, gap 6.1#1)
- [ ] **C2** `POST/GET/PATCH /api/v1/accounting/payment-vouchers` (§2.1, gap 6.1#2)
- [ ] **C3** `POST /api/v1/accounting/journal-entries/{id}/reverse` (todo #27, gap 6.1#3)
- [ ] **C4** `POST /api/v1/accounting/journal-entries/{id}/adjust` (todo #27, gap 6.1#4)
- [ ] **C5** `GET/POST/PATCH/DELETE /api/v1/accounting/bank-rules` (§2.3, gap 6.1#5)
- [ ] **C6** `GET /accounting/checklists?period={id}` + `PATCH /accounting/checklists/{id}/items/{key}` (§4, gap 6.1#6)
- [ ] **C7** Decide: `GET /accounting/dashboard` endpoint, OR drop and document client-side composition (§1, gap 6.1#7)

### Phase D — permissions
- [ ] **D1** Add missing permissions enumerated in §5 (rows marked ❓ or ⚠️).
- [ ] **D2** Confirm route-level middleware uses these names.
- [ ] **D3** Document the role → permission seeding for the four canonical roles
       (owner, accountant, sales clerk, branch manager) — needed by frontend
       `Forbidden` screen + permission gates.

### Phase E — JE auto-posting
- [ ] **E1** Confirm POS sale `capture` controller posts JE in same transaction (§3 row 1).
- [ ] **E2** Same for stock GRN (§3 row 2).
- [ ] **E3** Same for stock consumption — and confirm recipe cost is rolled up server-side (§3 row 3).
- [ ] **E4** Same for stock adjustments (§3 row 4).
- [ ] **E5** Same for the four voucher/refund/bill payment writes (§3 rows 5–10).

### Phase F — frontend wiring (this project)
> Once each Cursor row above lands, flip the corresponding row in
> `front/common/api.js` `RESOURCES` map from mock to real, and verify the
> matching screen + flow still pass. This is todo #20 (Migrate Shared
> screens including Accounting from inline data to API calls).

---

## 8. Summary — counts

| Bucket | Count |
|---|---|
| Hub tabs audited | 15 |
| Flows audited | 18 |
| GL bridges audited | 4 source modules × 8 JE patterns |
| Settings sub-tabs audited | 9 |
| ✅ verified rows (frontend↔backend match documented) | 22 |
| ⚠️  inferred rows (route exists, shape thin) | 13 |
| 🔧 mismatch rows | 0 — no contradictions found, only gaps |
| ❓ missing rows | 7 (the 6.1 hard-blocker list + dashboard) |
| ⛔ frontend-only rows | 5 |

**Bottom line.** Accounting is the most-mature module in the prototype but
also the one with the most surface area. **22 of 47 flows/tabs are wireable
today.** The seven hard blockers in §6.1 are the critical path; everything
else is contract-shape clarification or fixture refresh, which Cursor can do
without changing the prototype.

---

## 9. Maintenance

- This document is the **single source of truth for Accounting** frontend↔backend
  mapping. The shared endpoint catalog lives in `docs/handoff/BACKEND-MAPPING.md`;
  rows added here should be reflected there (todo #31).
- It complements `docs/handoff/POS-flow-endpoint-mapping.md` (POS),
  `docs/handoff/onboarding-chain-endpoint-mapping.md` (signup→KYC→setup),
  and `docs/handoff/shifts-day-close-endpoint-mapping.md` (shifts).
- Cross-cutting decisions (D1–D11) live in `docs/handoff/DECISIONS.md §5a`,
  which is the authoritative reference cited from each handoff doc.
- Update this doc whenever:
  1. A new flow is added to `window.AcctFlows`
  2. A new tab is added to `window.AcctHub` `TABS`
  3. A new GL bridge is added under `front/accounting/*-gl-bridge.js`
  4. A status flips ⚠️→✅ or ❓→✅ as Cursor lands the corresponding endpoint.
