# 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.