Appearance
Van Sales Mobile App
VanSalesMobileApp is the Flutter application for field sales agents who operate delivery routes. It is designed for offline-first usage — agents work in areas with intermittent connectivity and sync data when back in range or at end of day.
Overview
| Property | Value |
|---|---|
| Directory | VanSalesMobileApp/ |
| Platform | Android (primary), iOS |
| Backend services | AppsPortal.Apis, Inventory.Apis |
| Keycloak realm | microtec |
| Offline support | Full (core workflows operate without connectivity) |
| Auth flow | Keycloak OIDC / PKCE |
Key Features
Route Management
- Daily route load: At start of day (or on last successful sync), the agent downloads their assigned delivery route — a list of customers, delivery addresses, and planned order quantities.
- Route order: Customers are listed in delivery sequence with map pins.
- Route navigation: Deep-links to the device's map app (Google Maps / Apple Maps) for turn-by-turn navigation to each stop.
- Stop status tracking: Each stop is marked as Pending, Visited, Delivered, or Skipped with a timestamp.
Delivery Management
- Delivery confirmation: Agent confirms delivery by entering actual quantities delivered (may differ from planned if customer is short-shipped).
- Partial delivery: Agents can mark a partial delivery and note the reason (no storage space, customer refused, etc.).
- Return collection: Agents can collect goods returned by customers — returned items are logged as a sales return.
- Photo capture: Agents can attach delivery confirmation photos (uploaded to Attachment.Apis on sync).
- Customer signature: Digital signature collection for proof of delivery.
Sales Order Creation
Agents can create new sales orders in the field for customers not on their pre-planned route:
- Customer lookup from cached customer list
- Item lookup from cached product catalogue with current pricing
- Quantity entry and discount entry (within allowed discount limits)
- Order submission (queued for sync if offline)
Cash Collection
- Record cash payments received from customers
- Generate a payment receipt (PDF via MobileDesignSystem)
- Reconcile cash collected against invoices for the day
- End-of-day cash summary report
Inventory Van Stock
- View current stock loaded on the van (loaded from Inventory.Apis at day start)
- Real-time deduction as deliveries are confirmed (local calculation)
- End-of-day van stock count submission — agent physically counts remaining stock and enters quantities
Offline-First Architecture
The van sales app cannot rely on connectivity. The offline-first strategy is built into the data layer:
Morning Sync (Day Start)
When the agent opens the app at the start of their shift:
- The app checks connectivity.
- If online, it pulls the day's route, customer list, price list, and van stock from the backend.
- All pulled data is stored in the local SQLite database.
- The agent can now operate offline for the rest of the day.
Write Queue
Every action that modifies backend data (delivery confirmation, new order, cash collection) is written to a local sync queue first:
dart
// domain/entities/sync_event.dart
class SyncEvent {
final String id; // UUID
final String type; // 'delivery', 'new_order', 'payment', 'return'
final Map<String, dynamic> payload;
final DateTime createdAt;
SyncStatus status; // pending, inProgress, synced, failed
int retryCount;
}Sync Execution
The SyncManager processes the queue:
- Runs automatically whenever connectivity is detected (
connectivity_plus). - Processes events in creation order (FIFO).
- On failure, retries up to 3 times with exponential backoff.
- Events that fail permanently are flagged for manual review.
- A sync status indicator (icon + count) is always visible in the app header.
Conflict Resolution
The backend is the source of truth for prices, discount limits, and customer credit status. On morning sync:
- New pricing supersedes any locally cached prices.
- If a locally queued order uses a price that the backend rejects, the order is flagged and the agent is prompted to review.
Architecture
VanSalesMobileApp/
├── lib/
│ ├── core/
│ │ ├── config/ # Environment config
│ │ ├── di/ # GetIt DI setup
│ │ ├── navigation/ # go_router routes
│ │ ├── network/ # HTTP client + auth interceptor
│ │ └── sync/ # SyncManager, SyncQueue, ConnectivityWatcher
│ ├── data/
│ │ ├── api/ # Thin wrappers over MobileAPIClients
│ │ ├── local/
│ │ │ ├── database/ # Drift (SQLite) schema and DAOs
│ │ │ └── secure_storage/ # Token storage
│ │ └── repositories/ # Implementations combining local + remote
│ ├── domain/
│ │ ├── entities/ # Route, Stop, DeliveryOrder, SalesOrder, SyncEvent
│ │ ├── repositories/ # Abstract interfaces
│ │ └── use_cases/ # StartDaySync, ConfirmDelivery, CreateOrder, SubmitVanCount
│ ├── presentation/
│ │ ├── auth/ # Login
│ │ ├── day_start/ # Morning sync screen
│ │ ├── route/ # Today's route list and map view
│ │ ├── delivery/ # Stop detail, delivery confirmation, signature
│ │ ├── sales_order/ # New order creation
│ │ ├── cash/ # Cash collection and end-of-day reconciliation
│ │ ├── van_stock/ # Van stock view and end-of-day count
│ │ └── sync_status/ # Sync progress and error review
│ └── main.dart
├── android/
├── ios/
├── melos.yaml
└── pubspec.yamlLocal Database Schema (Drift)
Key tables in the SQLite database:
| Table | Content |
|---|---|
routes | Today's route header |
stops | Delivery stops with customer and address |
planned_orders | Pre-loaded orders for each stop |
deliveries | Confirmed deliveries (synced or pending) |
sales_orders | New field orders (synced or pending) |
customers | Cached customer list |
items | Cached product catalogue |
van_stock | Current van inventory |
sync_queue | All pending sync events |
payments | Cash collection records |
Shared Libraries Used
| Library | Usage in VanSalesMobileApp |
|---|---|
MobileDesignSystem | All UI — route cards, delivery forms, stock tables, PDF receipt layout |
MobileAPIClients | Generated Dart clients for AppsPortal.Apis and Inventory.Apis |
MobileSharedComp | Keycloak PKCE auth, token management, base sync utilities, connectivity helper |
Setup and Running
bash
cd VanSalesMobileApp
# First-time setup
melos run init
# Install / update dependencies
melos run get
# Run in debug mode
melos run run
# Build Android APK
melos run build-apk
# Run tests
melos run testEnvironment Configuration
dart
class Environment {
static const apiBaseUrl = String.fromEnvironment('API_BASE_URL');
static const keycloakUrl = String.fromEnvironment('KEYCLOAK_URL');
static const realm = String.fromEnvironment('KEYCLOAK_REALM', defaultValue: 'microtec');
}| Environment | API Base URL | Keycloak URL |
|---|---|---|
| dev | https://gateway.microtec-test.com | https://keycloak.microtec-test.com |
| stage | https://gateway.microtecstage.com | https://keycloak.microtecstage.com |
| production | https://gateway.onlinemicrotec.com.sa | https://keycloak.onlinemicrotec.com.sa |
bash
# Build for stage
flutter build apk \
--dart-define=API_BASE_URL=https://gateway.microtecstage.com \
--dart-define=KEYCLOAK_URL=https://keycloak.microtecstage.comPermissions Required
| Permission | Platform | Purpose |
|---|---|---|
ACCESS_FINE_LOCATION | Android | Stop navigation and delivery geo-tag |
ACCESS_COARSE_LOCATION | Android | Fallback location |
CAMERA | Android, iOS | Delivery photo capture |
INTERNET | Android | Sync and morning data pull |
NSLocationWhenInUseUsageDescription | iOS | Stop navigation |
NSCameraUsageDescription | iOS | Delivery photo |
Troubleshooting
Morning sync fails — "No route assigned"
- Verify the agent has a route assigned for today in the backend (Distribution module in the web app).
- Confirm the agent's Keycloak user has the
distribution.route.viewrole. - Check backend logs for the
GET /erp-apis/distribution/routes/todayendpoint.
Sync queue stuck — events not syncing
- Check the sync status icon in the app header for the pending count.
- Go to Sync Status screen — review any failed events and their error messages.
- Verify internet connectivity (the sync icon shows offline state if disconnected).
- If an event has failed 3 times, it is marked for manual review — the backend-side record may need to be created manually by a supervisor.
Delivery confirmation rejected by backend
The backend validates delivery quantities against van stock. If the delivered quantity exceeds the van's loaded stock, the backend rejects the confirmation. Ensure the morning van stock was loaded correctly.