E12: Physical Sales (Petrol) - Tasks¶
Epic: E12: Physical Sales (Petrol)
E12-F1: Petrol Sales Section (Mobile App)¶
Feature: E12-F1: Petrol Sales Section Mobile
| Status | Task | Verification |
|---|---|---|
| Create deeplink handler for petrol-sales/{match_id} | Deeplink opens app | |
| Hide section from navigation (accessible only via deeplink) | Section not in menu | |
| Check availability on page load | Sold out shows message | |
| Implement ticket holder entry form | Form submits | |
| Generate 5-digit numeric PIN on submit | PIN generated | |
| Store PIN in PetrolPIN table | Record created |
E12-F2: Petrol Quota Portal¶
Feature: E12-F2: Petrol Quota Portal
Implementation Note
Petrol quota is configured like other quotas but requires a special quota type identifier to enable PIN-based functionality.
| Status | Task | Verification |
|---|---|---|
| Create Petrol Staff role in RBAC | Role exists | |
| Implement special quota type identifier for Petrol quotas | Quota type stored | |
| Implement PIN lookup endpoint GET /petrol-portal/pins/{pin} | Returns customer data | |
| Reserve seats for 20 minutes on PIN lookup | Reservation created | |
| Implement POST /petrol-portal/pins/{pin}/confirm endpoint | Sale completed | |
| Deliver tickets to customer app on confirm | Tickets appear in app |
E12-F3: Petrol Reservation Management¶
Feature: E12-F3: Petrol Reservation Management
| Status | Task | Verification |
|---|---|---|
| Set 20-minute TTL on reservation | TTL set | |
| Send warning at 5 minutes remaining | Warning displayed | |
| Send warning at 1 minute remaining | Warning displayed | |
| Auto-release seats on timeout | Seats released | |
| Implement POST /petrol-portal/reservations/{id}/cancel | Immediate release |
E12-F4: Admin Petrol Section (Unified Quota + Auto-QR)¶
Feature: E12-F4: Admin Petrol Section
Backend (see also tasks-ticketing-backend.md → E12-F4)¶
| Status | Task | Verification |
|---|---|---|
Deprecate petrol_deeplinks table (drop migration OR stop writing) — deeplink now derived from Petrol quota ID |
Table no longer referenced by new code | |
Add partial unique index quotas (match_id) WHERE quota_type='PETROL' AND status='ACTIVE' |
At most one active Petrol quota per match | |
| Implement POST /admin/petrol/quotas — forces quota_type=PETROL, deferred_payment=TRUE, can_create_subquotas=FALSE, transfer_permission=NO | Quota created with forced flags | |
| Implement GET /admin/petrol/quotas (filters: match, status, deferred-payment status, date range) | Returns list | |
| Implement GET /admin/petrol/quotas/{id} — includes computed deeplink URL | Returns detail | |
| Implement GET /admin/petrol/quotas/{id}/qr.svg and /qr.png (≥600×600) — derived from quota ID | SVG/PNG decode back to hns://petrol-sales/{quota-id} | |
| Implement POST /admin/petrol/quotas/{id}/cancel (delegates to existing quota cancellation service) | Cancel works, standard options | |
Reject quota_type=PETROL in generic POST /admin/quotas with redirect hint |
400/422 with clear error | |
| Reject duplicate active Petrol quota creation with 409 | Concurrent create returns 409 | |
| Emit audit events on Petrol quota create/cancel | Audit records present |
Admin Portal (see also tasks-admin-portal.md → Petrol Management)¶
| Status | Task | Verification |
|---|---|---|
Add "Petrol Quotas" sub-route under existing /admin/petrol namespace |
Route renders | |
| Build Create Petrol Quota form (match picker, recipient email, sectors, quantity, algorithm, expiration — forced flags hidden) | Form creates via /admin/petrol/quotas | |
| Build Petrol quotas list view with inline QR thumbnail + SVG/PNG download + copy-deeplink | Downloads decode correctly; clipboard copy works | |
| Show match context, counters (allocated/reserved/sold), deferred-payment status, cancel action per row | All columns present; filters work | |
| Generic Quota section: mark Petrol quotas read-only, show "Managed in Petrol section →" link | Mutations disabled from generic list | |
| Generic Create Quota form: reject or redirect if admin picks Petrol type | Create blocked with redirect to Petrol section | |
| Restrict Petrol section access to ROLE_MATCH_MANAGER | 403 for other roles |
Mobile (cross-reference for completeness — belongs to E12-F1)¶
| Status | Task | Verification |
|---|---|---|
Verify iOS universal link entitlements include petrol-sales/* path |
apple-app-site-association updated | |
Verify Android App Links include petrol-sales/* path |
assetlinks.json updated | |
Update mobile deeplink handler: resolve /petrol-sales/{quota-id} — load match + sector availability from Petrol quota context |
Scanning QR opens correct match flow; CANCELLED/EXPIRED quota shows "Petrol sales unavailable" |
Last Updated: 2026-04-13