Back to ER Diagrams
ProKure DB

Mobile Application Logic

Offline-first Flutter mobile application for iOS and Android providing on-the-go procurement management with biometric authentication, push notifications, barcode scanning, and approval workflows.

Version 1.0
Phase 2
January 2026
Flutter 3.x (Dart)

1. Overview

The ProKure Mobile Application is a Phase 2 companion to the web platform, built with Flutter 3.x for cross-platform deployment on iOS and Android. It enables procurement stakeholders to manage approvals, monitor KPIs, receive real-time notifications, and perform GRN scanning from anywhere. The app follows an offline-first architecture using Hive local storage with a background sync queue, ensuring uninterrupted workflows even without network connectivity.

Key Capabilities

  • Biometric authentication (fingerprint + Face ID) with PIN fallback
  • Offline-first data sync with conflict resolution
  • Push notifications via Firebase Cloud Messaging (FCM)
  • Swipe-to-approve/reject from notification inbox
  • Real-time KPI dashboard with sparkline mini-charts
  • Barcode and QR code scanning for GRN receiving
  • Purchase Requisition creation and submission
  • Vendor directory with search and quick-access
  • Offline approval queuing with auto-sync
  • Configurable quiet hours for notifications

Platform Support

Platform Minimum Version Target Version Notes
iOS iOS 15.0+ iOS 17.x Face ID, Touch ID, APNs via FCM
Android Android 12 (API 31)+ Android 14 (API 34) Fingerprint, notification channels

2. Architecture & Tech Stack

The mobile app uses Flutter 3.x with the BLoC (Business Logic Component) pattern for state management. The architecture follows a clean layered approach separating UI, business logic, data repositories, and data sources.

Application Architecture

+---------------------------------------------------------------+
|                    PRESENTATION LAYER                         |
|  Flutter Widgets  |  Screens  |  Dialogs  |  Bottom Sheets    |
+---------------------------------------------------------------+
                            |
                            v
+---------------------------------------------------------------+
|                      BLoC LAYER                               |
|  AuthBloc | ApprovalBloc | DashboardBloc | SyncBloc | ...     |
|  (State Management via flutter_bloc)                          |
+---------------------------------------------------------------+
                            |
                            v
+---------------------------------------------------------------+
|                   REPOSITORY LAYER                            |
|  AuthRepo | ApprovalRepo | DashboardRepo | ScanRepo | ...    |
|  (Abstracts data source selection: API vs Local)              |
+---------------------------------------------------------------+
                    |                       |
                    v                       v
+-------------------------------+  +----------------------------+
|     REMOTE DATA SOURCE        |  |    LOCAL DATA SOURCE       |
|  REST API (.NET 10 Backend)   |  |  Hive Boxes (Offline DB)   |
|  Dio HTTP Client              |  |  flutter_secure_storage    |
|  JWT Token Interceptor        |  |  Sync Queue                |
+-------------------------------+  +----------------------------+
                    |
                    v
+-------------------------------+
|     BACKEND SERVICES          |
|  .NET 10 API  |  PostgreSQL   |
|  MongoDB      |  Redis        |
|  FCM          |  SignalR       |
+-------------------------------+

Key Dependencies

Package Purpose Version
flutter_blocState management (BLoC pattern)^8.x
dioHTTP client with interceptors^5.x
hive_flutterOffline-first local storage^1.x
flutter_secure_storageEncrypted token storage (Keychain/Keystore)^9.x
firebase_messagingPush notifications (FCM)^14.x
local_authBiometric authentication^2.x
mobile_scannerBarcode/QR scanning^3.x
fl_chartKPI sparklines and charts^0.65.x
connectivity_plusNetwork state monitoring^5.x
go_routerNavigation and deep linking^12.x
freezedImmutable state classes (code gen)^2.x
json_serializableJSON serialization (code gen)^6.x

3. Authentication & Security

The mobile app uses biometric authentication as the primary method with a 6-digit PIN fallback. JWT tokens are stored in platform-specific secure storage (iOS Keychain / Android Keystore) and managed with automatic refresh token rotation.

