Notifications: notification_preferences + Laravel notifications inbox.
SmsChannel adapter for our SmsService. RoutesByPreferences trait reads
per-user toggles. HouseholdApproved/Rejected, QrBalanceLow, and
CodesPurchased notifications wired in via auto-discovered listeners
or direct dispatch from controllers/StoreOperations.
Payments: payments table + PaymentDriver interface. ManualPaymentDriver
works out of the box; PayMongoDriver activates when
PAYMONGO_SECRET_KEY is set, falls back to manual otherwise. Resident
initiates code-purchase, admin can mark paid manually, webhook applies
real provider events. Fulfillment runs StoreOperations::sellToHousehold.
Live tracking (HTTP polling): truck_location_history (with SPATIAL
INDEX + 7-day retention plan). Driver POST /driver/trucks/{uuid}/location
writes history, updates trucks.last_known_coordinates, caches in Redis,
flags geofence-trigger when entering active trip dumpsite. Admin
GET /admin/live/trucks returns active truck positions. Reverb broadcast
deferred.
Flow corrections:
- QrAllocator now idempotent — re-approving a household no longer
re-dispenses free codes.
- arrive-dumpsite enforces dumpsite geofence via ST_Contains; can be
bypassed with override_geofence: true.
171 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
daily_collection_stats, weekly_route_performance, monthly_store_sales
aggregation tables. Aggregator service is idempotent — wipes the slice
and re-inserts. php artisan reports:aggregate (default: yesterday) for
the nightly cron. Admin endpoints: daily-collection / trip-performance
/ store-sales chart series + totals, compliance.csv stream of dumpsite
releases (DENR-style), POST rebuild for on-demand aggregation.
Payments, notifications, and live tracking sub-modules of Module 13
are deferred per scope.
164 feature tests passing.
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>
trips, trip_stops, trip_timeline_events. Admin: schedule/show/cancel.
Driver: start, arrive/depart/skip stops, report incident, arrive at
dumpsite, release load (creates dumpsite_release row + tallies trip
load), complete. Every action writes a typed timeline event with GPS.
Trip number auto-generated TRIP-YYYYMMDD-NNN. Promotes
dumpsite_releases.trip_id to a real FK now that trips exists.
148 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
trucks (with optional last_known_coordinates POINT 4326),
collection_teams, team_members. Admin CRUD for both. TeamConflictDetector
flags double-assignment of driver/scanner/truck/helper across active
teams; override_conflicts: true bypasses for emergencies. Promotes
routes.default_team_id to a real FK now that collection_teams exists.
144 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Modern minimalist split-layout login (left brand panel, right form) at
/login. Sign-in posts to /api/v1/auth/login, stores the bearer token in
localStorage, redirects to /dashboard. Demo-account expander auto-fills
the form. Placeholder dashboard fetches /me, shows top nav + stat cards
(values pending), and supports sign-out.
AdminUserSeeder now seeds 4 demo admins (admin/ops/audit/demo @verde.local,
all "password"). Tailwind config gets a Verde green palette + Inter font;
component classes for buttons + form inputs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
routes + route_stops tables. Admin CRUD with stops submitted as an
ordered array (drag-reorder is client-side; sequence is array-index).
Cloning produces an inactive copy with -COPY-XXXX suffix. RouteCalculator
recomputes total_distance_km via ST_Distance_Sphere across stop
coordinates + dumpsite, and estimated_duration_minutes from dwell time
+ travel time at config-driven avg speed (default 25 km/h).
default_team_id stays nullable bigint until Module 9 adds the FK.
139 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qr_code_batches + qr_codes tables. State machine via
spatie/laravel-model-states (unassigned → allocated → active → used,
plus expired/voided). Serial format PH-{area}-{YYMM}-{batch}-{code}-
{checksum} with 2-char SHA-256-derived checksum. BatchGenerator
bulk-inserts in chunks. QrAllocator allocates free codes to verified
households (prefers area-targeted batch, falls back to any). Real
HouseholdVerified listener replaces the placeholder. QrBalanceLow
event for Module 11.
Admin: batch CRUD, mark-printed, void code, lifecycle search, PDF
print sheet (24/A4 via dompdf + endroid/qr-code + picqer/barcode).
Resident: list own codes, balance with low_balance flag, activate
allocated codes.
Removed explicit Event::listen for HouseholdVerified — Laravel 11
auto-discovers it via handle() type-hint, and double-registering was
causing double-dispatch.
130 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dumpsites table with both coordinates POINT and boundary_polygon
POLYGON (SRID 4326). Admin CRUD accepts boundary as a list of {lat,lng}
points; ring is auto-closed. Dumpsite::containsPoint() runs ST_Contains
for geofence checks (Module 10 will fire arrived_at_dumpsite from this).
dumpsite_releases schema in place — trip_id stays nullable bigint until
Module 10 adds the FK.
109 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
drop_off_points (with SPATIAL INDEX on coordinates) +
drop_off_capacity_logs. Public nearby query via ST_Distance_Sphere
returns DOPs sorted by distance with distance_meters in payload.
Admin CRUD plus capacity-log endpoint. Household creation auto-assigns
to the nearest active DOP within 25km; resident can re-run the lookup
via POST /households/{uuid}/reassign-drop-off.
97 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
households + household_members tables. Resident endpoints to create one
household (auto-resolves barangay from GPS), upload proof of residency,
manage members. Admin endpoints to list/approve/reject. Approving fires
HouseholdVerified event with a placeholder listener; Module 7 replaces
the body with actual QR batch allocation. Approved households are
immutable to residents; resubmitting after rejection resets to pending.
84 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Laravel 11.51 + PHP 8.4 in backend/
- MySQL 9 connection (DBngin, db: verde)
- Installed Sanctum, Spatie permission/activitylog/model-states,
matanyadaev/laravel-eloquent-spatial
- API routes prefixed /api/v1 in bootstrap/app.php
- Standard response envelope { success, data, message, errors, meta }
via ApiResponse + ApiController base class
- Global exception handlers for validation/auth/not-found/http errors
on api/* routes (always JSON, never redirect to login)
- Extended users migration: uuid, phone+verified_at, role enum
(admin/resident/driver/helper/scanner/store_partner), status,
first/middle/last name, avatar_path, preferred_language, fcm_token,
last_login_at, soft deletes
- User model: HasApiTokens, HasRoles, LogsActivity, SoftDeletes,
role/status constants, auto-uuid on create
- Health endpoint at GET /api/v1/health verifies DB connection
- backend/CLAUDE.md documenting backend conventions
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>