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

63 lines
2.7 KiB
Markdown

# 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:
```sql
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:
```json
[
{ "entityId": "123", "entityType": "1", "lastChanged": "208980" }
]
```
The client then removes these entities from its local database.