1

Initial Login (First Time)

User enters email/password via the web-style login form. Upon success, the API returns an access token (15-minute expiry) and a refresh token (7-day expiry). Both are stored in flutter_secure_storage. The user is then prompted to enroll biometrics.

// Initial authentication flow
final response = await dio.post('/api/auth/login', data: {
  'email': email,
  'password': password,
  'device_id': deviceId,
  'platform': Platform.isIOS ? 'IOS' : 'ANDROID',
});
await secureStorage.write(key: 'access_token', value: response.accessToken);
await secureStorage.write(key: 'refresh_token', value: response.refreshToken);
2

Biometric Enrollment

After initial login, the app prompts for biometric enrollment. The biometric credential is linked to the device-specific secure enclave. A biometric token is generated server-side and stored locally for subsequent logins.

// Biometric enrollment
final isAvailable = await localAuth.canCheckBiometrics;
final biometrics = await localAuth.getAvailableBiometrics();
// Supports: BiometricType.fingerprint, BiometricType.face
if (isAvailable) {
  final authenticated = await localAuth.authenticate(
    localizedReason: 'Enroll biometrics for ProKure',
    options: const AuthenticationOptions(biometricOnly: true),
  );
  if (authenticated) {
    await registerBiometricToken(deviceId);
  }
}
3

Subsequent Login (Biometric)

On app launch, the user authenticates via biometric prompt. The stored biometric token is sent to the server which issues a fresh JWT pair. If biometric fails 3 times, the app falls back to PIN entry.

App Launch
    |
    v
Biometric Prompt (Fingerprint / Face ID)
    |
    +-- Success --> POST /api/mobile/auth/biometric
    |                   { biometric_token, device_id }
    |                   --> Returns new JWT pair
    |
    +-- Fail (3x) --> PIN Entry Screen
    |                   --> POST /api/auth/login (PIN mode)
    |
    +-- Cancel --> Stay on lock screen
4

JWT Token Management

A Dio interceptor automatically attaches the access token to every API request. When a 401 response is received, the interceptor transparently refreshes the token using the refresh token and retries the original request.

// Dio interceptor for token refresh
class AuthInterceptor extends Interceptor {
  void onRequest(options, handler) {
    options.headers['Authorization'] = 'Bearer $accessToken';
    handler.next(options);
  }
  void onError(error, handler) async {
    if (error.response?.statusCode == 401) {
      final newTokens = await refreshTokens();
      // Retry original request with new token
      final retryResponse = await dio.fetch(error.requestOptions);
      handler.resolve(retryResponse);
    }
  }
}
TokenExpiryStorageRotation
Access Token (JWT)15 minutesflutter_secure_storageOn each refresh
Refresh Token7 daysflutter_secure_storageRotated on use (one-time)
Biometric Token30 daysflutter_secure_storageRe-enrolled on expiry
5

Device Registration & Session Management

Each device is registered with the backend on first login. Maximum 3 devices per user. Sessions expire after 30 minutes of inactivity (configurable). The device registration includes FCM token for push notifications.

// Device registration payload
{
  "device_id": "UUID-generated-on-install",
  "platform": "IOS" | "ANDROID",
  "os_version": "17.2",
  "app_version": "1.0.0",
  "fcm_token": "firebase-cloud-messaging-token",
  "device_name": "iPhone 15 Pro",
  "biometric_capable": true,
  "biometric_type": "FACE_ID"
}
6

PIN Fallback

A 6-digit PIN is set during onboarding as a fallback when biometric authentication is unavailable or fails. The PIN is hashed locally (SHA-256 + salt) and verified against the server. After 5 failed PIN attempts, the account locks for 15 minutes.

4. Offline-First Data Sync

The app implements an offline-first architecture using Hive for local storage and a sync queue for deferred operations. The connectivity_plus package monitors network state and triggers sync when connectivity is restored.

Sync Architecture

