54 Commits

Author SHA1 Message Date
ramram1515
f42bb7010c feat(store): backfill existing adjustments and use setAttribute for receipt_url serialization 2026-07-06 16:24:14 +08:00
ramram1515
b9f179ea19 feat(store): link retail sales to inventory adjustments and add View Receipt action to Store Portal Inventory Logs table 2026-07-06 16:20:09 +08:00
Developer
aa8fdaa5aa fix(scanner): add team_id to collection_logs for robust stats tracking
- Created a database migration to add `team_id` to `collection_logs`.
- Created a database migration to backfill `team_id` for past scans using `trip_id` and scanner's active team.
- Updated `ScanService` to lookup the scanner's active team and store the `team_id` directly on the `CollectionLog`.
- Modified the `CollectionTeam` model's `collectionLogs()` relation to be a direct `HasMany` using the new `team_id` instead of routing through `Trip`.
- Updated `AdminTeamReportController` to query metrics directly from `collection_logs.team_id`, ensuring all past and future scans (including those done without an active trip) correctly increment team stats and daily charts.
2026-07-06 16:00:01 +08:00
ramram1515
a205ec7c43 feat(web): fix LGU edit validation and update drop-off point seeders and validation 2026-07-06 15:25:58 +08:00
ramram1515
8e4773cb11 feat(store): fix dashboard NaN/N/A, recent sales table and inventory references 2026-07-06 13:20:15 +08:00
Azure
4d246ab07c feat: integrate dynamic geofence fetching, service area spatial boundaries, partner store profiles & editing with Leaflet maps, custom store portal icons, QR distribution charts with geographic/LGU filters, and optimize admin dashboards (Super Admin: Azure) 2026-07-04 18:36:38 +08:00
ramram1515
24665a9594 feat: Add handed_off trip status handling and fix trips dashboard 2026-07-03 14:22:13 +08:00
ramram1515
db82d076b5 Fix geometry SRID 4326 error in QuezonCitySeeder 2026-07-03 12:24:53 +08:00
Developer
7dccb503c0 fix(seeder): hardcode detailed QC boundary to persist across database refreshes 2026-07-03 12:17:06 +08:00
ramram1515
f4d8a1f963 Fix StoreSale factory in DevelopmentSeeder to include commission_centavos 2026-07-03 12:02:43 +08:00
ramram1515
780b6bc05f Merge branch 'main' of https://gitea-qzlf.srv1050180.hstgr.cloud/admin/Verde-Web 2026-07-03 11:54:42 +08:00
ramram1515
21874c0764 Fix store sales search and QR wallet receipt downloads 2026-07-03 11:54:40 +08:00
Developer
0916341b6b feat: expand Quezon City seeder with full organizational structure
- Users: Added 2 Residents, 3 Drivers, 3 Scanners, and 4 Helpers with 'password1' credentials.
- Teams: Created 'QC Team Alpha' and 'QC Team Bravo' with assigned drivers, scanners, and helpers.
- Assets: Added 2 dedicated Trucks (QC-TRK-001/002) for collection teams.
- Business: Created 2 additional Partner Stores with owners and spatial coordinates.
- Fix: Ensured 'assigned_from' is populated for team members to meet DB constraints.
- Data: Maintained 3-month historical collection statistics for all 142 barangays.
2026-07-03 11:37:55 +08:00
Developer
7f3357206f feat: implement fixed dashboard layout, custom geofencing shapes, and QC seeder
- UI: Refactored admin layout to a fixed 'App Shell' design with independent scrollbars for sidebar and content area.
- UI: Cleaned up sidebar partial structure to support nested scrolling.
- GEOFENCING: Added support for Circle and Rectangle tools in LGU configuration.
- GEOFENCING: Implemented high-precision (128-point) circle-to-polygon conversion for database storage.
- PAGINATION: Integrated standard Laravel pagination across Teams and Reports tables.
- PAGINATION: Added a global 'renderPagination' helper and 'Per Page' selector (25, 50, 100).
- DATA: Created QuezonCitySeeder with 142 official barangays and 3 months of historical collection/sales data.
- FIX: Corrected API total aggregations in reports to remain accurate during pagination.
- FIX: Resolved unique constraint and axis-order issues in spatial data seeders.
2026-07-03 11:19:08 +08:00
Developer
9123ffe099 Implement trip continuation and driver history 2026-07-02 17:12:28 +08:00
ramram1515
7689ecda5e fix: trip_number collision and household schema mapping in development seeder 2026-07-02 16:06:44 +08:00
ramram1515
df9f97b78a fix: household schema mapping in development seeder 2026-07-02 16:05:57 +08:00
ramram1515
b8495c6bde fix: resident profile verification fields in seeder 2026-07-02 16:05:08 +08:00
ramram1515
190fe2486e feat: scale development seeder to 3 LGUs and 10x staff/residents per requested scenario 2026-07-02 16:03:39 +08:00
ramram1515
d65bc67eba feat: add global super admin to development seeder 2026-07-02 15:56:38 +08:00
ramram1515
42fa16affc fix: correct trip column names in development seeder 2026-07-02 15:54:02 +08:00
ramram1515
ddd61b9da8 feat: add dynamic and historical trips to development seeder 2026-07-02 15:53:00 +08:00
ramram1515
84a5a31567 feat: expand development seeder to multi-LGU with multiple stores and users 2026-07-02 15:50:50 +08:00
ramram1515
3bd4823036 feat: consolidate development seeder with single admin and default password1 2026-07-02 15:46:39 +08:00
Developer
1ee4a049dc feat(trips): live mid-trip truck load input with Pusher broadcast
- Migration: add current_load_kg (nullable unsigned int) to trips table
- Trip model: add current_load_kg to fillable + integer cast
- New TruckLoadBroadcast event (ShouldBroadcastNow)
  - Channel: private admin.live
  - Event name: truck.load
  - Payload: trip_id, team_id, truck_id, current_load_kg, capacity_kg, is_full
