docs user business logic - AlexandreZanata/Abastevo GitHub Wiki
User Business Logic
Contract document. Defines how the ANP Fuel Prices app behaves from the user's perspective. All implementation must follow this document and
.cursor/rules/. In case of conflict, this document and agent core principles prevail.
Product vision
The user opens the app to discover official ANP fuel prices for their city (or any Brazilian municipality), compare products, and optionally see per-station prices β without an account, without a backend, and without real-time data (ANP publishes weekly surveys).
All processing happens on the device. The app is a reader and local index of public ANP spreadsheets.
Actors
| Actor | Description |
|---|---|
| End User | Brazilian consumer or driver looking up fuel prices |
| System | Background sync (WorkManager), local database, parsers |
| ANP (external) | Publishes weekly XLSX files on gov.br β not controlled by the app |
There is no authentication in v1. The End User is anonymous; preferences are stored locally only.
User goals
- See current-week average prices for fuels in my city.
- Search any municipality by name or browse by state.
- Know when data was last updated and whether I am offline.
- Optionally see cheapest stations near my selected city.
- Optionally see price history for my city over past weeks.
- Trust that data comes from official ANP sources.
- Track estimated tank fill cost per registered vehicle on home (UC-011).
- Get local alerts when fuel price drops vs the previous survey week (UC-014).
- Skip manual city selection on first launch by optionally sharing device location (UC-012).
- Navigate to a gas station in Maps or Waze from the station list (UC-013).
- Reach the cheapest station near me with one tap instead of comparing every row (UC-015).
Core user journey
flowchart LR
A[Launch app] --> B{Active week set?}
B -->|No| C[Onboarding + week picker]
B -->|Yes| D{Data synced?}
C --> W[Select survey week]
W --> S[Sync selected week]
S --> D
D -->|No| C
D -->|Yes| E[Home]
E --> F[Select location]
F --> G[View fuel prices]
G -->|Tap fuel card| I[Station list for that fuel]
G -->|View full price details| P[Fuel averages detail]
P -->|Tap fuel row| I
I --> J[Done / history / settings]
P --> J
Journey steps (business language)
- Launch β App checks
activeSurveyWeekpreference and local database for importedSurveyWeekrecords (BR-018, BR-019). - Onboarding (first run) β Explain data source (ANP), offline model, weekly cadence. User selects survey week (UC-009) before first sync.
- Week selection β User picks latest week or a historical week from the gov.br catalog; optional operational notes shown per week block.
- Sync β Targeted download/import for the selected week only (UC-001 scoped); progress visible.
- Home β Show selected city (or prompt to select), active week chip (tap to change week), prices per
FuelProductwith vector icons, and tank fill cost cards (UC-011). - Location selection β User picks state + municipality, searches nationally by name (IBGE catalog + FTS, UC-004), or uses device location once after onboarding (UC-012).
- Fuel detail β Tapping a fuel card on Home opens the station list for that fuel directly (UC-007) β one tap, no intermediate fuel selection. The min/avg/max averages view remains one tap away via View full price details (UC-005); from there, tapping a fuel row also opens its station list. On the station list, Cheapest near me (UC-015) recommends the cheapest station close to the user β within a configurable search radius (Settings) β and opens Maps on it.
- Vehicles β Register cars with tank size and fuel type; choose cheapest or specific station for cost estimates (UC-010).
- Settings β Language, sync preferences, storage management, data attribution, week picker shortcut, geocoding attribution.
Week picker journey (v2 β UC-009)
| Step | User action | System response |
|---|---|---|
| 1 | Completes onboarding intro or taps Survey week chip / settings | Opens week picker (full screen or bottom sheet) |
| 2 | Taps Use latest week | Sets activeSurveyWeek to catalog newest; triggers sync (UC-001) |
| 3 | Selects historical week (e.g. 31/05β06/06/2026) |
Confirmation for download; sync scoped to that week |
| 4 | Sync completes | Home/prices reflect selected week; chip shows range + βLatestβ badge when applicable |
| 5 | Returns later with cached week | Skips picker; chip allows one-tap week change globally |
Rules: BR-006, BR-018, BR-019 β default display follows activeSurveyWeek, not implicit βnewest in DBβ when preference is set.
National search journey (v2 β UC-004)
| Step | User action | System response |
|---|---|---|
| 1 | Types β₯ 2 characters in search (BR-007) | FTS queries municipality_catalog (~5β―570 IBGE municipalities) |
| 2 | Sees ranked results with state abbreviation | BR-017 tiers: exact match β prefix β fuzzy; duplicate names disambiguated |
| 3 | Selects city with no ANP data this week | UC-005 empty state (BR-010), not an error |
| 4 | Works offline | Local FTS only (BR-004); no network required after catalog seed |
Examples (Appendix A2): "san paolo" β SΓ£o Paulo (SP); "Bom Jesus" β multiple states listed.
Application states (user-visible)
The app exposes a single DataReadiness state derived from local storage:
| State | User sees | Transitions to |
|---|---|---|
EMPTY |
Onboarding: "No data yet. Connect to download ANP tables." | SYNCING (first sync started) |
SYNCING |
Progress indicator with stage (discover / download / import) | READY, PARTIAL, or ERROR |
PARTIAL |
Prices available but station detail missing or sync incomplete | READY or SYNCING |
READY |
Full home experience for latest SurveyWeek |
SYNCING (refresh), STALE (time-based) |
STALE |
Cached data shown + banner "Data may be outdated" | SYNCING, READY |
ERROR |
Error screen with retry; cached data still shown if any (BR-004) | SYNCING |
Rule: The UI never blocks reading cached data when sync fails (BR-004, BR-011).
SyncJob state machine
Owned by the Domain Layer. UI only observes and displays.
stateDiagram-v2
[*] --> IDLE
IDLE --> DISCOVERING: trigger sync
DISCOVERING --> DOWNLOADING: new files found
DISCOVERING --> COMPLETED: nothing new
DOWNLOADING --> PARSING: file saved
PARSING --> IMPORTING: rows validated
IMPORTING --> COMPLETED: success
DISCOVERING --> FAILED: network/scrape error
DOWNLOADING --> FAILED: download error
PARSING --> FAILED: parse error
IMPORTING --> FAILED: db error
FAILED --> IDLE: acknowledged / retry
COMPLETED --> IDLE: idle
| Transition | Trigger | Allowed actor |
|---|---|---|
| IDLE β DISCOVERING | Manual refresh, periodic WorkManager, first launch | User, System |
| DISCOVERING β DOWNLOADING | New PriceTable URL not in local catalog |
System |
| DOWNLOADING β PARSING | File checksum/size validated | System |
| PARSING β IMPORTING | BR-001, BR-002 pass | System |
| * β FAILED | Unrecoverable error after retries | System |
| FAILED β IDLE | User taps Retry or auto-backoff elapsed | User, System |
Terminal states per run: COMPLETED, FAILED. A new sync starts a new run from IDLE.
User preferences (local)
Stored on device only. No cloud sync in v1.
| Preference | Type | Default | Business rule |
|---|---|---|---|
preferredState |
BrazilianState? |
null | BR-012 |
preferredMunicipality |
string? | null | BR-012 |
preferredFuelProduct |
FuelProduct? |
null | Highlights on home |
locale |
BCP 47 (en, pt-BR) |
system default | i18n rule |
syncStationDetail |
boolean | true |
BR-008 |
autoDownloadLatestWeek |
boolean | true |
BR-020 |
stationDetailRetentionWeeks |
int | 12 |
BR-013 |
autoSyncOnWifi |
boolean | true |
BR-014 |
showPriceHistory |
boolean | true |
UC-006 |
activeSurveyWeek |
SurveyWeek? |
null | BR-018, BR-019, UC-009 |
locationPromptCompleted |
boolean | false |
UC-012 β show GPS prompt only once |
vehicles |
list of Vehicle |
empty | UC-010, stored in Room (not cloud) |
Feature scope by version
v1 β MVP (must implement)
- First sync + manual refresh
- Home with municipality averages (latest week)
- Search city (FTS) + filter by state
- Fuel product list with avg/min/max
- Offline read + sync status banner
- Settings: language, data attribution, clear cache
- i18n:
en+pt-BR
v1.1 β Enhanced
- Station-level prices (included in sync by default; optional opt-out in Settings)
- Price history chart per fuel (last N weeks)
- Compare two
SurveyWeeks side by side
v2 β Week picker, national search, polish (shipped v2.0.0)
- Survey week selection before sync (UC-009) β gov.br catalog parity, historical weeks on demand
- National municipality search β IBGE baseline catalog + FTS ranking (BR-016, BR-017)
- Edge-to-edge safe areas β
AnpScaffoldon all screens (Phase 13) - Fuel product vector icons β per-fuel MDI icons with accessible labels (Phase 14)
- Active week chip on app bars; week picker bottom sheet for returning users
v3 β Vehicles, geolocation, navigation, alerts (shipped v3.0.0)
- Tank fill cost on home (UC-011) β per-vehicle estimate below survey week label
- Vehicle profiles (UC-010) β up to 3 vehicles, one
FuelProducteach - Optional device location (UC-012) β Nominatim reverse geocode after first sync
- Station navigation (UC-013) β open Maps/Waze from station list
- Local price drop notifications (UC-014) β after weekly sync, no backend
v3.2 β Nearest best-price station (unreleased)
- Find nearest best-price station (UC-015) β one-tap recommendation from the station list using device location plus Nominatim forward geocoding of the cheapest candidates, inside a user-configured search radius (3/5/10/15 km, BR-028)
Out of scope (v1)
- User accounts / login
- Real-time prices
- Backend API
- Server-side push notifications
Use case index
Detailed specs live in docs/use-cases/. Do not implement undocumented use cases.
| ID | Name | Actor | Doc |
|---|---|---|---|
| UC-001 | Sync price tables | User, System | uc-001-sync-price-tables.md |
| UC-002 | Onboarding and first launch | User | uc-002-onboarding.md |
| UC-003 | Select location (state + city) | User | uc-003-select-location.md |
| UC-004 | Search municipality | User | uc-004-search-municipality.md |
| UC-005 | View municipality fuel prices | User | uc-005-view-municipality-prices.md |
| UC-006 | View price history | User | uc-006-view-price-history.md |
| UC-007 | View station prices | User | uc-007-view-station-prices.md |
| UC-008 | Manage settings and storage | User | uc-008-manage-settings.md |
| UC-009 | Select survey week | User | uc-009-select-survey-week.md |
| UC-010 | Manage vehicles | User | uc-010-manage-vehicles.md |
| UC-011 | Estimate tank fill cost | User | uc-011-estimate-tank-fill-cost.md |
| UC-012 | Resolve location from device | User | uc-012-resolve-location-from-device.md |
| UC-013 | Navigate to station | User | uc-013-navigate-to-station.md |
| UC-014 | Fuel price drop alerts | User, System | uc-014-fuel-price-drop-alerts.md |
| UC-015 | Find nearest best-price station | User | uc-015-find-nearest-best-price-station.md |
Business rules catalog (user-facing)
Rules referenced by use cases. Full list maintained in glossary.md.
| ID | Summary |
|---|---|
| BR-001 | Valid SurveyWeek date range (β€ 7 days) |
| BR-002 | Normalize ANP product labels to FuelProduct |
| BR-003 | Price history records are immutable |
| BR-004 | Offline read always serves cache + status indicator |
| BR-005 | Search requires β₯ 1 imported SurveyWeek |
| BR-006 | Default displayed week = most recent successfully imported |
| BR-007 | City search requires β₯ 2 characters |
| BR-008 | Station detail syncs by default; opt-out or on-demand when disabled |
| BR-009 | ANP data attribution visible on every price screen |
| BR-010 | Empty municipality yields empty state, not an error |
| BR-011 | Sync failure must not delete existing local data |
| BR-012 | Preferred location persists across sessions |
| BR-013 | Station detail retention follows rolling window preference |
| BR-014 | Background sync respects WiβFi-only preference |
| BR-015 | Only one active SyncJob at a time |
| BR-016 | Municipality catalog completeness (IBGE baseline) |
| BR-017 | Intelligent search ranking tiers |
| BR-018 | Week selection required before sync when auto-download latest is disabled |
| BR-019 | Active survey week overrides default-latest display |
| BR-020 | Latest survey week auto-selected and synced unless user opts out |
| BR-021 | Nominatim reverse geocode compliance (rate limit, cache, attribution) |
| BR-022 | One FuelProduct per Vehicle |
| BR-023 | Tank fill cost and alert price resolution (station β average fallback) |
| BR-024 | Multiple vehicle tank cost cards on home with stable layout |
| BR-025 | Price drop alerts only when price decreases vs previous week |
| BR-026 | Normalized station address for external map navigation |
| BR-027 | Maximum three registered vehicles |
| BR-028 | Nearest best-price station (price band + device distance) |
Domain events (user-triggered)
| Event | Triggered by | Payload |
|---|---|---|
CitySelected |
UC-003, UC-004 | municipality, state, surveyWeekId |
FuelProductSelected |
UC-005, UC-007 | fuelProduct, municipality, state |
SyncRequested |
UC-001 | source: MANUAL | SCHEDULED | FIRST_LAUNCH |
StationDetailRequested |
UC-007 | surveyWeekId, municipality, state |
PreferencesUpdated |
UC-008 | changed keys |
CacheCleared |
UC-008 | scope: ALL | STATION_DETAIL_ONLY |
SurveyWeekSelected |
UC-009 | surveyWeek, selectionMode: LATEST | SPECIFIC |
VehicleRegistered |
UC-010 | vehicleId, displayName, fuelProduct |
VehicleUpdated |
UC-010 | vehicleId, changed fields |
VehicleRemoved |
UC-010 | vehicleId |
PriceDropAlertConfigured |
UC-014 | vehicleId, enabled, alertPriceSource |
DeviceLocationResolved |
UC-012 | state, municipality |
StationNavigationRequested |
UC-013, UC-015 | cnpj, stationNavigationQuery |
Error handling (user experience)
All errors use a structured error model in Application layer; UI maps to i18n strings.
| Code | User message (i18n key) | Recovery |
|---|---|---|
SYNC_NETWORK_ERROR |
error_sync_network |
Retry button |
SYNC_PARSE_ERROR |
error_sync_parse |
Retry; log for support |
SYNC_NO_NEW_DATA |
info_sync_up_to_date |
Informational, not failure |
SEARCH_NO_RESULTS |
error_search_no_results |
Widen search / pick state |
STATION_DETAIL_NOT_SYNCED |
error_station_detail_missing |
Offer download (UC-007) |
STORAGE_FULL |
error_storage_full |
Link to settings (UC-008) |
Data freshness rules
| Condition | UI behavior |
|---|---|
| Latest week imported < 8 days ago | Show week range only (normal) |
| Latest week imported β₯ 8 days ago | Show STALE banner + refresh CTA |
| No network + cache exists | Show offline banner (BR-004) |
| No network + no cache | Show onboarding/sync required |
ANP typically publishes weekly; the 8-day threshold allows for publication delay.
Privacy and compliance
- No account or cloud sync β preferences and vehicles stored on device only.
- Optional device location (UC-012) β one-shot GPS for municipality resolution; coordinates not persisted.
- Nominatim β reverse (UC-012) and forward (UC-015) geocode calls subject to OSM usage policy; results cached locally (BR-021). UC-015 sends candidate station addresses (public ANP data) and uses device coordinates only to pick the nearest station β coordinates are not stored.
- Local notifications (UC-014) β generated on device after sync; no push backend.
- CNPJ and station addresses are public ANP data β display as-is, no masking required.
- No analytics SDK without explicit future ADR.
- LGPD: location and notifications require transparent disclosure in privacy policy and runtime permission prompts.
Implementation checklist
Before coding a feature:
- Use case document exists and is approved
- Business rules referenced are listed in glossary
- Domain events identified
- State machine transitions validated (if applicable)
- i18n keys listed for all user-visible strings
- Unit tests written (GIVEN/WHEN/THEN) for domain rules
- Empty, loading, error UI states defined