HOCO B2B
SRSRequirements

Synchronisation & offline

Requirements SYN-01 to SYN-07: data flows between the back-office and the apps, the delegate app's offline queue, idempotent replay, conflicts, visible sync state and real-time streams.

This area covers how data moves between the back-office and the apps (cahier §7.1, §7.2), how the delegate app works under weak or no network (§7.3), and how changes reach open screens live (§5.3, §6.4 and the decision of 17 Oct 2026 on live order status — AUTHORING §1b.3). Apps: delegate app (offline-first), reseller app and web (online, live order status), back-office (live operations stream). Server module: sync (idempotency records, sync.batch, sync.delta), with the outbox dispatcher and notifications for the SSE streams. The technical design is in offline sync design; idempotency rules are in architecture.

IDRequirementReleasePrioritySourceApps
SYN-01Back-office → apps flows: accounts, roles, zones, tours, objectives, tasks, catalog, prices, stock, promotions, order statuses, invoices, notifications, support repliesV1MUST§7.1API
SYN-02Apps → back-office flows: sign-ups, profiles, orders, addresses, payments, claims, positions, visits, photos, checklists, reports, installs, conversionsV1MUST§7.2API
SYN-03Critical data sent immediately when a connection is availableV1MUST§7.3DA · RA
SYN-04Delegate app keeps the tour, visits, notes and photos locally under weak networkV1MUST§7.3DA
SYN-05Sync queue handling retries, duplicates (idempotency), errors and conflictsV1MUST§7.3DA · API
SYN-06Visible sync state: synced, pending, errorV1MUST§7.3DA
SYN-07Real-time events: to the back-office (orders, visits, tours, positions) and to the reseller apps (their own order status changes)V1MUST§5.3, §6.4API · BO

SYN-01 — Back-office to apps flows

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §7.1APISRV-23

Statement — The system shall deliver to each app, within its permissions and scope, every back-office change it needs: accounts, roles, zones, tours, objectives, tasks, catalog, prices, availability, promotions, order statuses, invoices, notifications and support replies.

Rules

  1. Delivery mechanism per data:

    DataDelegate appReseller app and webFreshness target
    Account status, roles, permissionsme.get at start and foreground; a suspended account gets 403 ACCOUNT_SUSPENDED on the next callsamenext call
    Zones, tours, visits, points of sale in scope, tasks, visit types, checklists, reference lists, field settingssync.delta (offline scopes) + data push {"type":"sync","scope":"tours"} on tour changes—1 min online
    Objectives, performancedelegate.objectives, delegate.performance online, cached—5 min
    Catalog, prices of the caller's customer type, promotionsonline reads (assisted orders)online reads, HTTP caching with ETagnext request
    Stockavailability only: IN_STOCK / OUT_OF_STOCKavailability onlynext request
    Order status changes, assisted orders, returns, ticket repliesdelegate.orders.* read-onlyevents.stream live + push + inbox (SYN-07)5 s p95 while open
    Invoices, payments—online readsnext request
    Settings, app versionsappConfig.get, sync.deltaappConfig.getapp start / foreground
  2. Stock quantities never leave the back-office: no reseller- or delegate-facing response carries a quantity (AUTHORING §1b.1, STK-07).

  3. sync.delta?cursor= returns, per scope, upserts and removals (op, id, version, data) since an opaque signed cursor, 500 changes per page with nextCursor. Scopes: tours from 7 days back to 7 days ahead and their visits, points of sale in scope, open tasks, reference lists, visit types and checklist templates, field settings. Records leaving the scope (reassignment, cancelled stop) are sent as removals (ACL-03).

  4. A cursor older than 30 days answers 410 CURSOR_EXPIRED; the app then re-downloads its offline scopes from scratch without touching its queue.

  5. The delegate app pulls a delta at start, at foreground, after every successful sync.batch, on a data push and every 15 minutes while open.

Acceptance criteria

  • Given the back-office adds a stop to today's tour, an online delegate app shows it within 1 minute without user action.
  • Given a point of sale reassigned to another delegate, the next delta removes it from the former delegate's phone.
  • No reseller or delegate API response contains a stock quantity (contract test on every reseller/delegate schema).
  • A 40-day-old cursor → 410 CURSOR_EXPIRED, followed by a full re-download; pending queue items are kept.

