# WP5 — Display base unit + seed defaults + validation hardening + ProductUnit destroy

## Goal
The polish/close-out WP: show the base-unit symbol where quantities appear (stock balances, movements, reports, PDFs); seed default unit groups/units so a fresh company isn't empty; tighten UoM validation so bad data (factor ≤ 0, multiple bases per group, cross-group product units) can't be entered; and allow deleting a per-product unit. Mostly presentation/data-integrity — not the money path.

## Parts
### A. Display base-unit symbol
- Stock balances + movements + inventory/valuation reports + document PDFs (DomPDF) should show the product's base unit symbol next to quantities. The unit is `product.base_unit_id → units.symbol`.
- BE: ensure the relevant Resources (e.g. `StockBalanceResource`, movement/report responses, `ProductResource`) expose the base unit symbol (many may already expose `baseUnit`). FE: render the symbol column/label on stock-balances + movements + relevant reports. Keep it lightweight (no N+1 — eager-load baseUnit).

### B. Seed default unit groups + units
- New seeder (Core) creating, per company, sensible defaults: e.g. **Count** (base: Piece; + Dozen=12, Box, Carton), **Weight** (base: Gram=1; Kilogram=1000, Ton=1,000,000), **Volume** (base: Milliliter=1; Liter=1000), **Length** (base: Meter). Only seed when the company has no unit groups (idempotent — never duplicate).
- Register it in the MoonStack updater seeders config (`config('moonstack.updater.seeders')`) so existing clients get the defaults on update, AND in the fresh-install seeder path. Run it on moonui2 dev (idempotently) so dev has defaults. ⛔ never migrate:fresh.

### C. Validation hardening
- `Modules/Core/app/Http/Requests/{Store,Update}UnitRequest.php` + `{Store,Update}ProductUnitRequest.php`: `conversion_factor` rule `min:0` → **`gt:0`** (reject 0/negative).
- **One base per group:** creating/updating a `units` row with `is_base = true` must ensure no OTHER active unit in that group is base (flip the old base, or reject — pick the safer: reject with a clear message, or atomically demote). Decide + implement.
- **Group consistency for product_units:** `StoreProductUnitRequest` must reject a unit whose `unit_group_id` differs from the product's base unit's group (so a product can't mix Weight + Count units). Validate against the product's base unit group.
- Keep messages bilingual (ar/en).

### D. ProductUnit destroy
- Add `destroy` to `ProductUnitController` + route (`Modules/Core/routes/api.php:102` currently `only(['index','store','update'])`). Permission-gated like the others. FE: a delete button on the product's "Alternative Units" table (`products.component`), calling a new `deleteProductUnit(productId, unitId)` service method.

## Acceptance criteria
- Stock balances/movements/reports show the base unit symbol.
- A fresh company (or dev, idempotently) gets the default unit groups/units; running the seeder twice doesn't duplicate.
- factor ≤ 0 rejected; a second base unit in a group rejected/handled; a product_unit whose group ≠ base group rejected.
- A per-product unit can be deleted via UI/API.
- `ng build` green; BE tests green vs baseline; single-unit behaviour unchanged.

## Tests
- BE Pest: validation (factor 0 rejected; cross-group product_unit rejected; second base handled); ProductUnit destroy; seeder idempotency. FE: `ng build` green.

## Flags
- Not deeply [FIN] (display/data-integrity), but the seeder touches data → run idempotently on dev, register in updater config. code-reviewer review. Migration: none (unless a display column is needed — prefer eager-load over new columns).

## Out of scope
- LIS lab_units unification (deferred). Per-lot × unit (deferred). The stock/cost/pricing paths (WP1/WP2/WP4).
