- Eager-load dumpsiteReleases.dumpsite + dumpsiteReleases.releasedByDriver
in AdminTripController::show()
- Add releases[] array to TripResource via whenLoaded('dumpsiteReleases')
- Fields: weight_kg, released_at, dumpsite_name, driver_name,
gate_pass_number, dumpsite_attendant, notes
- Admin Trips detail panel now shows a 'Dumpsite Releases' section
below the timeline with per-release cards
- Load row updated to show release count e.g. '450 kg (2 releases)'
- Add truck_is_full computed field to CollectionTeamResource
- true when current trip load >= truck capacity_kg
- true when trip status is at_dumpsite (driver rerouted to dumpsite)
- uses whenLoaded() guard, safe when currentTrip not eager-loaded
- Rework teams card badge layout (Admin UI)
- H row: remove misleading 'Not Full' for standby teams; show Full/Incomplete for active only
- Truck Load row: always rendered with 4 context-aware states
- IDLE (standby teams)
- READY (active, no current trip)
- COLLECTING (active trip, under capacity)
- TRUCK FULL (at capacity or at dumpsite)
Flutter app for scanner staff at drop-off points, package
ph.verde.verde_scanner. Built on the same foundation as
mobile-driver — Riverpod 2, Dio, go_router, secure_storage —
plus mobile_scanner for native MLKit-backed QR/Code-128 detection.
Backend:
- DropOffPointResource now exposes db_id alongside the public uuid.
The /scanner/scan endpoint takes the integer drop_off_point_id, so
the picker screen needs both to resolve a tap → scan target.
Auth + tenancy (mirrors driver app):
- Tenant gate at /tenant with the same SAN-PASCUAL-BAT shortcut.
- Login validates user.role ∈ { scanner, admin } and stashes the
bearer in keychain.
- ResolveTenant headers attached automatically on every request.
DOP picker (new):
- /dop screen lists drop-off points within 50 km of current GPS,
sorted by distance. Pick one → stored in secure storage as the
active session DOP. The router redirects authenticated scanners
here until they pick.
- GPS prompt screen if location services / permission are missing,
with retry.
Home screen (new):
- "STATIONED AT" banner showing the active DOP with a Change link
that returns to the picker.
- Premium gradient "Start scanning" CTA. Disabled until a DOP is
picked.
- Two stat counters (accepted / total) for the current session.
- Recent scans list with outcome-colored icons.
Camera scan screen (new):
- mobile_scanner with QR + Code-128 formats and noDuplicates speed.
- Verde reticle overlay with corner brackets, dimmed surroundings.
- Bottom translucent banner shows active DOP + accepted count.
- Full-screen toast overlay on every scan: green "Accepted" / amber
"Duplicate" / red "Expired" / red "Invalid" / gray "Network
error". Haptic + system click on accept. 1.5 s cooldown so the
same code can't pump.
- Torch + camera-flip controls top-right.
Data layer:
- DopRepository: GET /drop-off-points/nearby, returns DopOption with
both id and uuid.
- ScanRepository: POST /scanner/scan with current GPS, maps the
backend's "rejected: reason[]" response onto a typed ScanOutcome
(accepted / duplicate / expired / invalid / networkError).
- RecentScansController: in-memory ring buffer capped at 50.
Phase 2 will persist + add an offline buffer.
Permissions:
- iOS Info.plist: NSCameraUsageDescription + NSLocationWhenInUseUsageDescription.
- Android manifest: CAMERA + ACCESS_FINE_LOCATION + INTERNET +
uses-feature camera.
Tests: TenantScreen builds. flutter analyze: 6 style infos, no errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend:
- Surface a default retail price per code on the public partner-store
payload (config qr.default_retail_price_per_code_centavos, env
QR_DEFAULT_RETAIL_PRICE_PER_CODE_CENTAVOS, default ₱10 = 1000c).
Per-store overrides can come later — the LGU sets one official price
for now.
- Resident-facing flow uses the existing ManualPaymentDriver: resident
POSTs /me/payments/code-purchase, payment is created in pending,
admin marks it paid via /admin/payments/{uuid}/mark-paid, fulfillment
runs StoreOperations::sellToHousehold which activates the codes +
fires the CodesPurchased notification.
Frontend (customer-web/):
- /stores — list nearby partner stores (1/2/5/10/25 km radius pills),
shows in-stock pill + price + distance, links into purchase page
only when has_stock.
- /stores/[uuid] — store detail with stat row (price / stock /
distance) and PurchaseForm: quantity presets (5/10/20/50) + custom
input clamped to inventory, live total computation, blue "cash
payment" callout explaining the flow.
- /payments/[uuid] — three states:
- Pending: ticket-style card with reference + amount + 4-step
instructions; auto-polls every 8 s until paid.
- Paid: success card with checkmark + paid_at timestamp + link
to /codes.
- Failed/refunded: red card with retry CTA back to /stores.
- Resident nav adds "Buy codes" link.
- API client: PartnerStore now carries retail_price_per_code_centavos;
Payment + PaymentStatus types added; payments.show() returns the
typed status union.
Build: 21 routes, all type-check + lint clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- GET /partner-stores/nearby — public, residents browse active stores
ranked by distance via ST_Distance_Sphere; PublicPartnerStoreResource
excludes commission rate / owner / permit (only what a buyer needs).
- GET /partner-stores/{uuid} — public details, 404 if not active.
- GET /me/collections — paginated resident QR scan history with
optional from/to date filters.
- GET /me/upcoming-pickups — finds scheduled/in-progress trips whose
route includes the resident's assigned drop-off point. Returns
household_assigned: false when no household yet.
- GET /me/notifications — paginated database notifications inbox
with unread_only filter + unread_count in meta.
- POST /me/notifications/{id}/read, POST .../mark-all-read,
GET .../unread-count.
- config/cors.php — allow CUSTOMER_APP_URL and any EXTRA_CORS_ORIGINS
to call the API with credentials. Same-origin admin web is
unaffected.
202 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Dumpsites + Routes: full create forms (boundary point list,
click-to-add ordered stops with reorder controls)
- Trucks: list + create
- Teams: list + create with conflict detection (override checkbox)
- Trips: schedule + list + detail panel showing stops + timeline
- Partner Stores: list + create + Issue-Inventory action
- Reports: tabbed dashboard (daily collection, trip performance,
store sales, compliance CSV export) + rebuild aggregations
- Sidebar: moves Teams/Trucks/Trips/Stores/Reports out of "Soon",
reorganized groups
- Admin resources expose db_id alongside public uuid so admin forms
can post integer FKs without a separate lookup endpoint
164 tests still 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>
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>