---
task: Seed real agricultural demo products, create a named demo store, assign products to the store, and set up a POS access key — all via the performance seeder API so no server-side code changes are needed
cycles: 3
context: true
private: false
started: 2026-05-16T00:00:00Z
finished: 2026-05-16T00:04:00Z
---

## files
- `app/Http/Controllers/Market/PerformanceController.php` — seeder endpoints already exist; use these instead of writing new code
- `routes/api.php` — perf endpoints: `POST /api/perf/seed/stores`, `POST /api/perf/seed/products`, `POST /api/perf/pos/simulate`
- `.env` — needs `PERF_API_TOKEN` and `PERF_ACTOR_HASH` set (check if already configured)
- `resources/js/Pages/ManageProductsAdmin.vue` — can verify products after seed
- `resources/js/Pages/PosMain.vue` — target for POS demo verification

## steps
### Step 0 — Verify perf API is enabled
1. Check `.env` for `PERF_API_TOKEN` and `PERF_ACTOR_HASH`. If missing:
   - Set `PERF_API_TOKEN=bukid-demo-seed-2026` in `.env`
   - Set `PERF_ACTOR_HASH` to the hashkey of the ULTIMATE user account (query: `SELECT hashkey FROM users WHERE acct_type='ULTIMATE' LIMIT 1` via UltimateConsole or direct DB)
2. Test with: `curl -s -H "X-Perf-Token: bukid-demo-seed-2026" http://localhost:9522/api/perf/ping`
   - Expect `{"ok":true}`. If 403, token mismatch. If connection refused, server is down (task must wait for server).

### Step 1 — Create a demo store owner account (if the `099` store owner account doesn't own any store yet)
- Via the app UI or via Ultimate console SQL: confirm the store owner `099` (mobile) has a store. If not, proceed to create one.

### Step 2 — Create the demo store via perf API
```bash
curl -s -X POST http://localhost:9522/api/perf/seed/stores \
  -H "Content-Type: application/json" \
  -H "X-Perf-Token: bukid-demo-seed-2026" \
  -d '{
    "count": 1,
    "owner_hash": "<STORE_OWNER_099_HASHKEY>",
    "category": "Agri-Market",
    "prefix": "BukidBounty Fresh Market"
  }'
```
Note the returned store hashkey as `DEMO_STORE_HASH`.

### Step 3 — Seed 25 real agricultural products via perf API
```bash
curl -s -X POST http://localhost:9522/api/perf/seed/products \
  -H "Content-Type: application/json" \
  -H "X-Perf-Token: bukid-demo-seed-2026" \
  -d '{
    "count": 25,
    "target_store_hash": "<DEMO_STORE_HASH>",
    "attach_to_store": true,
    "prefix": ""
  }'
```

**However**, the perf seeder creates synthetic product names like "Product-0001". For demo quality, the products need realistic agricultural names. Therefore, instead of the perf seeder for products, **write a standalone Node.js or PHP artisan seed script** that creates the following 25 products directly via the app's own API endpoints (POST `/Products/Admin/New/` and `POST /Products/AssignToStore/`):

**Product list (agricultural, Bukidnon/Philippines context):**
1. Organic White Rice (25kg sack) — ₱1,200 — unit: sack
2. Organic Brown Rice (5kg) — ₱280 — unit: kg
3. White Corn Grits — ₱85 — unit: kg
4. Yellow Corn (fresh ears) — ₱35 — unit: piece
5. Fresh Kangkong (Water Spinach) — ₱25 — unit: bundle
6. Pechay (Bok Choy) — ₱30 — unit: bundle
7. Ampalaya (Bitter Gourd) — ₱60 — unit: kg
8. Sitaw (String Beans) — ₱55 — unit: bundle
9. Kamote (Sweet Potato) — ₱40 — unit: kg
10. Gabi (Taro Root) — ₱50 — unit: kg
11. Sayote (Chayote) — ₱45 — unit: kg
12. Labanos (Radish) — ₱30 — unit: bundle
13. Fresh Tomatoes — ₱70 — unit: kg
14. Carabao Mango (green) — ₱90 — unit: kg
15. Banana (Latundan) — ₱65 — unit: hand
16. Pineapple (Bukidnon) — ₱80 — unit: piece
17. Native Chicken (live) — ₱350 — unit: piece
18. Free-range Eggs — ₱12 — unit: piece
19. Fresh Tilapia — ₱130 — unit: kg
20. Bangus (Milkfish) — ₱180 — unit: kg
21. Carabao Milk (1L) — ₱120 — unit: liter
22. Coconut Oil (Virgin, 1L) — ₱250 — unit: bottle
23. Organic Peanuts (roasted) — ₱95 — unit: 250g pack
24. Muscovado Sugar — ₱110 — unit: 250g pack
25. Fresh Turmeric (Luyang Dilaw) — ₱60 — unit: 100g pack

**Implementation approach for the seed script:**

