Files
jtlPosSync/productSync.md
2026-07-12 20:39:01 +02:00

2.7 KiB

Product Sync Protocol

The Sync Flow

  1. The POS client polls /api/v1/init with its current lastChangedProduct cursor (e.g. 24599). The server counts how many products have a row version greater than that cursor and returns the count.

  2. If product_count > 0, the client fetches /api/v1/product with the same cursor and a limit (e.g. 20). The server returns the changed products ordered by row version ascending.

  3. The client updates its local cursor to the highest lastChanged value 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 / bRowversion on tKategorie)
  • Customer groups (lastChangedCustomerGroup / bRowversion on tKundenGruppe)
  • Composite products (lastChangedCompositeProduct / bRowversion on tArtikelBaugruppe)
  • Deleted entities (lastChangedDeletedEntity / bLastChanged on Pos.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.