S11 — price-change approvals with a nightly export

A price-change request is made, approved by a manager and applied once by an outside service; the shop's price row then shows the approved price.
A price change that needs a manager's yes before it reaches the shop's prices. This sample is for a developer who wants an approval step in iVendNext without building a screen: the shop's own workflow does the approving, and a small outside service applies the result.
When to use it: a change must be requested, approved by someone else, and only then applied, with a record of who did what.
What happens
- A requester asks for a price change: the item, the price list, the new price and the reason.
- A manager approves it. A requester cannot approve, and no one should hold both roles.
- An outside service (
approvals.py apply) applies the Approved request through the record API, once, with its own key. - The trail shows who approved and that the service applied it.
- The shop's price row now holds the new price.
The request moves Requested → Approved → Applied by a native workflow on the partner's own record type (manifest.json). approvals.py export writes the day's requests to a file; schedule it to run each night.
What it looks like
The approver is shown as Pieter Botha; the item and the state names are the sample's own placeholders (PP-APR-ITEM, PP APR Approved).
| Step | Frame | Screenshot |
|---|---|---|
| 1. A requester asks: the item, the price list, the new price, the reason | Desk, 1440 wide | ![]() |
| 2. The approver approves | Desk | ![]() |
| 3. The service applies it, once | Desk | ![]() |
| 4. The trail: who approved, and that the service applied it | Desk | ![]() |
| 5. The shop's price row: 120.00 | Desk | ![]() |
The files
| File | What it is |
|---|---|
manifest.json |
the record type, numbering, change tracking, the workflow, every right written out |
approvals.py |
apply and export (standard library), with the guard against a request whose price changed after it was raised |
tests/test_s11.py |
15 laptop tests against an in-memory shop |
Recipe: recipes/approvals.md.
What is refused, and why
- A request whose price changed after it was raised — by the approver before approving, or by anyone after. The shop accepts such an edit from anyone with write, but the service refuses to apply it. Only the price as raised and approved is applied.
- A request approved by the person recorded as raising it, or with ten or more recorded changes. The service refuses it: the shop's own self-approval check covers only the Approve action, and the shop shows only the last ten changes.
- A price changed by anyone but this service. The approval binds only the service: take write on price rows from every other role, and fence the service's user to its own price lists (the recipe's Know these limits).
- A permission row that leaves a right out. The shop treats a right that is left out as ON, so the manifest checker refuses such a row. Write every right out.
Technical notes
What was walked, and where
The GIF: frame: Desk, 1440 wide. Walked on our test bench on 2026-10-07, iVendNext POS app at 8dc36aab8. Built from the same screens as the stills; every frame is cut above the activity lines.
Each step was taken by that person's own key (S11's bench driver), and photographed in Desk between steps. That a requester cannot approve is S11's tests', not step 2's shot.
What was tested
| Where | What |
|---|---|
tests/test_s11.py |
15 laptop tests against an in-memory shop |
| (our test bench) | the same flow against a real shop: 29 checks, passed; and the nightly export run by its cron line |
Measured: the shop accepts an edit of the price after approval; the service refuses to apply it. A permission row that omits a right gets it ON, so the manifest checker refuses such a row.




