# WPS — Demo catalog seeder (unblocks WP2)

**Repo:** BE only — `/home/moonui4/moon-erp-be` (branch `hazemdev4`)
**Depends on:** nothing · **Review:** code-reviewer · **[FIN]:** no · **Migration:** NO

## Goal

`moonui4_dev_be` has **zero products and zero categories**, and every store setting is an empty string. The storefront catalog (WP2) cannot be built or verified against an empty database. Deliver an **idempotent** seeder that populates a small, realistic pharmacy-store demo catalog so the storefront renders real data — and so future e2e work has a stable fixture.

This is **dev-fixture work**, not a product feature. It must be impossible for it to run anywhere unintended.

## Hard constraints (violating any of these is a failed WP)

1. ⛔ **NEVER** run `migrate:fresh`, `migrate:refresh`, `db:wipe`, or `RefreshDatabase`. The dev DBs are **not binlogged** — a wrong wipe is unrecoverable. If you think you need a fresh DB, stop and report instead.
2. ⛔ Touch **only** `moonui4_dev_be`. Never `/home/moonui`, `/home/moonui2`, `/home/moonui3` or their databases.
3. **No migration.** Use the schema exactly as it is. If a column you want doesn't exist, drop the field — do not alter the schema.
4. **Idempotent.** Running it 5× in a row must leave the DB in the same state as running it once — no duplicates, no unique-constraint crashes. Use `updateOrCreate` keyed on a stable natural key (e.g. SKU / slug + `company_id`).
5. **Not wired into any automatic path.** Do **not** add it to `DatabaseSeeder`, `WebStoreDatabaseSeeder`, `local-deploy.sh`, or the scheduler. It runs only when a human types its name.
6. **Never destructive.** It may create and update its own demo rows. It must never delete, truncate, or modify rows it did not create.

## Tenancy — read this twice

`WEBSTORE_DEFAULT_COMPANY_ID=1` in `.env`, and there is exactly **one** company (`#1 Moon ERP Demo`) with **one** branch. Every row you create **must** carry `company_id = 1` — resolve it from the WebStore config (`config('webstore.default_company_id')`) with a sane fallback, do not hardcode a bare `1` in the middle of the file.

⚠️ The backend has **zero Eloquent global scopes**. `TenantAware` stamps `company_id` on create only. A row created without `company_id` is invisible to the storefront and effectively corrupt. Verify after seeding that **every** row you inserted has the right `company_id`.

## What to seed

Discover the real table/model names first (note: there is no `categories` table — find what the store actually reads; start from `Modules/WebStore/app/Http/Controllers/Storefront/` and the resources it returns). Then seed, at minimum:

- **~5 categories** in a shallow tree (remember **A14: the `category_id` filter is non-recursive** — so products must hang off the categories the UI will actually filter by, not only off leaves of a deep tree, or the catalog will render empty and look like a frontend bug).
- **~20–30 products** across those categories, with Arabic **and** English names (`name_ar` / `name_en` or whatever the schema uses), realistic pharmacy items, sane prices as `decimal(12,3)`, stock > 0, and **active/published flags set so they actually appear** in `GET /api/store/products`.
- A handful with **no image** (so the empty-image path is exercised), a couple with a **discount/offer price**, and at least one **out of stock** — the edge cases the UI must handle.
- **Store settings** — fill the empty `settings` rows the storefront reads (both `ar` and `en` where the schema is per-language), so the header/footer have real content instead of blanks.

Keep the data obviously demo (no real brand claims), bilingual, and small enough to eyeball.

## Acceptance criteria — prove each with actual output, not assertion

- [ ] `php artisan db:seed --class=<YourSeeder>` succeeds on `moonui4_dev_be`.
- [ ] Running it **again immediately** succeeds and changes no row counts (paste both counts).
- [ ] `curl` the **live** API and paste the real response summary: `GET https://moonui4.elbaset.com/moon-erp-be/api/store/products?per_page=12` returns `meta.total > 0` with populated `data`, and the categories endpoint returns the tree.
- [ ] `Accept-Language: en` vs `ar` returns **different** product names (proves the bilingual columns are populated where the API reads them).
- [ ] At least one returned product has an empty image, one has a discount, one is out of stock.
- [ ] Every seeded row has `company_id = 1` — verify with a direct count query and paste it.
- [ ] `vendor/bin/pint --dirty --format agent` clean.
- [ ] Existing WebStore tests still green: `php artisan test Modules/WebStore --compact`. Baseline is **376 passed / 1158 assertions / 0 failed** — zero NEW failures.

## Commit

Conventional commit on `hazemdev4`, with `[skip-changelog]` in the message — this is a dev fixture, not a user-facing capability, so it gets **no** CHANGELOG bullet. Commit only; **do not push**, do not deploy, do not merge.

## Report back

State plainly what you seeded, paste the verification output, and flag anything about the schema that contradicts this brief — especially if the store reads products through a path that needs a flag or relation this brief didn't anticipate. If something in here is wrong, say so rather than working around it silently.