+--------------------+          +--------------------+
|   ONLINE MODE      |          |   OFFLINE MODE     |
+--------------------+          +--------------------+
|                    |          |                    |
| User Action        |          | User Action        |
|   |                |          |   |                |
|   v                |          |   v                |
| Repository Layer   |          | Repository Layer   |
|   |                |          |   |                |
|   v                |          |   v                |
| API Call (Dio)     |          | Hive Local DB      |
|   |                |          |   |                |
|   v                |          |   v                |
| .NET 10 Backend    |          | Sync Queue (Hive)  |
|   |                |          |   |                |
|   v                |          |   +-- Stored as    |
| Response --> Cache |          |       pending ops  |
|   in Hive          |          |                    |
+--------------------+          +--------------------+
                                        |
                            Connectivity Restored
                                        |
                                        v
                              +--------------------+
                              | SYNC PROCESSOR     |
                              +--------------------+
                              | 1. Read queue FIFO |
                              | 2. POST /sync/push |
                              | 3. GET /sync/pull  |
                              | 4. Resolve conflicts|
                              | 5. Update Hive     |
                              | 6. Clear queue     |
                              +--------------------+

Conflict Resolution Strategy

Server-Wins Policy

  • When a conflict is detected (local version differs from server version), the server version always wins
  • The user is notified of the conflict with a toast message showing what changed
  • The user's offline changes are preserved in a "conflict log" Hive box for 7 days for reference
  • Conflict detection uses updated_at timestamp comparison and row_version optimistic concurrency

Sync Queue Processing

// Sync queue entry structure
class SyncQueueEntry {
  final String id;           // UUID
  final String entityType;    // 'approval', 'pr', 'grn'
  final String action;        // 'approve', 'reject', 'create', 'update'
  final Map payload;          // JSON data
  final DateTime createdAt;
  final int retryCount;       // Max 3 retries
  final String status;        // 'pending', 'processing', 'failed', 'completed'
}

// Processing order: FIFO with retry backoff
// Retry 1: 5 seconds
// Retry 2: 30 seconds
// Retry 3: 120 seconds
// After 3 failures: mark as 'failed', notify user

Data Priority Tiers

Tier Caching Strategy Data Types Max Size
Tier 1 Always cached (proactive sync) User profile, pending approvals, KPI snapshots ~5 MB
Tier 2 On-demand cache (LRU eviction) PR/PO details, vendor info, GRN records ~30 MB
Tier 3 Online-only (no caching) Reports, analytics drill-down, NLP queries N/A

Storage Limits

  • Total offline cache: max 50 MB per user (configurable)
  • Sync queue: max 500 entries before forcing online sync
  • LRU eviction triggered at 80% capacity
  • User can manually clear cache from Settings

5. Push Notification System

Push notifications are delivered via Firebase Cloud Messaging (FCM) for both iOS and Android. Notifications support deep linking to specific screens and respect user-configured quiet hours.

FCM Integration Flow

.NET 10 Backend (Notification Service)
    |
    v
FCM HTTP v1 API
    |
    +-- iOS: APNs (via FCM bridge)
    |       +-- Notification payload
    |       +-- Badge count update
    |
    +-- Android: FCM direct
            +-- Notification channel routing
            +-- Foreground/background handling

Notification Types

Type Trigger Recipient Deep Link Priority
KPI_ALERT KPI breaches configured threshold Dashboard owner /dashboard/kpi/{code} HIGH
ANOMALY_ALERT Critical anomaly detected Procurement admin /notifications/anomaly/{id} HIGH
APPROVAL_PENDING Approval waiting > 4 hours Assigned approver /approvals/{id} HIGH
DELIVERY_UPDATE ASN dispatched or delivery confirmed Receiving team /po/{id}/delivery NORMAL
BUDGET_WARNING Budget utilization > 80% Project manager /dashboard/budget/{projectId} HIGH

Quiet Hours Configuration

// User quiet hours stored in user_analytics_preferences (MongoDB)
{
  "mobile_settings": {
    "push_enabled": true,
    "quiet_hours_start": "22:00",    // 10 PM local time
    "quiet_hours_end": "07:00",      // 7 AM local time
    "quiet_hours_override": ["APPROVAL_PENDING"], // These bypass quiet hours
    "notification_channels": {
      "KPI_ALERT": true,
      "ANOMALY_ALERT": true,
      "APPROVAL_PENDING": true,
      "DELIVERY_UPDATE": true,
      "BUDGET_WARNING": true
    }
  }
}

