initial: bootstrap from BukidBountyApp base

This commit is contained in:
Jonathan Sykes
2026-06-06 18:43:00 +08:00
commit eb4a5731fb
5674 changed files with 160857 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
---
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 (50500 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