# LEDGER — Permissions Hardening (كل أبشن تحت السيطرة)

**Feature:** Permissions hardening — action-level UI gating across back-office + close ungated LIS endpoints + clearer role labels.
**Date started:** 2026-07-12 · **Branch:** `hazemdev2` (both repos) · **Install:** moonui2 (`/home/moonui2`, DB `moonui2_dev_be`, chown `moonui2:moonui2`).
**Approved plan:** `knowledge-base/plans/permissions-master-plan.html` (+ `permissions-review-analysis.html`, `lab-permissions-review-analysis.html`).

## Owner acceptance test
The owner will verify by: logging in as a **limited role** (NOT super-admin/admin) and confirming that **action buttons they weren't granted are hidden** (e.g. a "view invoices" user sees no New/Edit/Delete/Post/Cancel on sales & purchases screens). AND: in `/core/roles` the owner can **find each distinct action as its own grantable permission with a clear Arabic label** — explicitly **«اعتماد أمر الشراء» = `purchases.orders.approve`** — grant it to a role, and then only users with it see the **Approve** button and can approve.

## Decisions (from approved analysis — do NOT re-litigate)
1. Back-office grant model = existing `/core/roles` + FE button hiding [chosen]; extending the LAB "by-screen"/presets to back-office is deferred P2 (owner may request later).
2. Route guards stay coarse (prefix) for now — button-level hiding is the mechanism. Route tightening deferred.
3. The over-configured DB role is **data** — fixed from the LIS role editor by the owner, not code. Not a WP.
4. Every back-office action permission already EXISTS in the catalog (Sales 51 / Purchases 51, all actions incl. `.approve/.post/.cancel/.delete`). So WPs add FE gating keyed to existing strings — no new back-office permissions needed. New permissions ARE needed only for the 5 ungated LIS controllers (WP5).
5. `*appCan` uses EXACT `can()`; trailing-dot prefix form is broken → WP1 fixes the directive (add a prefix-capable check) and all gating uses exact action strings.

## WP table (execution order)
| WP | Scope (one dense line) | Repo | Depends | Review | Migr? | Status | Commits |
|----|------------------------|------|---------|--------|-------|--------|---------|
| WP1 | Fix `can.directive` (support prefix form safely) + gate **Purchases** action buttons with `*appCan` (requests/orders/bills/grns/payments/returns/supplier-prices — incl. `purchases.orders.approve` on the Approve button) | FE | — | Codex | — | ✅ done | FE 37367e6 (build green, all *appCan verified vs catalog) |
| WP2 | Gate **Sales** action buttons (invoices/orders/quotations/delivery-notes/payments/returns/commissions — incl. `.post/.approve/.cancel/.delete`) | FE | WP1 | Codex | — | ✅ done | FE 45d2e7034 (build green, strings verified) |
| WP3 | Gate **Inventory + Accounting** action buttons (stock-*, warehouses, adjustments, transfers, receipts, issues; journal-entries/accounts/vouchers/expenses/revenues/banking) | FE | WP2 | Codex | — | ✅ done | FE c8a52c192 (44 files, build green, strings verified) |
| WP4 | Gate **HR + POS + Core-admin** action buttons (employees/payroll/leave/attendance; pos terminals/sessions; users/roles/branches/products/partners/warehouses) | FE | WP3 | Codex | — | ✅ done | FE 7d2340c83 (81 files, 140 gates, build green) |
| WP5 | BE: close 5 ungated LIS controllers — add `lis.*` perms to catalog + `permission:` middleware (LabNewTypes histopath/antibiotics, HistopathTemplate, DeviceModel, PackagePartner, ExternalLabPackage) + seed on dev | BE | — | Codex + Fable (permission catalog / clinical-write security) | seed | ✅ done | BE 1577d8d (12 new + 18 baseline green; Fable unavailable → focused review + test-proof) |
| WP6 | FE: complete `ROLES.RESOURCES.*` / `ROLES.ACTIONS.*` i18n so every permission (incl. `purchases.orders.approve` → «اعتماد أمر الشراء») reads with a clear Arabic label in `/core/roles` | FE | WP4 | Codex | — | ✅ done | FE 86346d453 (JSON valid, 100 keys/lang, build green) |
| WP7 | BE: delete dead `Modules/Core/database/seeders/PermissionSeeder.php` (obsolete singular naming + rogue roles; referenced nowhere) | BE | — | Codex | — | ✅ done | BE d75d67899 (verified zero references) | |
| WP8a | BE: add missing back-office action perms + enforce + no-regression grants + deps + seed (Purchases cancel/close/duplicate/reject/convert; Inventory .update/.delete; Accounting .delete/confirm/lock; Core new currencies/tax-rates/categories/partner-freeze) | BE | — | Codex+Fable | seed | ✅ done | BE abf817eb3 (43 tests green, no-regression bridge, seeded on dev) |
| WP8b | FE: gate now-ungated buttons + i18n labels | FE | WP8a | Codex | — | ✅ done | FE d2448c0eb (14 files, build green, JSON valid) |

