2.7 KiB
Product Sync Protocol
The Sync Flow
-
The POS client polls
/api/v1/initwith its currentlastChangedProductcursor (e.g.24599). The server counts how many products have a row version greater than that cursor and returns the count. -
If
product_count > 0, the client fetches/api/v1/productwith the same cursor and alimit(e.g. 20). The server returns the changed products ordered by row version ascending. -
The client updates its local cursor to the highest
lastChangedvalue it received in that batch (e.g.24609), so the next poll only fetches newer changes.
Example from the log
init with lastChangedProduct=24599 -> product_count=0 (no changes)
init with lastChangedProduct=24599 -> product_count=1 (a new product appeared)
product fetch lastChangedProduct=24599 -> returns product with lastChanged=24609
init with lastChangedProduct=24609 -> product_count=0 (caught up)
What is lastChangedProduct?
It's the MSSQL bRowversion value -- an auto-incrementing binary counter that SQL Server bumps whenever a row is modified. The queries in product-count.js and product-list.js filter with:
WHERE CONVERT(BIGINT, a.bRowversion) > @cursor
The client simply persists the highest lastChanged value it received so it only gets new/changed rows on subsequent polls.
How it's kept track
Server side: The cursor is stateless -- the client sends its last-known value every time. The server just runs the SQL query filtering rows above that value.
Client side: The POS client stores its lastChangedProduct (and lastChangedCategory, lastChangedCustomerGroup, etc.) persistently so it can resume syncing after a restart without re-downloading everything.
Other entity types
The same pattern applies to:
- Categories (
lastChangedCategory/bRowversionontKategorie) - Customer groups (
lastChangedCustomerGroup/bRowversionontKundenGruppe) - Composite products (
lastChangedCompositeProduct/bRowversionontArtikelBaugruppe) - Deleted entities (
lastChangedDeletedEntity/bLastChangedonPos.vDeletedEntity)
Deleted entity sync
When entities are deleted in JTL-Wawi, they appear in the Pos.vDeletedEntity view with their entity type:
| nEntityType | Entity |
|---|---|
| 1 | Artikel (Product) |
| 2 | Kategorie (Category) |
| 7 | Stückliste (Composite Product) |
The client polls /v1/init with lastChangedDeletedEntity cursor, and if deletedEntity_count > 0, fetches /v1/deletedentity to get the list of deleted entities. The response format is:
[
{ "entityId": "123", "entityType": "1", "lastChanged": "208980" }
]
The client then removes these entities from its local database.