Android Notification Channels

Channel IDNameImportanceSound
prokure_approvalsApprovalsHIGHDefault
prokure_alertsAlerts & WarningsHIGHAlert tone
prokure_updatesUpdatesDEFAULTSilent
prokure_generalGeneralLOWNone

Badge Count Management

  • iOS: Badge count = pending approvals + unread notifications
  • Updated server-side and included in every push payload
  • Cleared when user opens the Approvals Inbox or Notifications Center
  • Android: Launcher badge via ShortcutBadger (vendor-specific support)

6. Mobile Approval Workflow

The mobile app provides a streamlined approval experience with swipe gestures, bulk actions, and offline queuing. Biometric re-authentication is required for all approval actions.

1

Swipe-to-Approve/Reject from Notification

Push notifications for APPROVAL_PENDING include quick-action buttons. On iOS, 3D Touch or long-press reveals "Approve" and "Reject" actions. On Android, inline action buttons are displayed in the expanded notification.

Push Notification Received
    |
    +-- Tap --> Opens Approval Detail Screen
    |
    +-- Swipe Right (iOS) / "Approve" Button
    |       |
    |       v
    |   Biometric Prompt
    |       |
    |       +-- Success --> POST /approvals/{id}/approve
    |       +-- Fail --> Open app for manual action
    |
    +-- Swipe Left (iOS) / "Reject" Button
            |
            v
        Opens Rejection Comment Dialog
2

Approval Detail Screen

Shows complete details including: document summary (PR/PO/Invoice), line items, requester info, budget impact, approval chain status, and attached documents. Approve/Reject buttons at the bottom with required biometric confirmation.

3

Bulk Approval

The Approvals Inbox supports multi-select mode. Users can select multiple pending items and approve/reject them in batch. Each bulk action requires a single biometric confirmation. Maximum 20 items per bulk action.

// Bulk approval request
POST /api/mobile/approvals/bulk
{
  "action": "APPROVE",
  "approval_ids": ["uuid-1", "uuid-2", "uuid-3"],
  "comment": "Bulk approved via mobile",
  "biometric_verified": true
}
4

Approval with Comments

Optional comments can be added before approving. Rejection always requires a comment (minimum 10 characters). Comments support text only (no attachments from mobile).

5

Delegation Handling

If the current user has an active delegation, the Approvals Inbox shows both their own approvals and delegated items. Delegated items are tagged with a DELEGATED badge and the original approver's name.

6

Offline Approval Queuing

When offline, approvals are queued in the sync queue with full payload. Biometric is still required (verified locally). Queued approvals show a PENDING SYNC badge. Upon connectivity, the queue is processed FIFO within 5 minutes.

Offline Approval Constraints

  • Offline approvals only allowed for Tier 1 cached data (already loaded)
  • If the approval was already actioned server-side, the sync detects conflict and notifies user
  • Offline rejections require comment to be entered before queuing

7. Mobile Dashboard & KPIs

The mobile dashboard presents six KPI cards in a responsive grid layout with sparkline mini-charts showing 7-day trends. Data is pulled from the cached KPI snapshots (Tier 1) with pull-to-refresh for live updates.

KPI Card Layout (2-column grid)

KPI Code Target Chart Type Drill-Down
Procurement Cycle Time CYCLE_TIME < 14 days Sparkline (7d) By project/department
Cost Savings % COST_SAVINGS > 5% Sparkline (7d) By category/vendor
On-Time Delivery % ON_TIME_DELIVERY > 95% Sparkline (7d) By vendor
Pending Approvals PENDING_APPROVALS 0 Count badge Opens Approvals Inbox
Budget Utilization BUDGET_UTIL < 80% Progress ring By project
Vendor Rating Avg VENDOR_RATING > 4.0 Star rating Vendor directory

Dashboard Data Flow