Data — Tour, Visit, Reseller, CrmTask, ReferenceItem, VisitType, ChecklistTemplate, Setting, OutboxEvent.

Interfaces

SYN-02 — Apps to back-office flows

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §7.2APISRV-23

Statement — The system shall record in the central database, visible in the back-office, every item sent by the apps: sign-ups, profiles, orders, addresses, payments, claims, positions, visits, photos, checklists, reports, installs and conversions.

Rules

  1. Flows:

    FlowOperation(s)Offline-capableBack-office surface
    Reseller sign-upauth.signup.resellernovalidation queue BO-13, account.pending event
    Field sign-updelegate.resellers.createyesBO-09, BO-13
    Profile changesprofile.update, delegate.resellers.updatedelegate: yesreseller 360° BO-11
    Orderscart.checkout, delegate.assisted.submit, orders.confirmAssistednoorders to confirm BO-41, order.created
    Addressesaddresses.*noBO-11
    Payment declarationspayments.declarenoBO-45
    Claims and returnstickets.create (incl. AFTER_SALES), returns.createnoBO-55, BO-69
    Positionssync.locationPingsyes (buffered)BO-06, BO-23
    Visits, checklists, notes, photos, reportsdelegate.visits.*, files.*yesBO-24, BO-25
    Installsinvitations.attribute—invitation funnel
    Conversionsautomatic status changes from events—CRM, reports
  2. Every record keeps the client app (ClientApp), the author, occurredAt (device time of the action) and receivedAt.

  3. Orders are never confirmed automatically: every order arrives SUBMITTED (or PENDING) and waits for the back-office (ORD-14).

Acceptance criteria

  • A visit finished offline at 10:12 and received at 14:30 shows both times on BO-25 and the delegate app as source.
  • A reseller checkout appears in the orders-to-confirm queue within 5 seconds with channel RESELLER_APP or RESELLER_WEB.

Data — Reseller, Order, Payment, Ticket, ReturnRequest, Visit, VisitPhoto, VisitReport, LocationPing, InvitationEvent, AuditLog.

Interfaces — none specific (see the operations above).

SYN-03 — Critical data sent immediately

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §7.3DA · RASRV-23, MOB-09

Statement — The system shall send critical data as soon as a connection is available, without waiting for a manual action.

Rules

  1. Critical in the delegate app: tour start, suspend, resume and close; check-in, start, end and not-done; reports; field sign-ups; notes and call logs; task completion; assisted-order submission.
  2. The outbox flushes immediately on enqueue when online; on network regain within 10 seconds; at app foreground; then with exponential backoff (5 s, 15 s, 45 s, 2 min, 5 min cap, jitter). Photos upload in queue order before the actions that need them; position pings travel on their own batched channel and never delay actions.
  3. Reseller app and web keep no offline order queue: checkout needs network (prices and availability are re-validated); if the response is lost, the app retries with the same Idempotency-Key and gets the original order (Idempotent-Replayed: true).
  4. Target: an online delegate action reaches the server within 5 seconds (p95), photo upload time excluded.

Acceptance criteria

  • Airplane mode off → queued check-ins and reports are sent within 10 seconds without user action.
  • A checkout whose response is lost and retried creates exactly one order.

Data — IdempotencyRecord, outbox on the device.

Interfaces

SYN-04 — Local storage of the tour under weak network

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §7.3DASRV-23, MOB-09, MDA-05, MDA-07, MOB-13

Statement — The delegate app shall keep locally the tours, visits, points of sale, notes and photos needed to work a full day without network.