Write a PHP artisan command `php artisan demo:seed-products` in `app/Console/Commands/SeedDemoProducts.php`:
- The command should authenticate as the ULTIMATE user (via `Auth::loginUsingId($ultimateUserId)`)
- For each product: call `ProductController@createNew_Admin` logic directly (or use Eloquent to create `Product` + attach to `prd_str`)
- Use categories: Fresh Produce, Livestock & Poultry, Seafood, Grains & Cereals, Processed Goods
- Set `is_active=true`, realistic prices, realistic `available` stock (50–500 units)
- After creating each product globally, attach to `DEMO_STORE_HASH` via the prd_str pivot: `Store::find($store->id)->products()->attach($product->id, ['price'=>$price, 'available'=>$stock, 'is_active'=>true])`
- Print summary table on completion

### Step 4 — Set up POS Access Key for the demo store
After the store exists, create a POS access key via the UI (`/pos-access-keys`) or directly:
```sql
INSERT INTO pos_access_keys (hashkey, access_key, terminal_name, store_id, is_active, created_by, updated_by, created_at, updated_at)
SELECT 
  CONCAT(UUID(), REPEAT(MD5(RAND()), 3)) as hashkey,
  CONCAT('PK-DEMO-', UPPER(LEFT(MD5(RAND()), 8))) as access_key,
  'Demo Counter 1' as terminal_name,
  (SELECT id FROM str WHERE hashkey = '<DEMO_STORE_HASH>') as store_id,
  1 as is_active,
  (SELECT id FROM users WHERE acct_type='ULTIMATE' LIMIT 1) as created_by,
  (SELECT id FROM users WHERE acct_type='ULTIMATE' LIMIT 1) as updated_by,
  NOW() as created_at,
  NOW() as updated_at;
```
Record the `access_key` value (starts with `PK-`) for use in POS demo.

### Step 5 — Simulate a POS session to generate demo history
```bash
curl -s -X POST http://localhost:9522/api/perf/pos/simulate \
  -H "Content-Type: application/json" \
  -H "X-Perf-Token: bukid-demo-seed-2026" \
  -d '{
    "store_hash": "<DEMO_STORE_HASH>",
    "items": 5,
    "cycles": 10,
    "complete": true
  }'
```
This creates 10 completed POS transactions with 5 items each, giving the POS History page real data.

### Step 6 — Verify via UltimateConsole SQL
Run these queries in UltimateConsole to confirm:
- `SELECT COUNT(*) FROM prd_items WHERE is_active=1` — expect ≥ 25
- `SELECT COUNT(*) FROM prd_str WHERE store_id=(SELECT id FROM str WHERE hashkey='<DEMO_STORE_HASH>')` — expect 25
- `SELECT COUNT(*) FROM pos_access_keys WHERE store_id=(SELECT id FROM str WHERE hashkey='<DEMO_STORE_HASH>')` — expect ≥ 1
- `SELECT COUNT(*) FROM pos_sessions WHERE store_id=(SELECT id FROM str WHERE hashkey='<DEMO_STORE_HASH>')` — expect ≥ 10

### Step 7 — Update presentation credentials note
Record for Playwright capture script (`/tmp/bukid-presentation/capture.js`):
- Store owner login: mobile `099`, password `polomiko32!`
- Demo store name: `BukidBounty Fresh Market` (first result)
- POS access key: `PK-DEMO-XXXXXXXX` (recorded in Step 4)
- Store hashkey: `<DEMO_STORE_HASH>`

## context
```
// Perf API endpoints (from PerformanceController.php / routes/api.php):
// GET  /api/perf/ping                — health check
// POST /api/perf/seed/stores         — { count, owner_hash?, category?, prefix? }
// POST /api/perf/seed/products       — { count, target_store_hash?, attach_to_store?, prefix? }
// POST /api/perf/pos/simulate        — { store_hash, items?, cycles?, complete? }
// Auth: X-Perf-Token: <PERF_API_TOKEN env>

// Table names (canonical):
// str         = stores
// prd_items   = products  
// prd_str     = product-store pivot (price, available, is_active, description)
// pos_access_keys = POS terminal keys (prefix PK- for real terminals)
// pos_sessions = POS sessions

// Product model: App\Models\Market\Product
// Store model: App\Models\Market\Store
// Product↔Store attach: $store->products()->attach($product->id, ['price'=>..., 'available'=>..., 'is_active'=>true])
// ModelSavingListener auto-sets hashkey, created_by, updated_by for Eloquent saves

// Artisan command skeleton:
// php artisan make:command SeedDemoProducts
// class SeedDemoProducts extends Command { protected $signature = 'demo:seed-products {store_hash}'; }
```

## notes
- dictionary: `ai-docs/dictionary.md`
- linters: none — this is a data seeding task, no Vue/PHP linting needed
- constraints: 
  - Server must be running before executing curl commands (user confirmed server is currently down — this task runs later)
  - The artisan command approach is preferred over direct SQL inserts for products because ModelSavingListener auto-populates hashkey/created_by/updated_by
  - For pos_access_keys and SQL steps, manually include all required fields (created_by, updated_by, created_at, updated_at, hashkey) since raw SQL bypasses Eloquent events
  - Perf seeder prefix names are synthetic ("BukidBounty Fresh Market-001") — use the prefix param to set the store name close to desired then rename via UI if needed
  - Do not truncate existing data before seeding; additive only
