# WP1 — The shared layer, FIRST (before any new document)

## Goal
Three enabling pieces that everything else depends on, and that must land **before** any new document screen exists — otherwise we copy-paste the same block 6 more times and duplicate the doc-type list a 5th time.

**(a) BE — one source of truth for document types.** `GET /core/approval-workflows/document-types` returns every `ApprovalDocumentType` with `{module, module_label, document_type, document_type_label, wired}`. `wired` = "a controller actually enforces this type". The FE will hide `wired=false`, which permanently kills the current trap where a user can configure a `delivery_note`/`grn` workflow that silently does nothing.

**(b) BE — the free guard.** Sales Order and Sales Quotation are protected only *by accident* (`confirm()` refuses and leaves the doc `draft`; downstream paths reject drafts). Add `assertApprovedForPost()` to them so the protection is explicit, not incidental.

**(c) FE — a shared approval-actions component.** The pending badge + level indicator + inline Approve/Reject + reject dialog is currently copy-pasted across 8 screens (~130 lines each). Extract it ONCE into `shared/components/approval-actions/` and refactor the 8 existing screens onto it. This is the standing house rule ("fix in the shared layer, not per-screen") and it makes WP2–WP4 cheap.

**The 8 existing document screens must behave EXACTLY as they do today after this WP.** This is a refactor + two additions, not a behaviour change.

## Exact files

### BE (`/home/moonui2/moon-erp-be`)
- `Modules/Core/app/Enums/ApprovalDocumentType.php` — read it. It has 10 cases and a `module()` map. Add whatever the endpoint needs (e.g. a `label()`/`isWired()` helper). **Do NOT add new cases in this WP.**
- `Modules/Core/app/Http/Controllers/ApprovalWorkflowController.php` — add the `documentTypes()` method + route in `Modules/Core/routes/api.php`. Gate it on the same permission the workflow list uses (`core.approval-workflows.view`).
- **How to compute `wired`:** a type is wired iff `ApprovalWorkflowService::attachDocumentReferences()` (`:382-391`) has a row for it. That map is the honest source — it is exactly what makes the inbox render. Today 8 of 10 are mapped (`delivery_note` and `grn` are NOT). Derive `wired` from that map (or a constant next to it) — do NOT hand-maintain a second list.
- `Modules/Sales/app/Http/Controllers/SalesOrderController.php` (~`:246-257` `confirm()`) and `SalesQuotationController.php` (~`:281`) — add `$this->assertApprovedForPost($doc, ApprovalDocumentType::SalesOrder|Quotation);` at the top of the commit path. The trait is already `use`d on these controllers (verify).

### FE (`/home/moonui2/public_html/moon-erp`)
- **Create** `src/app/shared/components/approval-actions/` (component + template; follow the style of the other shared components in `src/app/shared/components/`). Export it from `src/app/shared/index.ts`.
  - Inputs: the `approval` block (`DocumentApprovalState` from `core/models/approval-workflow.model.ts:78-93`).
  - Renders: pending badge with level ("بانتظار الاعتماد — مستوى X من Y"), approved badge, and — only when `approval.can_act && approval.my_pending_log_id` — inline ✓/✕ gated on `*appCan="'core.approval-logs.act'"`, plus the reject-reason dialog.
  - Output: an `acted` event so the host screen reloads its list.
  - It must call `ApprovalWorkflowService.approveLog(id)` / `rejectLog(id, {comments})` (`core/services/approval-workflow.service.ts`).
- **Refactor onto it** the 8 screens that currently hand-roll this: sales `quotations`, `orders`, `invoices`, `returns`; purchases `requests`, `orders`, `bills`, `returns`. Remove their now-dead TS (`approvalPendingLabel`, `logActing`, `logRejectDialogVisible`, `approveViaLog`, `openRejectViaLog`, `confirmRejectViaLog`) and template blocks.
  - **Reference implementation to copy semantics from:** `features/sales/orders/orders.component.html:68-104` (badge + inline act) and `:115-126` (commit button hidden while pending) + `orders.component.ts:588-676`.
  - **Do NOT change the commit-button gating** in those screens (`@if (x.approval?.state !== 'pending')`) — leave it inline in each screen; only the badge/act block moves into the shared component.
- i18n: the badge/action strings are already generic and reusable — `COMMON.APPROVAL.{PENDING_BADGE, LEVEL_OF, APPROVED_BADGE, AWAITING_YOUR_APPROVAL, APPROVE, REJECT, CONFIRM_APPROVE, REJECT_TITLE, REJECT_REASON, REJECT_REASON_PLACEHOLDER, APPROVED, REJECTED}` exist in both `ar.json`/`en.json`. **No new keys needed for the component.**

## Interfaces (later WPs consume these)
- **Exposes to WP2/WP3/WP4:** `<app-approval-actions [approval]="doc.approval" (acted)="load()" />` — drop it into any document row/dialog and the badge + inline approve/reject work. WP2–WP4 must NOT re-implement it.
- **Exposes to WP5:** `GET /core/approval-workflows/document-types` → `{ data: [{ module, module_label, document_type, document_type_label, wired }] }`. WP5 drives the config dropdown from it and filters `wired === false`.
- **Convention for WP2/WP3:** a new document type is "wired" the moment it gets a row in `attachDocumentReferences` — that same row is what makes it appear in this endpoint. One place, not two.

## Acceptance criteria
- [ ] `GET /core/approval-workflows/document-types` returns all 10 types with correct `module`, bilingual labels, and `wired` — **`delivery_note` and `grn` come back `wired: false`** (they have no row in `attachDocumentReferences` yet).
- [ ] `wired` is DERIVED from the `attachDocumentReferences` map (or a constant it also uses) — not a second hand-maintained list.
- [ ] Sales Order and Sales Quotation call `assertApprovedForPost` on their commit path; with no workflow configured it is a documented no-op (behaviour unchanged).
- [ ] `shared/components/approval-actions/` exists, is exported, and the **8** existing screens use it.
- [ ] **Zero behaviour change on the 8 screens**: same badge, same level text, same inline ✓/✕ visibility rules (`can_act && my_pending_log_id` + `core.approval-logs.act`), same reject dialog, same commit-button gating.
- [ ] `ng build` green.

## Tests
- BE: new test for the endpoint (returns 10 types; `delivery_note`/`grn` are `wired:false`; requires `core.approval-workflows.view`). Extend the existing approval suites to assert Sales Order/Quotation still behave identically with **no** workflow (no-regression) and are blocked **with** one.
- Run: `Modules/Core/tests` approval tests + `Modules/Sales/tests` + `Modules/Purchases/tests` — no NEW failures vs baseline.
- FE: `ng build` green. (Orchestrator deploys `/app`.)

## Flags
- **[FIN]** — touches the approval surface guarding posting. Fable consult on the `wired` semantics + the two new guards.
- **Migration:** none.

## Out of scope
- Do NOT add new `ApprovalDocumentType` cases (that's WP2/WP3/WP4).
- Do NOT touch Inventory, GRN, or Delivery Note controllers.
- Do NOT change the config screen or the inbox (that's WP5).
- Do NOT change the commit-button gating logic on the 8 screens — only extract the badge/act block.