Rules

  1. Stored locally (drift): the offline scopes of SYN-01, the outbox, and captured photos (app-private storage until upload is confirmed).
  2. Works offline: tour start, suspend, resume; programme, map pins (tiles when cached); check-in with on-device tolerance check; visit flow, checklist, note, photos, report; not-done; field sign-up; notes and call logs; task completion and rescheduling; showing and sharing an existing invitation.
  3. Needs network: tour closing (FLD-12), assisted orders (server pricing), first creation of an invitation, refreshed performance figures, ETA.
  4. Capacity: a full day fully offline — 30 visits, 60 photos, 5 sign-ups, 150 pings — then fully synchronised within 5 minutes on a 3G-class connection (≥ 1 Mbps up).
  5. Sign-out is refused while the queue is not empty (« 3 éléments non synchronisés »); sign-out wipes the local database. App updates migrate the queue without loss.
  6. Assumption — the local database is encrypted at rest with a key in secure storage.

Acceptance criteria

  • A delegate completes 5 visits with photos in airplane mode, kills and restarts the app: everything is still there and syncs on reconnection.
  • Sign-out with 2 pending items is blocked with the count.

Data — device database; Tour, Visit, Reseller, VisitPhoto.

Interfaces

SYN-05 — Sync queue: retries, duplicates, errors, conflicts

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §7.3DA · APISRV-03, SRV-04, SRV-23, MOB-02, MOB-09, MOB-13

Statement — The system shall replay queued offline actions in order, exactly once, and report per item whether it was applied, already applied, in conflict, rejected or to retry.