## DAG / serialization
- FE WPs (WP1→WP2→WP3→WP4→WP6) are **serialized** — one writer per FE repo at a time.
- BE WPs (WP5, WP7) are a separate repo → may run alongside FE, but serialize WP5 vs WP7 (both BE).
- WP6 depends on WP4 (labels after gating) but is independent of BE.

## Baseline (Phase A)
- **FE:** `ng build --base-href /app/` = GREEN (2026-07-12 17:08). Gate for every FE WP = build stays green.
- **BE:** to capture before WP5 — LIS + Core permission suites (scoped). Recorded at WP5 start.

## Deferrals / open decisions (discovered mid-flight)
- **[OWNER DECISION] Missing back-office action permissions (granularity gap).** WP1 found several Purchases actions have a BUTTON but NO distinct catalog permission, so they can't be granted/gated individually — this directly contradicts the owner ask "every action = its own permission": purchases.orders.{cancel,close,duplicate}, purchases.requests.{cancel,reject,convert} (submitApproval / convert-to-order), purchases.bills.duplicate, and Returns approve/post folded into one quickProcess. Note: **Sales is MORE complete** (has duplicate/close/ship/deliver/accept/reject/send). Recommendation: add the missing Purchases action permissions to the catalog (parallel to Sales) + middleware + gate — as a new **WP8 (BE)**. Awaiting owner OK to expand scope.
- **[minor, batch w/ WP8]** Cross-resource "spawn child doc" buttons left ungated in Sales/Purchases (Create Invoice/Delivery from Order, Convert-to-Order, record-payment). The target-resource create perm EXISTS (e.g. sales.invoices.create) — could gate them by that. Minor refinement.
- **[known, not this WP]** `LabNewTypesController` has a FRONTEND-PATCH header; a hard-reset deploy could wipe the file (and its new gates). The eventual proper controller split must re-apply the gates. Pre-existing infra note.
- **[WP8 expansion]** WP3 found the SAME granularity gap in Inventory/Accounting: missing action perms — inventory adjustments/transfers/receipts/issues lack `.update/.delete`; `inventory.opening` lacks approve/edit/delete; `inventory.counts` lacks edit/delete; vouchers/expenses/revenues lack `.delete`; `accounting.opening-balances` lacks confirm/delete/lock; `core.partners` lacks freeze/unfreeze; `stock-balances.returnToCustomer` no perm. → WP8 (add missing back-office action perms) spans Purchases + Inventory + Accounting + Core, not just Purchases. Awaiting owner OK.
- **[WP8 expansion]** WP4 found some Core-admin screens have NO catalog permission AT ALL (fully ungated): currencies, tax-rates, asset-categories, expense-categories, revenue-categories (accounting-module resources absent from the catalog). Plus HR gaps (leave-requests/loans/eos/offers .update/.delete; payroll edit/delete; exports). → WP8 scope now: Purchases + Inventory + Accounting + HR + a few Core screens missing action perms entirely. Substantial BE catalog addition — awaiting owner OK.
- **WP8c CANCELLED (correct decision — not a permission gap).** Investigated the 3-5 remaining ungated buttons: they call BE endpoints that DO NOT EXIST — `POST /purchases/orders/{id}/duplicate` (no duplicate route/method in Purchases), and inventory opening-balance update/delete/approve (OpeningBalanceController has only index/show/store/bulk/cancel). So these are **dead/broken FE buttons calling missing endpoints — a pre-existing bug, NOT a permissions gap.** Adding permissions would create phantom perms guarding nothing. Left ungated. **[OWNER FINDING] Separate bug: FE Duplicate (purchase orders) + opening-balance-inventory edit/delete/approve buttons hit non-existent BE endpoints (404). Fix = either implement the endpoints or remove the buttons — out of scope for permissions.**
