Plain-English summary
The counter app keeps working when the internet drops — orders, payments and customer edits are saved locally and synced to the server when the connection comes back. A sync conflict happens when the same thing was changed in two places at once: e.g. the same order was edited on two tills, or a till edited an order offline while the server copy also changed. Rather than silently pick a winner and risk losing a sale or a payment, the counter flags the conflict and asks a person to decide. The Conflicts screen lists each one — a Sales Order Conflict, Payment Conflict or Customer Conflict — and lets you resolve it by keeping either the local (this device's) version or the server version.
When you'd use this
- After a till was offline and reconnected, and something it changed had also changed on the server.
- When two devices edited the same order, payment or customer close together.
- When a badge or prompt tells you there are unresolved conflicts to clear before the data is trustworthy.
Where to find it
The counter app at /conflicts. [code: foodops.counter.app/src/App.tsx route /conflicts → src/pages/ConflictsPage.tsx @ main]
What a conflict looks like
Each conflict names the kind of record it's about. [code: ConflictsPage.tsx entityTypeLabel() — 'salesorder-payment'/'payment' → "Payment Conflict"; 'salesorder'/order* → "Sales Order Conflict"; 'customer' → "Customer Conflict" @ main]
| Type | Means |
|---|---|
| Sales Order Conflict | The same order was changed in two places (a line, a status, a total). |
| Payment Conflict | The payment side of an order diverged — e.g. a settle recorded on two devices. |
| Customer Conflict | The same customer record was edited in two places. |
For each, the screen shows the local version (what this device holds, from its offline store) alongside the server version, so you can see what differs before you choose. [code: ConflictsPage.tsx — local IDB row vs server conflict record @ main]
Walkthrough — resolving a conflict
- Open
/conflictson the counter. Unresolved conflicts are listed with their type and when they occurred. - Open one and compare the local and server versions.
- Choose the resolution — keep local (this device's version wins) or keep server (the server's version wins).
[code: conflictService.ts resolveConflict(resolution) with 'local' | 'server'; ConflictsPage.tsx resolution UI @ main] - The conflict is resolved and drops off the active list. (A resolved conflict can linger briefly as it settles; the client hides stale-resolved ones.
[code: conflictVisibility.ts isStaleResolvedConflict @ main]) - You can also dismiss a conflict if it no longer needs action.
[code: conflictService.ts dismissConflict @ main]
Key concepts
- Why conflicts exist at all. The counter is offline-tolerant: it stores work locally (IndexedDB) and syncs later. Conflicts are the honest cost of that — the alternative, silently overwriting, could drop a real sale or double a payment. A short conflict list after a bad-connection shift is the system protecting your data, not a bug.
- Local vs server is a human decision. The counter deliberately does not auto-resolve order/payment/customer conflicts — money and customer records are too important to guess. You pick the version that's correct.
- Payment conflicts deserve the most care. A Payment Conflict touches what was actually collected; resolve those against your physical/records evidence, not by reflex.
Common questions
Q: Where is Conflicts on the counter?
A: It's the /conflicts screen in the counter app. If there are unresolved sync conflicts you'll usually be pointed to it; you can also navigate there directly.
Q: What causes a sync conflict? A: The same order, payment or customer being changed in two places — most often a till that worked offline and then reconnected while the server copy had also changed, or two devices editing the same order at once.
Q: How do I resolve one — do I keep local or server? A: Compare the two versions on the conflict, then choose keep local (this device's version) or keep server. Pick whichever is actually correct; for payment conflicts, check it against what was really collected before deciding.
Q: Is a conflict a bug or lost data? A: Neither — it's the counter refusing to silently overwrite. Nothing is lost while a conflict is open; it's waiting for you to say which version is right.
Edge cases and known issues
A resolved conflict is still showing
- Cause: A just-resolved conflict can appear briefly while the resolution settles across client and server. The client filters stale-resolved conflicts from the active view.
[code: conflictVisibility.ts isStaleResolvedConflict @ main] - Workaround: Refresh the Conflicts screen; it should clear once the resolution has synced.
Related
- Cross-Device Session Sync — how counter data moves between devices.
- POS Takeaway Order — the order flow whose edits can conflict.