iVendNextDevelopers Request a sandbox

S54 — Sell-through by size (pp_sst): which sizes and colours sell, store by store

Start from this sample: python3 tools/manifest/start.py s54_sell_through --prefix "ACME STR" copies it into apps/ of your repository under your own prefix (your company's first word, then a short name for the app) and checks it (how).

A fashion retailer's buyer wants to know which sizes and colours of each style sell out and which sit on the shelf, store by store, so the next order and the next transfer follow the sizes that sell. A buyer names a range, the stores and a start date, and presses a button: the app writes, for each style, colour, size and store, the units sold at the till since that date, the units still in stock, and the sell-through. It is the worked sample of the recipe reading the retailer's own reports: both figures come from the retailer's own reports, never from a copy.

When to use it: the retailer sells styles in sizes and colours (clothing, footwear, sportswear) and wants sell-through per size in each store, from its own till sales and stock.

What happens

When What happens
A buyer makes a run A title; the item group (its own items, not its sub-groups) or one style in it; Sold since; the size attribute (such as Size) and the colour attribute (such as Colour, or none); one to 20 stores
The buyer presses Work out sell-through The service reads the run, the two attributes' own order of values, the group's items with their size and colour, and for each store the retailer's POS Sale Report and Stock Projected Quantity. It writes one row per style, colour, size and store that sold or holds stock, and a summary line, and says how many rows it wrote
A row Sold: the till's units sold since the date, returns taken off. In stock: today's stock, less any till sale whose stock has not left yet (the report's Reserved for POS). Sell-through %: sold divided by sold plus in stock. None left: in stock is 0 or less
The order of the rows By style; colours and sizes in their attribute's own order (XS, S, M, L as the retailer listed them, never the alphabet's); stores in the run's order
The buyer presses again The rows are replaced with today's figures
A run without a store, a start date after today, more than 20 stores Said in words; nothing is read or written
A group with no variants carrying the size attribute Said in words; nothing is written
More than 1,000 rows Said in words (name one style, or fewer stores); nothing is written
Reads that take the press's time Said in words (name fewer stores): a style narrows the item read only, while each report reads every item the store sold or holds; nothing is written
A report refuses the app's user, or is switched off Said in words, naming the report: check the app's role is on it, every right the setup names, and that the report is on
A report moved to the background (a prepared report) Said in words, naming it: untick Prepared Report on it
The app's user refused by the retailer's iVendNext Said in words: check the app's user, its role and the report rights
The service down The button's own words: Work out sell-through did not answer in time. Check the record before you try again. Nothing is written
What the buyer sees (on a demo site, 2026-10-29)
What the install will change, read before you consent
A run for two stores after a press: the sizes in their attribute's order, the till's sales at one store against the stock in both
The report of every run's rows

What iVendNext does

  1. Install the app from the App library (or paste its manifest into Retail Extension Package in Desk), read what it will do and consent. It comes in switched off: its service has no consent and is off, and its button is off. The install adds the record type PP SST Sell-through (with its tables of stores and rows) and the report PP SST Sell-through by size, and makes the role PP SST Integration.
  2. Give the app's user its reports and reads. From POS 2.1.0.667 the install made the app's own user, pp_sst@integration.invalid, and named it on PP SST Sell-through feed. It holds the role PP SST Integration alone and works through the API only; give it no User Permissions. In Role Permission for Page and Report, add that role to the reports POS Sale Report and Stock Projected Quantity. In Role Permissions Manager, give it Read and Report on POS Invoice; Read on Item, Item Attribute, Warehouse and UOM; and Select only on POS Profile, Item Group, Customer and Customer Group: a new row in Role Permissions Manager comes with Read ticked, so untick it there (Read on Customer would let the app's user read every customer). A report checks its reader's rights on every record type its filters and lookups name, and these are the least both reports answer with (measured on our test bench, one right at a time). The install gives the role its rights on the app's own record types. A service for one retailer: press Generate Keys on that user (User, API Access) and put the key in the service's settings. A service for many retailers is handed the key by the retailer's site itself; press Connect again on the app's record only if the site could not reach the app, or a key was lost or changed.
  3. Your buyers are your Stock Managers and Sales Managers: they keep the runs and press the button (a System Manager too). In Role Permissions Manager, give both roles Select only on Item Attribute and Warehouse (untick the Read a new row comes with): the run's size and colour attributes and its stores are picked from those, and neither role has them by default (measured on our test bench: the form's search refused both to each role alone).
  4. Switch the service and the button on: Retail Extension Subscription, the record Sell-through: read what it is sent, tick the consent, switch it on. Then Retail Desk Action, the record Work out sell-through: switch it on.
  5. Test it: make a run for one item group, one store and the first of this month, and press Work out sell-through. The rows must match what POS Sale Report (report type Item) and Stock Projected Quantity show for that store.

Where it shows. In Desk: the runs' list and form, with the button on the form; the report PP SST Sell-through by size. At the till: nothing new.

What leaves the retailer's iVendNext. On each press, the retailer's server sends the service the pressing user's login and language and the run's name. The service then reads, with the app's user's key: the run, the size and colour attributes, the group's items (code, style and attributes), and for each store POS Sale Report (store, item, the sale amounts and units sold; the service keeps the units only) and Stock Projected Quantity (the item's stock figures). It writes the rows and the summary on the run. No customer is read. It keeps nothing of a call. A hosted copy holds each retailer's press secret and the API key of the app's user until the retailer removes the app. Say so on your card.