- DriverTripController::updateLoad()
  - PATCH /api/v1/driver/trips/{trip}/update-load
  - Validates status is in_progress, saves current_load_kg, fires broadcast
- Route: PATCH driver/trips/{trip}/update-load registered
- CollectionTeamResource: truck_is_full uses current_load_kg (live estimate)
  with fallback to total_load_kg (authoritative dumpsite weight)
- TripResource: expose current_load_kg field
- Admin Teams card:
  - data-team-id attribute added to each card for Echo targeting
  - Load bar uses current_load_kg when available
  - Echo listener for truck.load event patches load bar + badge in real-time
  - truck-status-badge class added to Collecting/Truck Full spans
2026-07-02 15:01:44 +08:00
Developer
ba30a5fcdf fix: QrCode relation bug and Endroid create() error 2026-07-02 13:57:39 +08:00
Developer
d9495be0e0 feat: Implement segmented toggle for Resident/Household sales, Cash Only badge, and user_id in StoreSale 2026-07-02 13:57:39 +08:00
Developer
7745a57e84 style: apply additional linting fixes across the codebase 2026-07-02 13:56:02 +08:00
Developer
c4cbdb42c7 feat(store): complete partner store overhaul with mapping, household search, financial analytics, and resident notification system 2026-07-02 13:55:08 +08:00
Developer
a1470eb85e feat: implement team standby status, helper persistence, and store portal foundations 2026-07-02 12:03:50 +08:00
Developer
4a4b88ffe4 feat: partner store financial settlements, qr replacements, and tenant resolution fix 2026-07-01 16:38:49 +08:00
Developer
92f8ee95fc feat: Add Geofence Leaflet drawing tool for DOPs and Dumpsites 2026-06-30 13:31:36 +08:00
Developer
7dc074e3c3 Implement household, QR sale, team helpers assignment, and helper endpoints 2026-06-29 16:56:33 +08:00
Developer
b85fc255cf feat: implement barangay and household management with associated tests 2026-06-29 15:02:52 +08:00
ramram1515
9ac2ea7bda chore: apply code review checklist and web design guidelines fixes 2026-06-29 13:54:14 +08:00
bb2c574f5c feat: multi-LGU tenancy — Phase B (full data scoping)
Backend models + DB:
- Adds tenant_id to 13 more parent tables: service_areas, routes,
  collection_teams, trucks, trips, qr_code_batches, qr_codes,
  collection_logs, dumpsite_releases, payments, daily_collection_stats,
  weekly_route_performance, monthly_store_sales.
  Child tables (route_stops, trip_stops, team_members,
  truck_location_history, etc.) inherit scoping via their parent
  relation — they aren't queried directly in production code.
- HasTenant trait applied to all 13 models, plus tenant_id added
  to each fillable. Creating any one of these now auto-fills
  tenant_id from Tenancy::current().
- BatchGenerator's bulk QrCode insert was bypassing model events
  (and therefore the HasTenant creating hook). Now sets tenant_id
  inline from the just-created batch's tenant_id, which itself
  goes through the trait. Fixes "0 codes returned for resident in
  same tenant" regression.

Backfill:
- SanPascualTenantSeeder extended to backfill all 18 tenant-aware
  tables. Idempotent — only updates rows where tenant_id is null.
  Production live data: backfilled 1 route, 1 team, 1 truck, 3
  trips, 1 batch, 200 codes, 5 collection logs.

Tests:
- TestCase base now creates a default "TEST-LGU" tenant in setUp,
  sets it as Tenancy::current(), and attaches X-Tenant-Code on
  every JSON request. Tear-down clears the tenant.
- UserFactory defaults tenant_id to Tenancy::current()->id, so
  factory-created users land in the test tenant.
- Net result: 202 tests still passing, no per-test changes
  required.

Phase A scope (5 tables) + Phase B scope (13 tables) = 18 of the
~22 tenant-aware tables. The remaining child tables (route_stops,
trip_stops, team_members, truck_location_history, household_members,
trip_timeline_events, store_purchases, store_sales) are scoped
implicitly through their parent.