Rules

  1. Each queued item has: client id, operation id, path, body, an Idempotency-Key fixed at enqueue time and reused on every retry, occurredAt, an entity key (tour, visit or point-of-sale id) and its status.

  2. sync.batch (header Idempotency-Key required) carries up to 50 items in queue order; the server applies them sequentially, each in its own transaction, and returns one result per item (SyncItemResult).

  3. Idempotency: same key and same body → REPLAYED with the original result; same key, different body → REJECTED IDEMPOTENCY_KEY_REUSED; key in progress → RETRY. Refusals are stored too, so a replay returns the same refusal; a corrected action is a new item with a new key. Records kept 30 days.

  4. Ordering: within one entity key, items are strictly ordered. After a CONFLICT or REJECTED, the server does not attempt later items of the same entity key in that batch and returns them as CONFLICT with PREDECESSOR_FAILED (details.blockedBy); the app holds them until the user resolves the first item. Items of other entities continue.

  5. occurredAt is required; values more than 5 minutes ahead or 72 hours behind receivedAt → REJECTED OCCURRED_AT_OUT_OF_RANGE; timestamps of one visit must be ordered (OCCURRED_AT_INCONSISTENT). The app stamps actions with device time corrected by the last offset measured from the server's Date header, and warns when an item is pending for more than 48 hours.

  6. Conflicts and rejections:

    Situation while offlineResultCodeWhat the delegate can do
    Back-office cancelled the touractions dated before the cancellation APPLIED, after it CONFLICTTOUR_CANCELLEDdiscard, or contact the back-office
    Tour already closed (other device)CONFLICTTOUR_CLOSEDretry after an authorised reopening
    Stop removed by the back-officeCONFLICTVISIT_CANCELLEDdiscard
    Visit already finishedREJECTEDVISIT_LOCKEDask for an unlock
    Point of sale reassigned — edits, notes, photos on the recordREJECTEDOUT_OF_SCOPEdiscard (visits of the delegate's own tour stay APPLIED)
    Record edited meanwhileCONFLICTVERSION_CONFLICT (with details.current)review server values, re-apply as a new item
    Status moved automatically (prospect installed the app)CONFLICTRESELLER_TRANSITION_NOT_ALLOWEDaccept server state
    Phone registered meanwhile (field sign-up)REJECTEDPHONE_ALREADY_EXISTScorrect the phone or discard
    Server error, rate limit, timeoutRETRY—automatic backoff
  7. RETRY items are retried automatically; CONFLICT and REJECTED never are. 426 UPGRADE_REQUIRED pauses the queue until the app is updated; 503 MAINTENANCE pauses it until maintenance ends — offline work continues.

  8. Batch request and result shape:

{
  "items": [
    { "clientId": "0192f5a3-0001-7000-8000-000000000001", "op": "delegate.visits.arrive", "idempotencyKey": "6f1c…", "entityKey": "visit:0192…", "occurredAt": "2026-11-03T08:12:00Z", "body": { "position": { "lat": 35.6969, "lng": -0.6331, "accuracy": 18, "capturedAt": "2026-11-03T08:11:52Z" } } }
  ]
}
{
  "results": [
    { "clientId": "0192f5a3-0001-7000-8000-000000000001", "result": "APPLIED", "status": 200, "body": { "status": "CHECKED_IN", "distanceMeters": 42 } },
    { "clientId": "0192f5a3-0001-7000-8000-000000000002", "result": "CONFLICT", "status": 409, "error": { "code": "TOUR_CANCELLED" } }
  ]
}

Acceptance criteria

  • The same batch replayed three times creates each record once; items 2 and 3 answer REPLAYED (business rules).
  • A batch of 51 items → 400 VALIDATION_FAILED.
  • A not-done item dated 80 hours ago → REJECTED / OCCURRED_AT_OUT_OF_RANGE.
  • Given a rejected check-in, the start and end of the same visit in that batch come back CONFLICT / PREDECESSOR_FAILED, while another visit's items are APPLIED.
  • A 50-item batch (photos excluded) completes server-side in under 3 seconds p95 (cross-cutting NFR-1.9).

Data — IdempotencyRecord, OutboxEvent, all offline-creatable entities.

Interfaces

SYN-06 — Visible sync state

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §7.3DAMOB-09, MDA-10, MDA-11, DES-14

Statement — The delegate app shall always show whether its data is synchronised, pending or in error, and let the delegate act on items in error.

Rules

  1. Global indicator in the app bar: Synchronisé (queue empty, last delta under 15 minutes), En attente (n), Erreur (n) (items CONFLICT or REJECTED), Hors ligne.
  2. Sync centre (DA-27): items grouped by tour, visit or point of sale with status, attempts, localised error message, photo upload progress (n / m, MB), last successful sync time; actions Réessayer (after resolution), Voir (open the record), Ignorer (discard after confirmation, only for CONFLICT / REJECTED), Exporter le journal for support.
  3. Records with pending changes show a clock icon; records in error a red badge.
  4. The closing screen (DA-20) shows the pending count and blocks closing until it is 0.
  5. Messages never show raw codes alone: « Tournée annulée par l'administration — 3 actions non appliquées ».

Acceptance criteria

  • With 3 items pending offline the bar shows En attente (3); after sync, Synchronisé.
  • A TOUR_CANCELLED conflict appears in the sync centre with Ignorer and the record link.

Data — device outbox.

Interfaces

SYN-07 — Real-time events to the back-office and the reseller apps

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §5.3, §6.4; decision 17 Oct 2026API · BOSRV-26

Statement — The system shall push live events to the back-office (orders, visits, tours, positions) and to each open reseller app or web page (their own order status changes), so screens update without reload.

Rules

  1. Two SSE streams fed by the outbox (integrations):
    • admin.events.stream (/v1/admin/events, back-office): order.created, order.status_changed, visit.checked_in, visit.completed, tour.started, tour.closed, delegate.position (max one per delegate per 60 s), stock.alert, account.pending; each delivered only to sessions holding the module's view permission.
    • events.stream (/v1/events, RA, RW, DA while open): only the caller's own order.status_changed, order.modified, assisted_order.awaiting_confirmation, ticket.replied, return.status_changed, notification.created.
  2. Every order status change reaches the reseller live and as push plus inbox notification (ORD-15).
  3. Latency: commit to screen refresh within 5 seconds p95.
  4. Resume with Last-Event-ID within a 5-minute replay window; beyond it the client reloads the screen. Heartbeat every 25 seconds. Mobile apps open the stream in the foreground only; reconnection backoff 1 to 30 seconds.
  5. Payloads carry identifiers and the new state only; clients refetch details through the API with their own session.

Acceptance criteria

  • When the back-office confirms an order, the reseller's open order detail shows Validée within 5 seconds and a push arrives.
  • Reseller A's stream never carries reseller B's events; a stock manager's back-office stream carries stock.alert but no delegate.position.
  • After a 2-minute network cut, the stream resumes from Last-Event-ID without losing events.

Data — OutboxEvent, Notification.

Interfaces

On this page