iVendNextDevelopers Request a sandbox

S40 — Expiry and recall (pp_rcl): recalls, disposal on the record, goods-in

A pharmacy, a grocery or any shop that sells lots with an expiry date. Four jobs, one small service:

Kit sample S40; its App library copy is ivendnext_rcl. Part of iVendNext's pharmacy standard version, and useful to any shop that sells by lot.

What happens

When What happens
Block these batches on a recall notice each lot gets disabled (the till's automatic lot read drops it at once), except a lot of an item with Has Expiry Date and no expiry date: that one is left as it is and named, because saving it would make the shop write a guessed date into it (or refuse); the notice gets each store's stock of the lots, read from the stock ledger, and, at consumer depth, the customers supplied (a till sale, a Desk invoice, a delivery; a till sale is listed under its POS sale until its day closes, then under the day's merged invoice, never twice); a Store Progress record per selling store holding the lots (not the quarantine stores); the clock (start by, complete by) from the class's settings where the notice has none. "Blocked 2 lots. 1 not blocked: no expiry date (enter the pack's, then press again). Stock found in 2 stores; 3 sales to 3 customers."
the same press again nothing is written to a lot again; the stock and customers are read afresh (a sale that synced from an offline till since shows up); a store's own status and quarantined quantity are kept
Refresh on a worklist its lots near expiry (with stock, within the window, per store, past-dated included) and its medicine lots with no expiry date, each marked when a recall notice names it (that lot stays blocked); the action the pharmacist chose on a lot is kept
a goods receipt is submitted (Desk, import, API) refused: a lot with fewer days left (counted from today, or the posting date if later) than the item's or the shop's minimum, unless its line has a Letter of Undertaking for at most the days it has; an expired lot, always; a Letter of Undertaking with no days; a lot of an item with a shelf life until Pack expiry is typed on its line (the shop can work such a lot's expiry out, and a later edit leaves no trace), or one that does not match it; a line with no lot (a line rejected whole is not judged); no supplier's invoice number when the settings ask. By the ERP connector's own user: submitted, the problems written into Receiving check
a stock entry is submitted refused when it disposes of a witnessed item without a Disposal Record for the line's store whose visit is recorded on or before the posting date, that is locked, and whose lines still cover the lot and quantity (in the stock unit) after what earlier issues drew on it. A disposal is stock of the item written off (an issue, a consumption, a repack or manufacture whose finished line is not itself a witnessed item), or stock leaving a quarantine store, or a blocked or expired lot, for anywhere but a quarantine store. With no quarantine store in the settings, every such disposal is refused, saying so; other movements go on
a stock entry is posted by the ERP connector's own user (SAP brings its receipts and disposals as stock entries, every few minutes) not refused for what it holds: its incoming lots are judged as a goods receipt and its disposals as above, and the problems are written into the entry's ERP check field (it is refused only when the app itself cannot run: no settings, or its user refused). A Desk Material Receipt (opening stock, adjustments) is not judged: goods-in is the purchase receipt
a stock count is submitted refused when it lowers a witnessed item's stock in a quarantine store, or of a blocked or expired lot: a count never disposes; issue it naming its Disposal Record

What the shop does

  1. Install the manifest. It installs switched off.
  2. The app's user: give the role PP RCL Integration to the user the service works as, with read on Item, Serial and Batch Bundle, POS Invoice, Sales Invoice, Delivery Note, Stock Entry and Retail Save Rule, and read and write on Batch (it writes disabled only). Make its API key, choose it on the feed PP RCL Recall feed (leave the feed switched off) and press Connect again.
  3. The settings record Default: your site's time zone (hours from UTC); the minimum shelf life at receipt; whether the pack's expiry is required where an item has a shelf life (untick it, and the supplier's number, if you receive on Handheld, which can send neither); the near-expiry window; the quarantine stores; the ERP connector's own users (never a person's login); each class's clock and depth (the defaults are Kenya's PPB table: 24 h / 14 days, 72 h / 21 days, 5 days / 28 days); and who needs the regulator's visit (by the items' schedules: Pharmacy counter rules' field by default; Every disposal; or No disposal).
  4. People: give PP RCL Recall Officer to whoever runs recalls and disposals.
  5. Switch on: the five services (read what each sends, tick the consent), the two buttons, and the five save rules on the Disposal Record. ⚠ Until the lock rule PP RCL disposal locked once the visit is recorded is on, every disposal is refused: a record that can still change proves nothing.
  6. The prints: in the print view, choose PP RCL Recall Report for a notice (the report for the regulator) and PP RCL Disposal Record Print for a disposal, and set each as the default (Customize Form, Default Print Format: a manifest may not set it).
  7. Daily list of lots with no expiry: the report PP RCL Lots with no expiry date by e-mail, daily (the shop's own Auto Email Report).

The files

File What it is
manifest.json five record types and six tables, seven fields on the shop's records, two Desk buttons, three document checks (receipt, stock entry, stock count), a workflow, five save rules, two reports, two prints, a reminder, the feed registration
recall.py the pure core: the signature, the recall's outcome per lot, stock per store, customers supplied, the clock, the worklist, the receiving and disposal rules, the answers
service/app.py the service: five routes, one per door, each signed with its own secret; the shop's record API as the app's user
app.json hosted as a function, paid per call
tests/ 98 laptop tests against a stand-in shop, and 63 canaries (canaries.py): each guard's test goes red when its check is removed

Run it

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

One shop's .env.local: PP_BASE, PP_KEY, PP_SECRET (the app's user) and the five secrets the install handed back once, PP_SECRET_BLOCK, PP_SECRET_WORKLIST, PP_SECRET_RECEIPT, PP_SECRET_DISPOSAL, PP_SECRET_COUNT.

Many shops, hosted

IVN_LIBRARY_APP=ivendnext_rcl with a shops table (IVN_SHOPS_TABLE or IVN_SHOPS_FILE), and RCL_PREFIX=IVENDNEXT RCL: each shop connects itself (the app template's handshake, guide chapter 14b), each call is checked with that shop's own secret for that door, and read and written with that shop's own key. The service keeps nothing between calls.

Make it your own

What a partner usually changes:

Before you copy it

Technical notes

Why it is built this way

Bounds, stated

Needs this serves

This page in the kit