App Launch / Pull-to-Refresh
    |
    v
Check connectivity
    |
    +-- Online --> GET /api/mobile/dashboard
    |               |
    |               v
    |           Update Hive cache (Tier 1)
    |               |
    |               v
    |           Render KPI cards with fl_chart sparklines
    |
    +-- Offline --> Read from Hive cache
                    |
                    v
                Show cached data with "Last updated: X min ago" label
                    |
                    v
                Render KPI cards (sparklines from cached history)

Refresh Behavior

  • Pull-to-refresh triggers a live API call (debounced, min 30s between refreshes)
  • Background refresh every 5 minutes when app is in foreground
  • KPI snapshots cached with 7-day history for sparkline rendering
  • Stale data indicator shown if cache is older than 15 minutes

8. Core Module Screens

The mobile app provides focused screens for the most critical procurement workflows. Each screen is optimized for mobile interaction patterns with appropriate offline support.

Screen Purpose Actions Offline Data Tier
Dashboard KPI overview with 6 metric cards View, drill-down, refresh Yes Tier 1
Approvals Inbox Pending approval items across all modules Approve, reject, bulk action, comment Yes Tier 1
PR List / Detail View purchase requisitions, create new PRs View, create, submit, search, filter Partial Tier 2
PO List / Detail View purchase order details and status View, search, filter Partial Tier 2
GRN Confirmation Scan and confirm goods receipt Scan barcode, confirm qty, capture photo Yes Tier 2
Vendor Directory Search and view vendor details Search, view profile, view rating, call Partial Tier 2
Notifications Center All notifications with read/unread status View, mark read, deep link, filter Yes Tier 1
Reports View and download generated reports View list, download PDF/CSV/XLSX No Tier 3
Profile & Settings User profile, notification prefs, cache mgmt Edit prefs, quiet hours, clear cache, logout Yes Tier 1

Navigation Structure

Bottom Navigation Bar (5 tabs)
    |
    +-- Dashboard (Home)
    |       +-- KPI Drill-Down
    |
    +-- Approvals
    |       +-- Approval Detail
    |       +-- Bulk Approve
    |
    +-- Scan (Center FAB)
    |       +-- Barcode Scanner
    |       +-- QR Scanner
    |       +-- GRN Confirmation
    |
    +-- Documents
    |       +-- PR List --> PR Detail --> Create PR
    |       +-- PO List --> PO Detail
    |       +-- Reports
    |
    +-- More
            +-- Vendor Directory --> Vendor Detail
            +-- Notifications Center
            +-- Profile & Settings

9. Barcode / QR Scanning

The mobile app integrates the device camera for barcode and QR code scanning, enabling quick GRN receiving, PO lookup, material stock checks, and vendor quick-access.

1

GRN Receiving via Barcode Scan

At the receiving dock, users scan the PO barcode printed on the delivery documents. The app pulls the PO details and line items, allowing the user to confirm received quantities, flag discrepancies, and capture photos of damaged goods.

Scan PO Barcode (Code 128 / Code 39)
    |
    v
POST /api/mobile/scan/barcode { value: "PO-2026-00145" }
    |
    v
Returns PO details + line items
    |
    v
GRN Confirmation Screen
    +-- Confirm qty per line item
    +-- Flag discrepancies (short/excess/damaged)
    +-- Capture photo evidence (camera)
    +-- Submit GRN confirmation
    |
    v
POST /api/grn/confirm (or queued if offline)
2

Material Barcode for Stock Lookup

Scan a material barcode to instantly view current stock levels, recent PO history, and price trends. Useful for warehouse staff checking inventory before creating requisitions.

3

QR Code for Vendor Quick-Access

Vendors can share a ProKure QR code (generated in the vendor portal) that links to their profile. Scanning the QR code opens the vendor detail screen with contact info, rating, and recent PO history.

// QR code payload format
{
  "type": "VENDOR",
  "vendor_id": "uuid-vendor-123",
  "tenant_id": "uuid-tenant-456"
}

// POST /api/mobile/scan/qr
// Returns: vendor profile with quick-action options
4

