# WP3 — Offline: a unit barcode must still resolve

**Repo:** FE · **Branch:** `hazemdev2` · **Migration:** no · **Depends:** WP2

## Goal

Online, scanning a carton's own barcode resolves to the product **and** its unit. Offline it does not: `src/app/features/pos/services/pos-offline.service.ts` (~lines 284-287) matches **`p.barcode` only** — the product's own barcode. A carton barcode is simply unknown when the connection drops, so the cashier gets "unmatched scan" for a code that works fine online.

That is the same asymmetry WP1 fixed on the server side, one layer down.

## What to do

1. **Cache the units.** Confirm what the IndexedDB `products` store actually holds today — if `syncAllToCache()` stores the API payload as-is, `product_units` may already be there for free now that WP1 emits it on the paged endpoint. **Check before adding anything**; do not add a store or a migration you do not need.
2. **Match per-unit barcodes offline**, mirroring the online resolution: on a hit, return the product **and** the matched unit so `PosCartService` puts the line on that unit at that unit's price — exactly as `matched_unit` does online.
3. If the cache schema genuinely must change, note that `pos_cache` is already at **v2** (it gained a `sync_failed` store). A version bump must not drop queued or parked sales — that is money waiting to be posted.

## Acceptance criteria

1. Offline, scanning a **unit** barcode adds the line on that unit at that unit's price.
2. Offline, scanning the **product's own** barcode behaves exactly as today.
3. An unknown barcode still raises the existing unmatched-scan banner — unchanged.
4. A cold offline start (cache populated, page reloaded, no network) still resolves both barcode kinds.
5. **No queued or parked sale is lost** by anything this WP does. If you touch the DB version, prove the `sync_queue` and `sync_failed` stores survive the upgrade.
6. `npx tsc --noEmit` clean, `npx ng build --base-href /app/` green.

## Environment

- FE `/home/moonui2/public_html/moon-erp`, branch `hazemdev2`. **moonui2 ONLY — never `/home/moonui`.**
- `npx ng build --base-href /app/` and `npx tsc --noEmit` — pre-authorized.
- **Do NOT deploy** — the orchestrator deploys after this WP. No merge, no push.
- Commit on `hazemdev2`, conventional `feat(pos): …`. One unpushed commit per repo from a previous package (D1) — do not touch it.
- `chown moonui2:moonui2` after every edit. Never `git checkout`/`restore`/`stash` on `src/assets/i18n/{ar,en}.json`.

## Out of scope

- The online path (WP1/WP2, done).
- The sync-queue/idempotency logic beyond not breaking it.
- Any backend change.
