# WP4 — Warehouse UI: delete the fake scoping, show the real one

**Repo:** FE (`/home/moonui2/public_html/moon-erp`) · **Branch:** `hazemdev2` · **Depends on WP3** · **BE change:** none

## Goal

The frontend currently **fakes** warehouse scoping. This WP removes the fake, consumes the real
server-side scope from WP3, and makes the scoped state legible to the user.

## What exists today — all of it wrong, and all of it verified

1. **`WarehouseService.listForCurrentBranch()`** (`src/app/core/services/warehouse.service.ts:61`) is
   **not** a scoped endpoint. It calls `listAll()` and filters **in the browser**:
   `res.data.filter(w => w.is_active && this.branchContext.isWarehouseAllowed(w.branch_id))`.
   Every warehouse still crosses the wire.
2. **Two screens filter returned rows client-side** and leave the paginator lying:
   - `stock-balances.component.ts` (~262-271): `data = data.filter(...)` then
     `this.totalRecords.set(res.meta.total)` — **the server's total, unadjusted**. Pages appear partly
     empty and the totals cards stay company-wide.
   - `stock-card.component.ts` (~254-260 and ~381-383): the same, twice. And `loadLots()` (~303)
     fetches lots for **all warehouses** and is **not filtered at all**.
3. **The picker population is inconsistent across sibling screens of one flow:** 12 call sites use
   `listForCurrentBranch()`, **10 use raw `listAll()`** (production orders/consignment/toll/cost
   reports, purchase returns/GRNs/orders, POS settings, settings, warehouses admin). A purchase *bill*
   offers scoped warehouses while the *GRN* and *PO* in the same chain offer all of them.
4. **The same branch rule is duplicated ~10 times with divergent semantics** — 8 bank-account pickers
   and 2 branch pickers in Accounting, some allowing a null-branch resource and some not.

## What to build

1. **Delete the client-side row filters** in stock-balances and stock-card. WP3 makes the server
   return the right rows; filtering again in the browser is now both wrong and a lie about
   `totalRecords`. Removing them **fixes the paginator bug for free** — say so in your report.
2. **Make the picker consume a server-scoped list.** Read `../tasks/WP3-report.md` for what the
   warehouse endpoint now returns for a scoped user. `listForCurrentBranch()` should either become a
   thin call to that endpoint or be deleted in favour of `listAll()` (which is now scoped server-side).
   **Decide, and unify all 22 call sites onto one method** — the sibling-screen inconsistency is a bug
   in its own right, and this is the moment it is cheapest to fix.
3. **The scope badge** (settled decision §9 Q6). A small **non-interactive** indicator naming the
   user's scope on the scoped list screens. Not a filter, not a switcher. Its job is to stop a scoped
   list being indistinguishable from "the company owns one warehouse".
4. **One resource ⇒ don't ask.** When the user's assigned list has exactly one warehouse, render it as
   **static text, not a disabled select** — a disabled control reads as broken, a label reads as fact.
   The codebase already has the auto-select-when-single idiom (production confirmations, clinic slots,
   POS barcode); this is applying it to resource pickers.
5. **Out-of-scope records → the app's ordinary not-found state.** WP3 returns 404. Make sure the
   screens render the existing error path rather than an empty screen that looks like a bug.
   ⚠️ **Historical documents referencing an out-of-scope warehouse must still display it read-only** —
   the invoices screen already back-fills a missing warehouse by id for exactly this reason; follow
   that precedent rather than blanking the field.
6. **Consolidate the ~10 duplicated branch rules** into one shared helper. This is the owner's standing
   rule (fix in the shared layer, not per screen), and leaving 10 divergent copies next to a new
   scoping mechanism is how the next inconsistency gets born.

## Acceptance criteria

1. No component filters warehouse rows in the browser — grep proves it.
2. `totalRecords` and the totals cards agree with the visible rows (the paginator bug is gone).
3. All warehouse pickers across the app use **one** population method.
4. A user with one warehouse sees static text, not a select.
5. The scope badge appears on the scoped list screens and is non-interactive.
6. A 404 from an out-of-scope record renders the ordinary not-found state; a historical document
   still shows its out-of-scope warehouse read-only.
7. The ~10 duplicated branch rules are one helper.
8. **Mode `all` (the default) looks and behaves exactly like today** — this is what protects every
   existing install.
9. `npx ng build --base-href /app/` green; `npx tsc --noEmit` no new errors.

## Environment / rules

- ⛔ **NEVER** `git checkout` / `restore` / `stash` on `src/assets/i18n/ar.json` or `en.json` — they
  were destroyed that way once. Additive edits only; any new key goes into BOTH files.
- Builds are pre-authorized. **Do NOT deploy to `/app`** — the orchestrator deploys.
- `chown moonui2:moonui2` every edited file. Commit on `hazemdev2`, conventional. **No push, no merge.**
- moonui2 ONLY — never `/home/moonui`. Never print a git remote URL.

## Out of scope

The assignment UI (WP5) · cash boxes (WP6) · any backend change.