Supported Barcode Formats

FormatUse CaseLibrary
Code 128PO barcodes, material codesmobile_scanner
Code 39Legacy material labelsmobile_scanner
QR CodeVendor profiles, deep linksmobile_scanner
EAN-13Product identificationmobile_scanner

10. MongoDB Collections

The mobile app leverages three MongoDB collections (in the prokure_app database) for push notification analytics, user mobile preferences, and AI prediction request logging.

mobile_push_analytics

Tracks push notification lifecycle from queue to delivery to open. TTL: 90 days.

  • push_id — ObjectId, unique identifier
  • tenant_id — UUID, tenant reference
  • user_id — UUID, recipient user
  • device_id — UUID, target device
  • notification_type — KPI_ALERT | ANOMALY_ALERT | APPROVAL_PENDING | DELIVERY_UPDATE | BUDGET_WARNING
  • title — notification title text
  • body — notification body text
  • data_payload — JSON deep-link data (screen, entity_id, entity_type)
  • platform — IOS | ANDROID
  • status — QUEUED | SENT | DELIVERED | OPENED | FAILED
  • sent_at — timestamp when FCM accepted
  • delivered_at — timestamp when device received
  • opened_at — timestamp when user tapped notification
  • fcm_token — device FCM registration token
  • error_message — error details if FAILED
  • created_at — creation timestamp (TTL index: 90 days)

Indexes: (tenant_id, user_id, created_at), (notification_type, status), (created_at) TTL

user_analytics_preferences

Per-user mobile settings including quiet hours, notification channel toggles, and dashboard preferences. TTL: None (persistent).

  • user_id — UUID, primary key
  • tenant_id — UUID, tenant reference
  • default_dashboard_id — preferred dashboard layout
  • pinned_kpis — array of kpi_code strings for mobile dashboard
  • favorite_reports — array of report_ids for quick access
  • alert_thresholds — [{kpi_code, operator (GT/LT), threshold_value, channels: [PUSH/EMAIL]}]
  • digest_frequency — DAILY | WEEKLY | MONTHLY | NONE
  • timezone — IANA timezone string (e.g., "Asia/Kolkata")
  • mobile_settings — {push_enabled, quiet_hours_start, quiet_hours_end, quiet_hours_override: []}
  • notification_channels — per-type enable/disable toggles
  • updated_at, created_at — timestamps

Indexes: (user_id) unique, (tenant_id)

ai_prediction_logs

Logs AI prediction requests originating from the mobile app (e.g., price prediction on PR creation, vendor recommendation). TTL: 180 days.

  • prediction_id — ObjectId, unique identifier
  • model_id — reference to ai_model_registry
  • model_type — PRICE_PREDICTION | VENDOR_RECOMMENDATION | DELIVERY_RISK
  • tenant_id — UUID, tenant reference
  • user_id — UUID, requesting user
  • source — "MOBILE_APP" (distinguishes from web requests)
  • input_features — JSON object of input parameters
  • predicted_value — model output
  • confidence_score — 0.0 to 1.0
  • actual_value — filled post-facto for accuracy tracking
  • error_amount — |predicted - actual| (filled post-facto)
  • latency_ms — inference time in milliseconds
  • device_id — UUID, originating device
  • created_at — timestamp (TTL index: 180 days)

Indexes: (tenant_id, model_type, created_at), (user_id), (created_at) TTL

11. API Integration

The mobile app communicates with the .NET 10 backend via RESTful API endpoints. All endpoints require JWT authentication. Mobile-specific endpoints are prefixed with /api/mobile/ and include device context headers.