The files

File What it is
manifest.json the record type PP SST Sell-through (numbered SST-#####) with its tables PP SST Store and PP SST Row (the rows are read only: the integration role writes them), the report, a help address, a feed registration (it names the app's one integration user), one desk_action service and the button Work out sell-through on the run's form for Stock Manager, Sales Manager and System Manager; installed switched off
sst.py the pure core: the press's signature (verify) and run (run_of_press), a run (run_of), an attribute's order (order_of), the group's variants (variants_of), the two reports' answers read by their column names (sold_of, stock_of), the rows (rows_of), the sell-through (sell_through), the summary (summary_of) and the press's answer (message)
service/app.py the service, standard library only: POST /sst/work (the press), GET /health, and the app template's handshake for many retailers; one handle answers the web server and the function entry (lambda_handler) alike
Dockerfile · app.json the settings file for hosting ("runs": "function": on CitiXsys hosting it is a function, paid per call) and an image for your own server (built from the kit's root)
tests/ the core, the service over the wire with signed presses (one retailer and many), iVendNext's reads and writes against a stand-in iVendNext, the manifest; canaries.py breaks each guarded rule and expects its test to fail

Run it

python3 -m unittest discover -s samples/s54_sell_through/tests -p 'test_*.py'
python3 samples/s54_sell_through/tests/canaries.py
python3 samples/s54_sell_through/service/app.py samples/s54_sell_through/service/.env.local

Settings, one retailer (service/.env.local, never committed): IVN_WORK_SECRET (handed back at install as pp_sst:sub:work), IVN_SHOP_URL, IVN_SHOP_KEY (<api key>:<api secret> of the integration user), PORT. SST_PREFIX names the record types (default PP SST).

Before you install: every host.docker.internal address in manifest.json (in allowed_urls, and where the manifest calls it) is our test bench's own address for the sample's service, and it resolves only where Docker provides it. Replace it with your own service's address first, or iVendNext's presses never reach your service; if this manifest is already installed, raise its version and install it again (the manifest recipe, step 6).

Many retailers, hosted

The service is the app template's shape: set IVN_LIBRARY_APP and a store (IVN_SHOPS_TABLE on CitiXsys hosting, else IVN_SHOPS_FILE). Each retailer connects itself after its install and hands over the press's secret and the key of the app's own user (Connect again is only for when the site could not reach the app, or a key was lost or changed). Until the key arrives a press is answered in words. Every press names its retailer's iVendNext (X-iVendNext-Site), so each retailer is read with its own key.

Make it your own

What a partner usually changes:

Before you copy it

Technical notes

Why it is built this way

Option Cost Verdict
Do nothing 0 the buyer exports two reports and joins them by hand, by item code, with no size order
Stock Balance with Show Variant Attributes, by store configuration stock by size per store, but its Out Qty counts transfers as sales, and there is no sell-through
A manifest report only — a manifest's report reads one record type: no join, no sum, no ratio
A container recomputing every night about the same code, and a container's monthly cost a press when the buyer asks does the same at no standing cost
A press that reads the retailer's two reports and the items, and writes the rows on the run about 400 lines of core and service with their docstrings, and about 600 of tests picked: kit doors only, a function paid per call; S49's press shape reused

The answer, exactly

Bounds, stated

Needs this serves

This page in the kit