S8 — an estate roll-out as code: fifty stores and three regions from one file
Your whole estate — regions, prices, stores, tills, cashiers — written down in one file, and a script that makes the shop match it. This sample is for a partner or head office that sets up many stores and wants each change previewed, applied and checked, not clicked through store by store.
When to use it: you open or change stores in numbers, and need to see what will change before it does and prove afterwards that the shop matches the file.
It is applied over the record API by a script, standard library only; written up as a recipe: recipes/estate_rollout.md.
The files
| File | What |
|---|---|
estate.json |
the whole estate as data: 3 regions (a price list, prices and one promotion each), 51 stores (a warehouse, a till profile cloned from the reference profile, a receipt format, a cashier), managers, the reason switch and a reason code |
generate_estate.py |
writes estate.json (only so the sample can hold 51 stores without anyone typing them) |
rollout.py |
plan (what would change, nothing written) · apply (idempotent) · verify (reads everything back; RED on a hand edit or an orphan) · remove |
tests/test_s8.py |
10 laptop tests against an in-memory shop |
How to run it
PP_BASE=https://shop.example PP_KEY=… PP_SECRET=… python3 samples/s8_estate_rollout/rollout.py plan|apply|verify|remove --estate estate.json
planlists every change the file would make, by store and by field. It writes nothing.applymakes those changes. Running it again changes nothing.verifyreads everything back and compares it with the file. It is GREEN when they match. It is RED when someone edited a record by hand, or a record the file does not name is left over, and it names the store and the field.removetakes the estate out again.
There is no screenshot. Desk would show fifty-one stores as a list, but not what plan and verify say: what differs from the file, by store and by field. That is what an estate owner needs before and after a change.
Before you copy it
- The script keeps its state in a
state/folder. - A store that a sale points at is retired (disabled), not deleted, when you remove the estate.
- It has been tried on a test shop with one real store. The other stores were made for the test and taken out after. One sale was rung at one of them; no till ran against the others.
Technical notes
Why it is built this way
Why this way (R6). Click each store through in Desk — rejected: 51 stores, nothing to repeat or to check. A manifest — rejected: a manifest may not touch a shop's own stores, users or prices. One file, one diff engine for plan, apply and verify (picked, ~330 lines): the same comparison prints the plan, drives the writes and judges the read-back, so they cannot disagree. State is kept in state/ (excluded from the programme's rsync --delete).
Bounds, stated
Bench limits, stated. The bench has one real store. The "51 stores" are warehouses and till profiles made for the test and taken out after; one sale is rung at one of them; no till runs against the others. A record a sale points at is retired, not deleted (set 1, F7).
What was tested
| Where | What |
|---|---|
tests/test_s8.py |
10 laptop tests against an in-memory shop |
| our test bench's own run (not in the kit) | the bench tests and the money-kit run with the estate applied |
What our test bench's run printed (2026-10-07, 33 of 33 checks; trimmed):
PASS T1 plan exits 0 and lists every object the file names (3 price lists, 9 prices, 51 warehouses, 51 profiles, 3 promotions, 54 users, their permissions, 1 reason code)
PASS T1 …and wrote nothing: the shop's counts are the same after the plan
PASS T2 verify is GREEN: every object read back equals the file, no orphan
PASS T3 the second apply creates nothing, updates nothing, deletes nothing
PASS T4 the plan lists exactly two rows, both Item Price rows of the South price list
PASS T5 verify is RED and names the profile and the field the hand edit changed
PASS T5 apply repaired only that profile
PASS T7 …and ONLY that store is left: one profile, one warehouse (both disabled); the other 50 stores are deleted