Method Endpoint Description Auth Offline
POST /api/mobile/auth/biometric Authenticate via biometric token Biometric token No
POST /api/mobile/auth/refresh Refresh JWT using refresh token Refresh token No
POST /api/mobile/device/register Register device with FCM token JWT No
GET /api/mobile/dashboard KPI dashboard data with sparkline history JWT Cached
GET /api/mobile/approvals List pending approvals for current user JWT Cached
POST /api/mobile/approvals/{id}/approve Approve a pending item (biometric required) JWT + Biometric Queued
POST /api/mobile/approvals/{id}/reject Reject a pending item with comment JWT + Biometric Queued
POST /api/mobile/sync/pull Pull latest data from server (delta sync) JWT No
POST /api/mobile/sync/push Push queued offline actions to server JWT No
GET /api/mobile/notifications List notifications with pagination JWT Cached
POST /api/mobile/scan/barcode Resolve barcode to PO/material entity JWT Partial
POST /api/mobile/scan/qr Resolve QR code to vendor/entity JWT No

Required Headers (All Mobile Requests)

Authorization: Bearer {access_token}
X-Device-Id: {device_uuid}
X-App-Version: 1.0.0
X-Platform: IOS | ANDROID
X-OS-Version: 17.2
X-Timezone: Asia/Kolkata
Content-Type: application/json

API Security Measures

  • All endpoints require valid JWT token (except auth/biometric and auth/refresh)
  • Rate limiting: 100 requests/minute per device
  • Certificate pinning enabled for production API domain
  • Minimum app version check on every request (X-App-Version header); returns 426 Upgrade Required if outdated
  • Device ID validation against registered devices

12. Business Rules

Core business rules governing the ProKure mobile application behavior, security, and data synchronization.

BR-MOB-001: Biometric Required for Approvals

  • Biometric authentication (fingerprint or Face ID) is mandatory for all approval actions (approve, reject, bulk approve). PIN fallback is accepted if biometric hardware is unavailable. This applies to both online and offline approval actions.

BR-MOB-002: Offline Approval Sync Window

  • Offline approvals must be synced to the server within 5 minutes of network connectivity being restored. The sync processor runs immediately upon connectivity change event and retries every 60 seconds until the queue is empty.

BR-MOB-003: Push Notification Quiet Hours

  • Push notifications respect user-configured quiet hours (default: 10 PM to 7 AM local time). During quiet hours, notifications are queued server-side and delivered when quiet hours end. Exception: notification types listed in quiet_hours_override are delivered immediately regardless of quiet hours.

BR-MOB-004: Session Inactivity Timeout

  • The mobile session expires after 30 minutes of inactivity (configurable per tenant, range: 5-60 minutes). Upon timeout, the user is returned to the biometric/PIN lock screen. Background sync continues independently of session state.

BR-MOB-005: Device Limit

  • Maximum 3 active devices per user account. When a 4th device is registered, the oldest device is automatically deregistered and its refresh token is revoked. The user is notified on the deregistered device upon next app open.

BR-MOB-006: Sync Conflict Resolution

  • When a sync conflict is detected (local and server versions differ), the server version always wins. The user is notified via an in-app toast message describing the conflict. The user's offline changes are preserved in a conflict log (Hive box) for 7 days for reference and audit purposes.

BR-MOB-007: Camera Permission for Scan Features

  • Camera permission is required for barcode/QR scanning features. If permission is denied, the scan tab shows an explanatory message with a button to open device settings. The app must not crash or behave unexpectedly when camera permission is revoked mid-session.

BR-MOB-008: Minimum App Version Enforcement

  • The backend API enforces a minimum app version via the X-App-Version header. If the app version is below the minimum, the API returns HTTP 426 (Upgrade Required) with a message directing the user to update. A "soft update" prompt is shown for recommended (non-mandatory) updates, while a "force update" blocks all functionality for critical updates.

BR-MOB-009: Offline Cache Size Limit

  • The total offline cache is limited to 50 MB per user (configurable per tenant, range: 20-100 MB). LRU eviction is triggered at 80% capacity, removing Tier 2 data first. Tier 1 data is never evicted. Users can manually clear cache from Profile & Settings.

BR-MOB-010: Failed Sync Retry Policy

  • Failed sync operations are retried up to 3 times with exponential backoff: Retry 1 after 5 seconds, Retry 2 after 30 seconds, Retry 3 after 120 seconds. After 3 consecutive failures, the sync entry is marked as "failed" and the user is notified via an in-app alert with the option to retry manually or discard the action.