Files
cproudlock a1802a2bf5 Match the number on the bin, and let an unset kiosk say so
The last two kiosk findings.

Keypad entry collected EVERY digit in an item's code, and the shipped prefix
contains one: '3DP0004' collapsed to 30004, so an operator typing the 4 printed
on the label was told no part matches that barcode and had to work out that the
leading 3 belonged to the prefix. The number on a label is the number at the
end, so the trailing digit run is what is compared now. An ambiguous number is
still refused rather than guessed - taking from the wrong bin is worse than
being asked to scan the label.

A parts kiosk that had never been provisioned drew its normal "Scan the barcode
on the bin" screen and answered "Not found" to every scan, which reads as "we
do not stock that part" rather than "this screen was never set up". The server
cannot say which: the routes answer a bare 404 on purpose, so a caller without
the device token does not learn the door exists, and that stays. But the BROWSER
knows whether it holds a token, and saying so discloses nothing - an
unprovisioned screen now says it is not set up and how to fix it.

For the case the client genuinely cannot distinguish - kiosk switched off versus
a part that is not stocked, both a 404 by design - three misses in a row add a
line suggesting the kiosk may have been switched off. Truthful without the
server having to leak anything.

Three tests, verified by planting the all-digits match back.

Scope: product
2026-09-03 17:00:49 -04:00
..

Printedparts plugin

3D-printed parts inventory + kiosk checkout

This plugin was generated by flask plugin new printedparts. It satisfies the framework contract out of the box. Replace the example model and routes with your domain.

What's here

  • plugin.py - the PrintedpartsPlugin class extending BasePlugin. Edit init_app for custom setup, on_install to seed reference data.
  • models/printedparts.py - example Asset extension table. Replace examplefield with your domain fields.
  • api/routes.py - example list and detail endpoints. Add CRUD as needed.
  • schemas/__init__.py - marshmallow schema stub for request/response validation.
  • tests/test_plugin.py - smoke tests asserting contract compliance.
  • manifest.json - plugin metadata. Bump version on changes; keep core_version range broad.

Common edits

You want to... Do this
Add a hook (search, navigation, dashboard widget) Override the method in PrintedpartsPlugin. See the PLUGIN-HOOKS wiki page.
Accept external collector data Override get_collector_schema() to return a JSON Schema. See ADR-006.
Add another model Create models/<other>.py, export it in models/__init__.py, return it in get_models().
Add a CLI command Override get_cli_commands() returning a list of Click commands.

Frontend

Vue components for this plugin live under frontend/src/views/printedparts/ (per project convention). Backend scaffolding does not generate frontend yet; copy from an existing plugin's view files (e.g., frontend/src/views/network/) as a starting point.

Install and run

flask plugin install printedparts
flask db migrate -m "Add printedparts plugin tables"
flask db upgrade
pytest plugins/printedparts/tests/

References

  • the PLUGIN-HOOKS wiki page - canonical hook reference
  • the PLUGIN-QUICKSTART wiki page - 30-minute walkthrough
  • migrations/adr/ADR-001-asset-as-platform-contract.md - the platform contract
  • migrations/adr/ADR-002-plugin-versioning.md - versioning rules

Why the kiosk take endpoint is unauthenticated

POST /api/printedparts/kiosk/take is the product's first open WRITE (every other kiosk endpoint is a read). Accepted deliberately, against the criteria in docs/proposals/printedparts-plugin.md:

  1. Decrement-only: it can reduce stock of an active item, nothing else.
  2. Fully attributed: it refuses to act without a badge that resolves under the site policy; every action lands in the ledger with SSO + name + time.
  3. Bounded blast radius: worst case is stock counts driven low - visible in the ledger and reversible with an adjust.
  4. Physically rate-limited: it serves a touch screen on the shop floor; nothing enumerable, nothing worth scraping.

Any future open-write endpoint must clear the same bar.