Backend:
- New tenants table with PSGC-derived code, links to
cities_municipalities, optional boundary_polygon override, theme
color, contact info, timezone.
- Adds nullable tenant_id to users / households / drop_off_points /
dumpsites / partner_stores. Foreign-keyed, indexed.
- Tenant model with deriveCode() helper + effectiveBoundary()
fallback chain.
- App\Tenancy\Tenancy — process-level current-tenant register with
withTenant() / withoutScope() helpers for jobs + super-admin.
- App\Tenancy\TenantScope — global Eloquent scope, no-op when no
tenant is set (so seeders + super-admin reads still work).
- App\Tenancy\HasTenant trait — applied to Household, DropOffPoint,
Dumpsite, PartnerStore. Auto-fills tenant_id on create from
Tenancy::current().
- ResolveTenant middleware — reads X-Tenant-Code (or X-Tenant-Id),
validates tenant exists + active, sets Tenancy::current(). Falls
back to authenticated user's tenant_id when header missing.
Registered globally on the api group.
- Login + register now require an active tenant (super-admin
bypasses). Cross-tenant credential reuse is rejected with a
403 + clear message.
- super_admin role added to RoleSeeder + users.role enum.
- Public GET /api/v1/tenants/lookup?code= — no auth, returns tenant
details for the pre-login screen.
Seeders:
- SuperAdminSeeder seeds super@verde.local (tenant_id = null).
- SanPascualTenantSeeder seeds Region IV-A → Batangas → San Pascual
municipality → sample Poblacion barangay → Tenant row with code
SAN-PASCUAL-BAT, then backfills every existing tenant-aware row
(13 users / 2 households / 5 DOPs / 1 dumpsite / 3 stores) so
the dev environment keeps working end-to-end.
- Wired into DatabaseSeeder so migrate:fresh --seed bootstraps
cleanly.
Customer-web:
- New /tenant page — text input, calls public lookup, confirms with
resolved tenant card, stores code + name in cookies (1 year).
"Pilot users: SAN-PASCUAL-BAT" hint as a clickable shortcut.
- /login + /register now redirect to /tenant?next= when no cookie,
show a verde "signing in to <LGU>" pill with a Switch link,
delegate the actual form to client components.
- /api/tenant route — POST sets cookie, DELETE clears.
- apiServer auto-attaches X-Tenant-Code on every API call when the
cookie is present.
- Tenant cookies are non-httpOnly so the client can echo them; the
session token stays httpOnly.
Build: 23 routes (added /tenant), 196 backend tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
partner_stores, store_inventories, store_purchases, store_sales tables.
Promotes qr_code_batches.target_store_id and qr_codes.assigned_to_store_id
to real FKs. StoreOperations service handles wholesale issuance
(generates fresh batch -> codes go allocated to store -> inventory tops
up -> StorePurchase recorded) and resident sales (codes flip allocated
-> active to a household, commission computed at the store's rate).
Admin endpoints: store CRUD, issue-inventory, record-sale.
160 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>