What's still un-tenant-aware:
- Admin web (Blade-based) — single-tenant context for now
- Driver/scanner endpoints — they currently rely on the bearer
  token's user.tenant_id, which works correctly via ResolveTenant's
  fallback. No action needed.
- Super-admin onboarding UI — Phase C.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 22:57:56 +08:00
fcf2f9d0bf feat: multi-LGU tenancy — Phase A (foundation + customer-web)
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>
2026-05-01 22:38:35 +08:00
0883d63abb chore(backend): demo resident seeder for customer-web walkthrough
Three personas covering the main resident states the customer-web
needs to render:

  juan@verde.local  → approved household, DOP assigned, 10 free
                      codes already allocated. Tests full dashboard,
                      /codes, /pickups, /tracker, /settings.
  maria@verde.local → household submitted, verification_status =
                      pending. Tests the awaiting-verification banner
                      + admin-approval flow (set status to approved
                      in /admin/households and the page auto-flips).
  pedro@verde.local → user only. Tests the /onboarding wizard from
                      scratch.

All passwords: "password". Phone + email pre-verified so login works
without OTP loops.

Idempotent — safe to re-run via:
  php artisan db:seed --class=DemoResidentSeeder

Also generates a 200-code free QR batch when none exists, so the
QrAllocator has something to draw from when seeding Juan. Hooked
into DatabaseSeeder so `migrate:fresh --seed` includes it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 21:11:56 +08:00
86ea731eb0 feat(backend): finish all deferred items — Sentry, audit, bulk, FCM, Reverb, PSGC
- **Sentry** wired in bootstrap/app.php via Sentry\Laravel\Integration.
  No-op when SENTRY_LARAVEL_DSN is empty.

- **Audit logging** broadened: LogsActivity applied to Household, QrCode,
  Trip, Payment with tight logOnly whitelists and named log channels
  ('household', 'qr_code', 'trip', 'payment') to keep the activity
  stream useful.

- **Bulk admin actions**:
  POST /admin/households/bulk-approve — skips already-approved /
  no-proof / unknown ids, reports per-id outcomes
  POST /admin/bulk/users/{suspend,activate} — admins protected
  POST /admin/bulk/qr-codes/void — respects state machine transitions

- **FCM push scaffold**: PushDriver contract with LogPushDriver
  (default) and FcmPushDriver (activates when FCM_SERVER_KEY is set).
  PushChannel adapts notifications. RoutesByPreferences now also
  routes via push when push_enabled + fcm_token. toPush() payloads
  added to HouseholdApproved / QrBalanceLow / PickupImminent.

- **Reverb WebSocket broadcast**: laravel/reverb installed.
  TruckLocationBroadcast fires on every TruckTracker::record().
  routes/channels.php authenticates: admins → private-admin.live,
  residents → area.{id}.trucks (only if their household barangay is
  covered by the service area).

- **PSGC seeder** expanded: all 17 PH regions, 4 NCR districts,
  all 17 NCR cities. Sample barangays still carry rectangular
  boundaries for point-in-polygon resolution tests.

- **Conditional SPATIAL INDEX migration** for barangays.boundary —
  safe no-op until every row has a polygon (i.e., after full PSA
  dataset import). Re-running `php artisan migrate` after import
  flips the column to NOT NULL and adds the index.

196 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:22:43 +08:00
15d56d0e98 feat(backend): finish Module 13 sub-modules + flow fixes
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>
2026-05-01 14:01:38 +08:00
0a2e187f45 feat(backend): complete Module 13 reports + analytics
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>
2026-04-30 02:58:09 +08:00
989f4b87b9 feat(backend): complete Module 12 (partner stores)
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>
2026-04-30 02:55:29 +08:00
8643057c81 feat(backend): complete Module 11 (scanning + collection logs)
collection_logs table. ScanService validates code state (active only;
used = duplicate, expired = expired, unassigned/allocated = invalid),
geo proximity (≤200m from DOP via ST_Distance_Sphere), then atomically
transitions code -> Used and writes a CollectionLog. Scans inside a
trip context bump trip_stop.total_scans + emit qr_scanned timeline event.
Bulk scan endpoint processes each scan independently for offline sync.
Triggers QrBalanceLow when household active count drops below threshold.

155 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:53:25 +08:00
13148fab97 feat(backend): complete Module 10 (trips + timeline)
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>
2026-04-30 02:51:52 +08:00
15f9295d34 feat(backend): complete Module 9 (teams + trucks)
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>
2026-04-30 02:48:40 +08:00
6643b0b1da feat(web): add login + dashboard scaffold with Verde branding
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>
2026-04-30 02:35:39 +08:00
de77503cdd feat(backend): complete Module 8 (routes)
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>
2026-04-30 02:25:04 +08:00
d458e74301 feat(backend): complete Module 7 — QR code system (core)
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>
2026-04-30 02:12:41 +08:00
474b1cfe0b feat(backend): complete Module 6 (dumpsites + geofence)
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>
2026-04-30 00:39:14 +08:00
1150a518e8 feat(backend): complete Module 5 (drop-off points + auto-assign)
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>
2026-04-30 00:11:22 +08:00