iVendNextDevelopers Request a sandbox

10. Integrations — system sync, estate roll-out, BI feed

For: a builder connecting a large retailer's own ERP, order system, warehouse system or analytics to iVendNext, or setting up many stores the same way. Reference implementation: Keep a customer's own system in step with ../samples/s7_system_sync/ · Set up fifty stores from one file with ../samples/s8_estate_rollout/ · Feed a data warehouse with ../samples/s9_bi_feed/. All three are Python, standard library only; any language can do the same.

This chapter covers the three integrations a large retailer asks for first. Each is a program you run on your own server, beside the shop, talking to it over the record API and the event feed (chapter 5).

When to use it

The customer says Use
"Our ERP must agree with the shop: items, prices and stock go out, sales and returns come in, nothing lost or doubled." the system sync
"Set up fifty stores the same way, change them together, and check them afterwards." the estate roll-out
"Our analysts need every sale, line and payment in our warehouse, and the day must agree with the shop's own report." the BI feed

Our own WooCommerce and Acumatica connectors are ours to build; the kit does not test or document them, and chapter 16 states what we say of their limits. Any other system is an outside app, as here. A connector of your own cannot be placed on our integration framework today (chapter 16).

What the three share.

Steps: the system sync

  1. The shop registers the sync as an application following POS Invoice and makes an integration user. Give that user these rights: the feed's log (read and write, to mark events done), POS Invoice read, Item and Item Price create and write, Stock Entry create, write and submit, Bin and Warehouse read.

  2. OUT: what your system changed goes to the shop. Your system numbers every change; the sync's cursor is the last number done. It handles each change, oldest first:

    • an item is created, renamed or disabled (never deleted: a booked sale points at it);
    • a price goes into the named price list;
    • a stock movement is a Stock Entry the shop books (Material Receipt in, Material Issue out). The sync never sets a quantity.

    Each stock write carries a key in its remarks, PP-SYNC <your system's own id> mv:<movement number>. The sync looks the key up before every write. The cursor moves only after the change is fully written, so a crash repeats that change and finds it by its key. ⚠ The key must contain your system's own id. Without it, the key can match another system's entry, and the stock is never written.

  3. IN: what the shop booked comes to your system, once each. Run the feed client's loop (chapter 5). For sale.booked and return.booked, read the invoice over the API and record it in your system, keyed on the shop's invoice number, so a second delivery writes nothing. A return links to its sale. Follow POS Invoice only, so the day's merged back-office invoice never counts a sale twice.

  4. Reconcile every night. The check only reads, on both sides. It names each difference: a sale on one side only, a total that differs (both figures), an unlinked return, an item the sync never pushed, a price that differs, and stock on hand on each side:

    SYNC_KEY=… SYNC_SECRET=… python3 samples/s7_system_sync/sync.py reconcile --base … --app … --erp … --state … --warehouse … --company … --since "2026-10-04 00:00:00"
    
  5. Run it:

    SYNC_KEY=… SYNC_SECRET=… python3 samples/s7_system_sync/sync.py run --base https://shop.example --app "My Sync" --erp erp.sqlite --state sync.sqlite --warehouse "Store - X" --company "My Company" --watch 30
    

    One run pass pushes OUT, then collects IN. Run one instance per system per shop: the sync takes no lock.

Steps: the estate roll-out

  1. Write estate.json: the whole estate as data, kept in your own repository. It holds:

    • regions (a price list, prices, one promotion, a manager each);
    • stores (a code, a region, a receipt format, a cashier);
    • the reference profile new till profiles are cloned from;
    • the warehouse defaults every store carries;
    • estate-wide switches and reason codes.
  2. Run its four verbs.

    • plan shows every change, object by object, and writes nothing. A blocker, such as a missing receipt format or role, is named, and apply refuses until it is gone.
    • apply makes the shop match the file and is idempotent. It deletes any user permission an estate user holds that the file does not name.
    • verify reads back the fields the file owns and exits 1 naming every record edited by hand and every record with the estate's prefix that is not in the file. A field the file does not own can be edited without a finding.
    • remove takes the estate out in reverse order.
    PP_BASE=https://shop.example PP_KEY=… PP_SECRET=… python3 samples/s8_estate_rollout/rollout.py plan|apply|verify|remove --estate estate.json
    
  3. Know three facts about the shop.

    • A store's price list, cash customer, item tax template and currency live on its warehouse, not its till profile: the shop refuses a profile whose warehouse has no price list.
    • A till profile is cloned from the reference profile.
    • The till rings at the outlet its user is bound to, so assigning a cashier means binding the user to the store.
  4. Enrol the till devices as a separate step: a user the roll-out makes has no password and no enrolled device.

Steps: the BI feed

  1. Register a read-only application. Its key needs the feed's log (to mark events done), and read on POS Invoice and POS Closing Entry.

  2. Pick how it picks up.

    • run follows the event feed. Each invoice is read over the API and replaces its rows, in one transaction with the seen-set.
    • backfill re-reads invoices modified since a per-record-type watermark, to heal a gap at the end of the file.
    • rebuild is a full refresh into a fresh file.
    • export writes CSV, the same bytes for the same data.
  3. Check the day against the shop's own report:

    BI_KEY=… BI_SECRET=… python3 samples/s9_bi_feed/bi_feed.py control --base … --warehouse state/bi.sqlite --since "2026-10-04 00:00:00"
    

    For each POS Closing Entry it adds up the booked invoices the closing lists and compares with the closing's total. A cancelled sale stays in the warehouse as cancelled and is never revenue.

Limits to know before you build

What is refused, and why

When your service is down, and when the till is offline

Technical notes

What the kit has proven

What it has not

Notes on the steps

See also

4. Configure first · 5. Outside apps and the event feed · 14. Testing and release · 16. Possible, not possible, limits

This page in the kit