diff --git a/docs/planning/completion-audit.md b/docs/planning/completion-audit.md new file mode 100644 index 0000000..571022e --- /dev/null +++ b/docs/planning/completion-audit.md @@ -0,0 +1,127 @@ +# PrepFlow — Completion Audit (Step 0) + +> Generated 2026-06-27 by an autonomous engineering pass. Baseline commit: `816f7e1 chore: initial commit` on `main`. +> This report inventorys the existing code against `DECISIONS.md` §3–6 and the project objective to find what is complete, stubbed, or missing before any feature work begins. + +## 1. Controllers / routes — complete vs stubbed + +| Controller | Method | Route | State | +|---|---|---|---| +| `AuthController` | `register` | `POST /api/register` | ✅ Complete — validates, `Hash::make`, `Auth::login`, `session()->regenerate(true)`. | +| | `login` | `POST /api/login` | ✅ Complete — `Hash::check`, regenerate. | +| | `logout` | `POST /api/logout` (auth) | ✅ Complete — `invalidate()` + `regenerateToken()`. | +| | `me` | `GET /api/me` (auth) | ✅ Complete. | +| `MenuController` | `index` | `GET /api/menu` | ✅ Complete — raw PDO prepared statement, `?all=1` admin flag. | +| | `store` | `POST /api/admin/menu` | ✅ Complete — PDO prepared INSERT. | +| | `update` | `PATCH /api/admin/menu/{id}` | ✅ Complete — dynamic SET, PDO. | +| | `destroy` | `DELETE /api/admin/menu/{id}` | ✅ Complete — soft-delete via `is_available=0`. | +| | `uploadImage` | `POST /api/admin/menu/image` | ✅ Complete — Cloudinary singleton, data-URL in. | +| `OrderController` | `batches` | `GET /api/batches` (auth) | ✅ Complete. | +| | `index` | `GET /api/orders` (auth) | ✅ Complete — scoped to `user_id`. | +| | `store` | `POST /api/orders` (auth) | ✅ Complete — `DB::transaction` + `lockForUpdate` capacity check. | +| | `show` | `GET /api/orders/{id}` (auth) | ✅ Complete — authz: customer can't read others' orders. | +| | `uploadProof` | `POST /api/orders/{id}/payment-proof` (auth) | ✅ Complete — data-URL decode, local disk store, `pending→payment_uploaded`, audit row. | +| | `adminIndex` | `GET /api/admin/orders` (admin) | ✅ Complete — filter by status/date. | +| | `updateStatus` | `PATCH /api/admin/orders/{id}/status` (admin) | ✅ Complete — audit row `status:{from}->{to}`. | +| `ConfigController` | `paymentMethods` | `GET /api/payment-methods` | ⚠️ Read-only. No admin CRUD for payment methods (DECISIONS §3 implies admin manages `payment_methods`; Settings UI is display-only). | +| | `fulfillmentOptions` | `GET /api/fulfillment-options` | ⚠️ Read-only. No admin CRUD. | +| `PrepController` | `index` | `GET /api/admin/prep` (admin) | ✅ Complete — aggregation SQL, optional date filter. | + +**Verdict:** Backend is functionally complete for the locked Phase-1 scope. Gaps are (a) no admin write endpoints for payment methods / fulfillment options (Settings is display-only), and (b) no shipping/courier-link/email/Maps endpoints (Phase 2 scope, not yet in DECISIONS.md). + +## 2. Customer screens — exist and what they call + +| Route file | Path | API calls | State | +|---|---|---|---| +| `Home.tsx` | `/` | none | ✅ Static marketing hero + "how it works". | +| `Menu.tsx` | `/menu` | `GET /menu` | ✅ Lists available items, add-to-cart, loading skeleton. | +| `Cart.tsx` | `/cart` | none (local store) | ✅ Qty edit, remove, clear, total. | +| `Checkout.tsx` | `/checkout` | `POST /orders`, `POST /orders/{id}/payment-proof`, `GET /payment-methods`, `GET /fulfillment-options` | ⚠️ Functional but **pre-Phase-2**: no shipping fee display, no maps/address autocomplete, no success screen (navigates to `/orders/{id}`), payment-proof camera uses `Camera.getPhoto` directly instead of `capturePhoto()` helper. | +| `Orders.tsx` | `/orders` | `GET /orders` | ✅ List with status badges. | +| `OrderDetail.tsx` | `/orders/:id` | `GET /orders/:id` | ⚠️ Tracker renders `courier_note` as plain text only — no courier-link card / "awaiting booking" placeholder, no shipping fee line, no success-state screen. | +| `Login.tsx` | `/login` | `POST /login` | ✅ Loading state + error toast. | +| `Register.tsx` | `/register` | `POST /register` | ✅ Loading state + error toast. | + +## 3. Admin screens — exist and what they call + +| Route file | Path | API calls | State | +|---|---|---|---| +| `Dashboard.tsx` | `/admin` | `GET /admin/orders` | ⚠️ Status-count cards + recent list. **No order-pattern charts** (DECISIONS §6.3 "Order pattern dashboard" is a stub). | +| `MenuManager.tsx` | `/admin/menu` | menu CRUD + `POST /admin/menu/image` | ✅ Full CRUD + camera via `capturePhoto()` helper. | +| `Orders.tsx` | `/admin/orders` | `GET /admin/orders?status=` | ✅ Filter chips + list. | +| `OrderDetail.tsx` | `/admin/orders/:id` | `GET /orders/:id`, `PATCH /admin/orders/:id}/status` | ⚠️ Advance/reject works. **No courier-link paste field** (only `rejection_reason` input exists). | +| `Prep.tsx` | `/admin/prep` | `GET /admin/prep?date=` | ✅ Procurement table. | +| `Settings.tsx` | `/admin/settings` | `GET /payment-methods`, `GET /fulfillment-options` | ⚠️ **Display-only** — no CRUD controls; copy says "Editing controls arrive in the next iteration." | + +## 4. DECISIONS.md §3–6 feature coverage + +### §3 Payment flow (no GCash API) +- ✅ GCash/Bank: QR + account shown, screenshot upload → `payment_uploaded`, admin verify → `confirmed`. +- ✅ COD: skips screenshot; admin accept → `confirmed`. +- ✅ Card tile is disabled "Coming Soon" (`is_active=false`, filtered out in Checkout). +- ⚠️ Admin payment-verification queue: `OrderDetail` shows proof + total side-by-side but there is no dedicated queue view (it's folded into the status-filtered Orders list). + +### §4 Fulfillment flow (seller-preferred couriers, no API) +- ✅ `fulfillment_options` configured per context; customer picks mode. +- ✅ Delivery address recorded; `courier_note` field exists. +- ❌ **Seller "paste rider/courier note" UI is missing** — `updateStatus` accepts `courier_note` but the admin OrderDetail form has no input for it. This is the central gap for Phase-2 shipping. + +### §5 Order lifecycle +- ✅ Full ENUM present in schema, migration, `shared/index.ts`, `types.ts`. +- ✅ `pending→payment_uploaded→confirmed→preparing→ready/out_for_delivery→completed`; `cancelled` path. +- ✅ Every status transition writes an `AuditLog` row with `action: "status:{from}->{to}"`. + +### §6 Core innovations +1. ✅ **Procurement list** — `PrepController` + `v_daily_procurement` view + `/admin/prep`. +2. ✅ **Batch capacity meter + cutoff** — `OrderController::store` inside `DB::transaction` + `lockForUpdate`; `/batches` endpoint; (capacity bar UI in Checkout is not present, only the endpoint). +3. ⚠️ **Order pattern dashboard** — only status-count cards; no SQL-aggregate charts. +4. ❌ **Smart reorder / favorites** — no top-N per customer, no "Order Again" pre-fill. Fully missing. + +## 5. Test coverage + +| Test file | Covers | +|---|---| +| `MenuCrudTest` | Menu CRUD, authz, validation, soft-delete (8 tests). | +| `MenuImageUploadTest` | Cloudinary upload mocked, authz, error paths (6 tests). | +| `ExampleTest` (Feature + Unit) | Laravel defaults. | + +**Missing tests (per Step 8):** +- ❌ `AuthTest` — register, login, logout, session regeneration, role assignment. +- ❌ `OrderTest` — create, capacity enforcement, status transitions, audit-log writes, customer authz. +- ❌ `PaymentProofTest` — upload, status transition to `payment_uploaded`, audit. +- ❌ `ConfigTest` — payment methods + fulfillment options listing. +- ❌ `PrepTest` — procurement list aggregation. + +Only Menu has feature tests today, as the objective states. + +## 6. Customer payment-proof camera flow — wiring status + +- `POST /api/orders/{id}/payment-proof` endpoint: ✅ implemented (`OrderController::uploadProof`). +- `apps/web/src/lib/camera.ts` `capturePhoto()`: ✅ exists, used by **admin** `MenuManager`. +- `Checkout.tsx` payment-proof capture: ⚠️ **wired, but not via the shared helper.** It calls `Camera.getPhoto` directly (lines 35–46) with a `catch` that toasts "Camera cancelled" on *any* error, and a separate hidden `` path. This works but diverges from AGENTS.md §1 (camera.ts is the canonical capture seam) and swallows non-cancel errors as "cancelled". Step 7 will rewire it to `capturePhoto()` + `CaptureCancelledError` for parity with MenuManager and to surface real errors. + +## 7. storage:link + +- ✅ `api/public/storage` symlink already exists (points to `api/storage/app/public`). Not needed to re-run; `uploadProof` writes to the `public` disk and returns its URL. + +## 8. Other observations / quick wins + +- `.env.example` has `DB_CONNECTION=sqlite` (correct for the `:memory:` test driver per AGENTS.md §3) and `CLOUDINARY_URL=` placeholder. No `PAYMONGO_*`, `GOOGLE_MAPS_API_KEY`, or `MAIL_*` Phase-2 placeholders yet. +- `bootstrap/app.php` disables CSRF for `api/*` for SPA dev convenience (documented). +- No `pint.json` — Pint runs on Laravel defaults (PSR-12). Step 8 must run `vendor/bin/pint --test` repo-wide and fix the 16 pre-existing failures the objective flags. +- `composer.lock` and `bun.lock` are committed and tracked. +- Order model `fillable`/`casts` has no `shipping_fee` or `courier_link` yet (Phase-2 columns). +- `v_daily_procurement` view exists in `schema.sql` but SQLite (test driver) ignores `CREATE VIEW`; `PrepController` uses inline SQL so tests will work. + +## 9. Summary — what to build, in order + +1. **DECISIONS.md §8** — unlock Phase-2 scope with reasoning (Step 1). +2. **Shipping tiers** — `shipping_fee` + `courier_link` columns (3-mirror sync), tier table, checkout integration, seller courier-link paste (Step 2). *Centerpiece.* +3. **PayMongo** — opt-in checkout-session redirect, webhook, card tile activation (Step 3). +4. **Maps** — server Distance Matrix proxy, Places autocomplete, dropdown fallback (Step 4). +5. **Transactional email** — `OrderConfirmationMail` mailable, triggers, `Mail::fake()` test (Step 5). +6. **Success screen + tracker** — `/orders/:id/success`, courier-link rendering (Step 6). +7. **Customer camera** — rewire Checkout to `capturePhoto()` (Step 7). +8. **Backend completion + tests** — Auth/Order/PaymentProof/Config/Prep tests, pint cleanup, admin config CRUD (Step 8). +9. **Frontend completion** — charts, reorder, settings CRUD, responsive sweep (Step 9). +10. **Gates + git + report** (Steps 10–12).