Skip to content

mrp — the system of record for EmbeddedEra

Requested 2026-09-11. One place for parts, stock, suppliers, purchase orders, receiving, BOMs, build orders, serial numbers, test results, sales orders, invoices — later accounting. Multi-user with logins, on a subdomain, run on the Plex box. Business: embeddedera-business · docs: growth-plan (1.6) · site: embeddedera-site.

Why

Three products are sold today with manual invoicing and no stock record. Every quote, build and invoice re-derives facts that should be looked up. An MRP turns "how many do I have / what do I need to order / which unit went to whom and did it pass" into queries. It also gives helpers a login and a dashboard instead of questions.

Non-negotiables

  • Self-hosted, free, open source, Docker, in the torrentvpn stack → nightly backup + parity + restic.
  • REST API with tokens — the box drives it (quotes in, test results in, invoices out).
  • Multi-user with roles. Reachable as erp.embeddedera.com through a Cloudflare Tunnel with Cloudflare Access in front (email OTP / Google login; free ≤ 50 users). No open ports.
  • Electronics-grade: part numbers with manufacturer/supplier parts, BOMs from KiCad, build orders that consume stock, serial numbers per built unit, pass/fail test results per serial.

The data model we need (whatever the tool calls it)

Part (internal PN, description, category, units) → Supplier part (MPN, supplier SKU, price breaks) → Stock item (qty, location, batch/serial, cost) → BOM (part → sub-parts × qty) → Build order (make N of assembly → allocates and consumes stock → creates serialised stock) → Test result (serial, test name, pass/fail, value, date; source = Universal Tester) → Sales order (customer, lines, ship) → Shipment (serials shipped) → Invoice → Payment → Accounting.

Integrations (all through the API, all on the box)

  1. Universal Tester → test results. The station posts {serial, test, result, value} per unit the moment a test finishes. A unit cannot ship unless its required tests are green.
  2. KiCad BOM → BOM import. CSV from the KiCad BOM export; part matching by MPN.
  3. Website quote → sales order / proposal. A [EE- request becomes a draft customer + sales order; new-proposal reads it; the proposal PDF renders from the order.
  4. Invoice PDF. Our render-doc template renders from the ERP's invoice data, so the paperwork stays on-brand even if the tool's own PDFs are ugly.
  5. Low-stock → purchase list. Nightly: parts under minimum → draft PO grouped by supplier, ntfy to Angel; PO PDF from the same template.
  6. Accounting. Invoices/payments/purchases export to the bookkeeping tool (Firefly III or a CPA CSV) — decided separately in embeddedera-business.

Complete capture — everything the system must hold (so nothing breaks later)

Written 2026-09-11 after Angel asked "what am I missing". ✓ = InvenTree has it natively; ⚠ = InvenTree partly / by convention; ✗ = lives elsewhere (vault, repo) and is linked.

A. Master data — decide these FIRST, they are hard to change

  • Part-number scheme ⚠ (decide once). Proposal: EE-<class>-<seq> — classes A assembly, P PCB (bare), C component, M mechanical, K consumable, S software/firmware image, D document. e.g. EE-A-0001 (product), EE-P-0001 (its bare board), EE-C-0042 (a resistor). Revisions as a suffix on assemblies and PCBs only: EE-P-0001-B. Never reuse a number.
  • Part categories tree ✓; units of measure ✓; default location per category ✓.
  • Supplier parts with MPN, supplier SKU, price breaks, MOQ, lead time ✓; manufacturer parts ✓.
  • Alternates / substitutes per BOM line ✓ — the obsolescence service depends on this.
  • Revisions ✓ (InvenTree part revisions) — board rev A/B/C; BOM per revision.
  • Customers with bill-to / ship-to, payment terms ✓ (basic).
  • Locations tree ✓: e.g. Shop / Shelf A / Bin 01, plus process locations Receiving, Quarantine, Kitting, In build, In test, Finished goods, Shipped, Scrap.
  • Consumables ✓ with expiry dates ✓ (solder paste, flux, adhesives) and open-date.
  • MSL / humidity-sensitive parts ⚠ (part parameter + a "bake before use" note).
  • Lot / date codes on stock ✓ (batch field) — needed for traceability.
  • Firmware image as a part ⚠ (EE-S-… with the version as the revision) so a serial can record which firmware it shipped with (as a test result or stock parameter).

B. Documents attached where they are used

  • Datasheets on component parts ✓ (attachment). Gerbers/drawings/STEP on the PCB and assembly parts ✓. Stencil file on the assembly ✓.
  • Work instructions per assembly ✓ as an attached PDF and the part's notes field (Markdown). Steps: kit → stencil → paste → place → reflow → inspect → program → test → coat → pack.
  • SOPs ✗ authored in the vault/repo (versioned, reviewable), exported to PDF and attached to the relevant category or location: Receiving, Kitting, Build, Test, Shipping, Returns, Quoting, Invoicing, Stocktake. Version in the filename (SOP-RECEIVING-v2.pdf).
  • Test procedure per assembly ✓ = InvenTree test templates on the part (required/optional, expected values), results posted per serial by the tester.
  • Certificates / coating specs / supplier docs ✓ attachments on the supplier or PO.

C. Labels ✓ (InvenTree label templates + a printer plugin; QR codes built in)

  • Location label: QR + path (Shelf A / Bin 01).
  • Stock item label (printed at receiving): internal PN, description, MPN, qty, lot/date code, location, receive date, QR of the stock item.
  • Serial label (per built unit): assembly PN + rev, serial, QR, build date; second small label for inside the enclosure.
  • Shipping label / packing slip: from the sales order + shipment (carrier label separate).
  • Printer: any label printer (Brother QL, Zebra, Dymo) via plugin, or PDF to a sheet.

D. Processes (each becomes an SOP + the matching InvenTree action)

  1. Quote → order: website request → customer + sales order (draft) → proposal PDF → accepted → confirmed order.
  2. Purchase: low stock or a build's shortfall → PO by supplier → send PDF → receive: count, inspect, label, put away; short/wrong → note on the PO line, quarantine location.
  3. Build: build order → allocate (kit) → work instruction → complete → serials created → stock moves to In test.
  4. Test: tester posts results per serial; failing units → Quarantine with a note; rework → re-test. Ship gate: required tests must pass.
  5. Ship: allocate serials to the order → packing slip + labels → shipment with tracking → stock Shipped.
  6. Invoice: from the shipped order → render-doc PDF → sent → payment recorded.
  7. Returns / RMA ✓ (InvenTree return orders): receive back → test → repair/replace/credit.
  8. Stocktake / cycle count ✓: monthly for A parts, quarterly for the rest.
  9. Change control (ECO) ⚠: a revision bump + note on the part, obsolete the old one; the EOL/NRND check ties in here.
  10. Scrap and rework ✓ stock adjustments with reason codes.

E. Traceability and quality (what an ISO 9001-style system expects, all free here)

  • Lot → build → serial → customer chain ✓ (recall capability).
  • Firmware version per serial ⚠ (see A).
  • Per-serial test record with values, not just pass/fail ✓.
  • Non-conformance log ⚠ (notes + quarantine location + a "NCR-" attachment; revisit if volume grows).
  • Equipment calibration/maintenance ✗ for now (tester, oven profile checks) — a vault note with dates; move into the system only if audits need it.
  • Audit history ✓ (InvenTree tracks who changed what).
  • Training records ✗ (vault) — only if helpers do builds.

F. Costing ✓

BOM cost roll-up from supplier prices; build cost; unit cost on stock; margin per sales order. Labour as a consumable part (EE-K-LABOR, unit = hour) if you want it in the roll-up.

G. Access and safety

Roles: Owner (Angel), Assembler (stock, builds, tests; no prices), Purchasing (POs), Read-only. Cloudflare Access in front; nightly backup (stack dir) + parity + restic; API tokens per integration, each with the minimum scope.

First hands-on path for Angel (after phase 1 is up)

  1. Create the categories and the three process locations. 2. Create ONE product as an assembly (EE-A-0001), its bare PCB (EE-P-0001-A), and 5–10 of its components with MPNs. 3. Build the BOM by hand for those lines. 4. Receive stock for them (with a printed label). 5. Raise a build order for 2 units, allocate, complete → two serials. 6. Add a test template ("Power-on", "LED check") and record a result by hand. 7. Create a sales order, ship one serial. Then Claude wires the tester, the KiCad import and the PDFs to what you built.

Phases

  1. Stand it up (Claude): container in the stack, subdomain + tunnel + Access, Angel + 1 helper login, backups verified.
  2. Seed the data (Angel + Claude): the 3 products as parts with BOMs; current stock counted once; suppliers; the KiCad BOM import proven on one product.
  3. First real loop: a build order for one product → serials → tester posts results → sales order → shipment → invoice PDF. Manual invoicing stops here.
  4. Automations: low-stock POs, quote → order, weekly stock/aging report into the vault.
  5. Accounting hook-up.

Tool: InvenTree — see mrp-tool

Electronics-native, free per-serial test results, ~1–2 GB RAM, REST API, weekly releases. Invoices/proposals render from its sales orders through our render-doc template; bookkeeping stays separate. ERPNext is the fallback if the company outgrows it.

Open

  • Chase the LCSC API access request if there is no reply — sent to support@lcsc.com on 2026-09-24 with SOSID 2827835, the annual report and an end-user (not reseller) statement. It unblocks creating supplier parts without a browser: part-lookup returns MPN, price, stock and lifecycle but not the distributor SKU, which SOP-002 requires, and that gap is what keeps ~140 components off any purchase order. Worth checking first whether the Digi-Key and Mouser APIs already configured in ~/.config/kicad-libsync.env can return their SKUs — that may solve most of it without waiting. If granted: key 0600 at ~/.config/lcsc/env and in Vaultwarden (Hard Rule 4, SOP-020), then extend part-lookup with LCSC part numbers. If refused or ignored: read SKUs off the LCSC packing-list PDFs attached to shipping emails (Angel hands them over; C306880 came from one). Known: C306880 = Omron B3SL-1002P (EE-C-0193, 6x in Cow Finger, 5x in Cow Remote); C2847904 = STM32G0B1CBT6 (EE-C-0098, from a back-in-stock email, not an order).

  • Print-template sources live in two places: ~/torrentvpn/inventree/templates-src/ (reports + the 62×29 and 25×25 labels, installed by erp-templates) and ~/Notes/hardware/labels/ (the Zebra 2×1.5 in labels, uploaded by hand with curl). Move the Zebra ones into templates-src/, add them to erp-templates, and drop the special case from SOP-029 step 1 — one home per file. See ~/Notes/tasks.md — this section is now generated from the task register.

  • Stock task #193 (USBLC6 × 3000) duplicates #192. Angel: a repeat count, leave it; skip it in the Stock count queue so it doesn't flag as stale after 14 days.

  • EE-M-0002 stencil (pk 215): photo and thickness. SOP-002 requires an image; photograph the stencil. Record its thickness from the stencil or the JLC order (W2026082710026585, the 1-piece line).
  • EE-K-0002 solder paste (pk 216): photo (SOP-002 needs an image), and add a DigiKey/Mouser supplier part when it's next bought. Watch the expiry: lot A260188 expires 2027-03-17.
  • Robot Cow packing materials: label roll MPN and photos. EE-K-0003 Zebra 2×1.5 in label has no MPN yet; EE-K-0003/0004 need images (SOP-002). The TZe-641 cassette in the PT-P900 isn't counted.

Log

Moved out: Log entries dated 2026-09-11 to 2026-09-28 are in mrp-archive; newer entries and the dated sections follow.

2026-09-24 — what actually happened to the stock count

The Haiku intake pipeline was not the thing that stalled — it was barely used. Across every Kanboard project there is exactly one open task with intake_kind=stock, and it is marked stock_skipped. Zero items have ever been confirmed into the ERP through the /stock queue.

The 138 stock items that do exist were created by some other route, in bursts between 15 and 22 September (21 / 2 / 1 / 27 / 59 / 25 / 1 per day) and then stopping. So somebody counted steadily for about a week and stopped on the 22nd; the tool built for the job was not what they were using.

The one intake attempt worked well, which makes the non-adoption more interesting than a bug: task 193, USBLC6-2SC6-ES × 3000, decoded from an LCSC QR with mpn, supplier, description, quantity, lot code 0727-0004, date code 26+ and — notably — supplier part number C5180249. It was skipped because the part (EE-C-0155) and that LCSC supplier part already existed, so there was nothing to add. A reasonable skip, not a failure.

That last detail matters more than the count. The reel QR encodes the LCSC part number, so scanning physical stock yields supplier SKUs without any API. For parts we already hold, this closes the same gap the LCSC API request was sent for — see the LCSC item under Open.

Corrected coverage figures (previous notes said "~140 components need supplier parts", which was loose):

Active EE-C-* components 191
Purchaseable 182
Purchaseable with no supplier part 152
Components with stock counted 49
Supplier parts in the ERP 47

So the two gaps are real and roughly the same size: about 150 components cannot be ordered, and about 140 have never been counted. Neither is blocked on tooling that does not exist — the intake pipeline works and the ERP is ready. It stopped being done.

Cow Finger BOM completed to what is actually built, 2026-09-24. Angel described the real build: the switches, the SN label, the antistatic bag, and a label for the bag. Two consumable lines were missing and are now on it:

Ref Part Qty
SW1–SW6 EE-C-0193 tactile switch 6 LCSC C306880
PCB EE-P-0005-A bare board 1 JLCPCB Y12-7663019A, 160 in stock
SN LABEL, BAG LABEL EE-K-0003 Zebra 2 × 1.5 in 2 consumable, 3,700 in stock
BAG EE-K-0001 antistatic bag 8 × 12 cm 1 consumable, 170 in stock

Both consumables carry consumable=True, following the COATING line on the Cow Remote BOM.

The SN label stock was deduced, not guessed: the ee_serial_label.html template is 25 mm square, which cannot be printed on 18 mm Brother TZe tape, so it has to be the Zebra 2 × 1.5 in stock. Angel confirmed the bag label is the same stock, hence quantity 2.

Worth noting for build planning: 170 bags is the real ceiling on finished units, well below the 3,700 labels, and the switches still read zero because they have never been counted.

Cow Finger BOM corrected to the real build, 2026-09-24 (Angel).

Ref Part Qty Stock
SW1–SW6 EE-C-0193 tactile switch 6 pcs 800
PCB EE-P-0005-A bare board 1 pcs 160
SN-LABEL EE-K-0004 Brother TZe 18 mm 27 mm 23,946
BAG-LABEL EE-K-0003 Zebra 2 × 1.5 in 1 pcs 3,700
BAG EE-K-0006 antistatic bag 6 × 9 cm 1 pcs 200

Changes: the Zebra label went from 2 to 1; the 8 × 12 bag (EE-K-0001) was replaced by a new EE-K-0006 antistatic bag 6 × 9 cm, created per SOP-001 with 200 booked at Receiving to match where the 8 × 12 bags sit; 800 switches booked at Shop/Shelf A.

The Brother tape length was not guessed — Robot Cow (EE-A-0002-A) already carries an SN-LABEL line at 27 mm per label of the same 18 mm tape, and Angel's "same length as the remote" matched the only recorded figure in the system.

Corrected immediately after: it is one SN label at 27 mm, not two — so the two label lines divide by job rather than by count. The SN label is the 18 mm Brother tape (27 mm) that goes on the board; the bag label is the Zebra 2 × 1.5 in that goes on the antistatic bag. That also explains the two different label stocks, which had been the open question.

The BOM now answers the question it exists to answer: 133 buildable units, limited by the switches (800 ÷ 6). Boards allow 160, bags 200, tape 443, Zebra labels 3,700.

Gap spotted next door: the Cow Remote BOM (EE-A-0004-A) has a COATING line but no SN label and no bag, so it has exactly the shortfall Cow Finger had until today. Worth the same pass. Its label length is presumably also 27 mm.

2026-09-25 — Cow Remote BOM

Packing lines added, matching the pattern established on the Cow Finger (Angel: bigger bag, same tape, same bag label):

Ref Part Qty
SN-LABEL EE-K-0004 Brother TZe 18 mm 27 mm
BAG-LABEL EE-K-0003 Zebra 2 × 1.5 in 1 pc
BAG EE-K-0001 antistatic bag 8 × 12 cm 1 pc

Note the bags differ by product and are easy to confuse: the Cow Remote takes the larger EE-K-0001 (8 × 12), the Cow Finger the EE-K-0006 (6 × 9).

The BOM is 34 lines, every original line validated, and it still builds zero. The three new packing lines need validating.

Every zero-stock line has never been counted — 17 of them, and not one has ever had a stock record, so none of this is evidence of a real shortage:

EE-C-0105 EE-C-0186 EE-C-0047 EE-C-0048 EE-C-0187 EE-C-0132 EE-C-0111 EE-C-0188 EE-C-0189 EE-C-0190 EE-C-0191 EE-C-0192 EE-C-0101 EE-C-0098 EE-C-0037 EE-C-0194 EE-P-0004-A

That distinction matters: "never counted" and "counted and empty" look identical in a stock report and mean opposite things. Everything on this board falls in the first bucket.

Also open: 13 lines have no supplier part, 26 have no image, and the bare board EE-P-0004-A has neither supplier part nor stock — the same position the Cow Finger board was in before the JLCPCB details arrived.

STM32 duplicate resolved, 2026-09-25. Angel spotted it; the ERP held the 48-pin part under three numbers, which is why the Cow Remote reported a shortage on a chip he had 250 of.

Number What it really was Outcome
EE-C-0098 STM32G0B1C_B-C-E_Tx The KiCad family symbol — B/C/E flash, Tx package. No single MPN, so not orderable. Was the Cow Remote BOM line. Kept as the symbol record (SOP-008 owns the symbol↔IPN link), marked not purchaseable so it cannot reach a PO
EE-C-0145 STM32G0B1CBT6 Created by the KiCad symbol pull; empty Deactivated as a duplicate, not deleted (SOP-001 never reuses a number)
EE-C-0196 STM32G0B1CBT6 Created from purchasing; 250 in stock, LCSC C2847904, proper description Survives. Cow Remote BOM now points here

EE-C-0196 survives because it holds the stock and the supplier part — moving those would lose provenance for no gain. The KiCad symbol STM32G0B1CBT6 was repointed from EE-C-0145 to EE-C-0196 (kicad-lib 28d15d8), one property on one symbol, which is what kicad-erp-sync push would write.

The general lesson: parts arriving from two directions — the KiCad symbol pull and purchasing — produce duplicates that look distinct because one carries the drawing and the other carries the stock. Worth checking the rest of the library for the same shape. A BOM line naming a family symbol rather than a fitted device is unorderable by construction and reports a false shortage.

Checked first and worth recording: nothing pushes a BOM into the ERP (erp-bom-export only reads), so an ERP-side BOM correction is durable and will not be overwritten by a KiCad sync.

Cow Remote now: 7 lines still uncounted, buildable still 0, limited by EE-C-0105.

Cow Remote count finished, 2026-09-25 — all 34 lines now counted. Booked from Angel's counts: PCB EE-P-0004-A 100 (batch 006), EE-C-0132 100, EE-C-0105 100, EE-C-0111 100, EE-C-0186 100, EE-C-0187 800, EE-C-0101 150, plus the earlier batches.

Buildable from stock on hand 0 — the RF module EE-C-0037 is genuinely zero
Once PO-0001 lands 90, then limited by EE-C-0191 (220 Ω, 11 per board)
Next constraints after that EE-C-0105 and EE-C-0111 at 100 each

PO-0001 is correct and already linked — 200 × E01-ML01SP4 from Chengdu Ebyte, placed 2026-09-17, due 2026-10-01, nothing received, note "Tape and reel, 200 pcs. Confirm date code before shipping." The part reports ordering = 200, so the ERP knows they are inbound even though the line has no supplier part attached. I had assumed the missing supplier part meant the link was broken; it is not.

What the finished count actually showed: of the 17 lines that read zero when this started, 16 were record-keeping and one was real. The ERP now distinguishes them, and the Cow Remote has a truthful answer for the first time — blocked on one purchase that is already on order, then good for 90 units.

Robot Cow unblocked with BOM substitutes, 2026-09-25. It asked for 1 kΩ and 10 kΩ 5% 0603 resistors, of which there were none — while ~20,000 of the 1% equivalents sat on the shelf. That is the exact case SOP-001 names: "A different but acceptable spec (1% where 5% was asked, 50 V where 25 V was asked) is its own number, and is offered on an assembly as a BOM substitute on that assembly's BOM line." Tighter tolerance is always acceptable where looser was specified, and Angel confirmed: "they work even better".

Line Asked Substitutes added Available
EE-C-0175 1 kΩ 5% 5/board, 0 stock EE-C-0163 (9,990) + EE-C-0164 (10,000) 19,990
EE-C-0177 10 kΩ 5% 8/board, 0 stock EE-C-0166 (9,984) 9,984

No renumbering, no revision bump — a substitute changes neither (SOP-001).

Robot Cow: 0 → 83 buildable, now limited by EE-C-0176 rather than by a paper shortage. The BOM already used substitutes elsewhere (three capacitor lines), so the mechanism was established; these two lines had simply never been given any.

Worth generalising: a zero-stock line is worth checking against the rest of the library before it is treated as a shortage. Twice today the ERP reported a part missing that the shop demonstrably had — once as a duplicate part number (the STM32), once as a tolerance variant.

Still open on Robot Cow: the COATING line (EE-K-0005) is unvalidated; the other 31 are.

Coating, across the three boards (confirmed 2026-09-25): Robot Cow 1.0 mL and Cow Remote 0.7 mL both carry an EE-K-0005 line, sized from a top-side area estimate. Cow Finger deliberately has none — coating over six exposed tactile switches would foul them. Recorded in the part's ERP notes so nobody later "completes" that BOM by adding one.

Robot Cow's coating line is now validated, so all 32 of its lines are validated.

Mechanism worth knowing: BOM validation is not a writable field. PATCH /api/bom/{pk}/ with validated: true returns success and silently does nothing; the working route is PATCH /api/bom/{pk}/validate/ with {"valid": true}.

  • 2026-10-13: BO-0002 created (pk 3): 9 × EE-A-0002-A Robot Cow = one panel of EE-P-0002-A, batch 004B, Pending, no target date, destination In test, take from Kitting; via the app's draft/confirm build-order flow. https://erp.embeddedera.com/web/manufacturing/build-order/3 . The 1K/10K 'shortages' were false: BOM lines 19/21 already carry substitutes EE-C-0163 (1K 1%) and EE-C-0166 (10K 1%), 19,865 and 9,784 available; the app's shortage check ignores substitute stock (fix spun off). Next: move one panel (9) from Shelf A (stock item 33, 243 free) to Kitting per SOP-003, then Issue. BO-0001 still open: 23 of 27 done.
  • 2026-10-13: False-shortage fix shipped (app v2026.10.39, assistant e122691): one helper actions.bom_available (part + BOM substitutes + variants only when allowed) now feeds the build-order preview, the part BOM tab, build lines and Reports; proven on the sandbox ERP (9-unit Robot Cow preview: no shortages) and on production after release. Substitutes are already used in practice: BO-0001 allocated 105 × EE-C-0163 and 168 × EE-C-0166 (1% reels, Shelf A); BO-0002 draws on them the same way at kitting (its build lines read 0 until stock reaches Kitting, its take-from location — expected).
  • 2026-10-18: SO-0001 (LHG, $54,750) found invoiced but never fulfilled. Lines: 26 + 1 × EE-A-0006-A Cow System, 1 × EE-A-0007-A Cow Remote Set; 0 allocated, 0 shipped, no shipments. Both parts have 0 stock and no build orders, so the 27 EE-A-0002-A Robot Cows (BO-0001, locations 23 and 18) were never consumed; the SO does not reference EE-A-0002-A at all. Cow System BOM = Robot Cow + Cow Remote (32 in stock) + Cow Finger (41 in stock); Remote Set = Remote + Finger. Owner: these are kit picks, nothing to build. In InvenTree the only way to turn components into the sellable kit stock is a no-operation build order (allocate the 3 parts, complete). Next: (1) ask owner whether the 27 Cow Systems are already physically delivered or still on the shelf, (2) pick a serial convention for the trackable Cow System (Robot Cow serials are 09260036..62), (3) BO of 27 × EE-A-0006-A and 1 × EE-A-0007-A (auto-allocate, complete), (4) allocate to SO-0001, Complete Shipment, Complete order per SOP-007 steps 12-19. SOP gap: SOP-006 has no kit-pick path; add one (bump version) when step 3 is done. Nothing was changed in the ERP.
  • 2026-10-18: Owner: the 27 Cow Systems are already sold/delivered (stock sits in Finished goods/Paul Shop, loc 23; 6 Robot Cows in Finished goods loc 18). Blocker found before building: my "Cow System takes its Robot Cow's serial" idea is wrong — SOP-006 step 15 + ERP setting SERIAL_NUMBER_GLOBALLY_UNIQUE=true: serials are MMYY####, assigned by the test station, never reused (next free 09260063). Cow Remote (32) and Cow Finger (41) are untracked quantity stock (one stock item each, loc 23). EE-A-0006-A and EE-A-0007-A are Trackable, so a build output needs a serial. Decision needed from owner: make the two kit parts non-trackable (recommended: a kit is a pick, the Robot Cow keeps its serial in the build's consumed-items history; needs SOP-002 note + test that InvenTree lets an untracked build consume the tracked Robot Cow) OR give the kits new serials from the monthly sequence. Nothing changed in the ERP yet.
  • 2026-10-18: Owner chose option 1 (kits non-trackable). Tried on production with a 1 × Remote Set trial build (BO-0003); it does not work in InvenTree 1.5: a tracked component line (Remote, Finger, Robot Cow are Trackable) can only be allocated to a build output, and create-output on EE-A-0007-A still demands serial numbers although the part was set non-trackable ("Serial numbers must be provided for trackable parts"). So untracked kit builds cannot consume tracked parts. Reverted: EE-A-0006-A / 0007-A trackable restored to True, BO-0003 cancelled and deleted, no stock moved. Options left: (a) kit serials from the monthly sequence (09260063+), (b) untrack Remote/Finger (their stock has no serials) and handle the Robot Cow by hand, (c) no build at all: create the 27 + 1 kit stock directly, remove the consumed components with a note, ship against SO-0001.
  • 2026-10-19: SO-0001 fulfilled and completed (owner decision: serials go on the order, not the kit; assigned sequentially). Cow System (EE-A-0006-A) and Remote Set (EE-A-0007-A) set non-trackable (kits; no serials). Created kit stock directly (items 339 = 27, 340 = 1), removed 28 Remote + 28 Finger (items 32/31: now 4 Remote, 13 Finger left) with a note, moved the 27 Robot Cows (09260036..62) to Shipped (loc 19; assigning to customer needs the part Salable, which would leak Robot Cow into the store sync). Line 4 = serials 09260036..61, line 6 = 09260062; CSV attached to the order as SO-0001-serials.csv. Allocated all 3 lines, shipment 1 completed 2026-10-19, order completed. No build orders used: InvenTree 1.5 cannot consume tracked parts into an untracked build. To do: SOP-006 kit-pick path (stock create + remove + serials on the SO attachment) and SOP-002 note that kits are non-trackable; bump versions, notes-sync, sop-deploy.
  • 2026-10-19: Correction to the entry above: the 27 Robot Cows ended up assigned to customer LHG (not left in Shipped loc 19, which still counted as in stock). Done by flipping EE-A-0002-A Salable on for seconds (no store-sync timer was running), assigning, flipping it back off; Robot Cow in_stock = 0, salable False. SOP-006 kit-pick text should say: the part must be Salable to assign to a customer.
  • 2026-10-19: Kit pick built and proven on the sandbox ERP (assistant repo, branch sandbox-kit-pick, commit bcc8d49; not merged, production service not restarted). Issuing a sales order now picks every kit line from stock: one kit stock item per unit, BOM components installed into it (the serialised Robot Cow's serial rides on the sale), kit allocated to an open shipment. Shortage never blocks the invoice (message "Kit pick pending: need N more X", nothing written for that line); the Pick kits button (POST /invoice/api/pick-kits) re-runs it idempotently via SO metadata kit_pick; shipping an unpicked kit line is refused. Runs on the admin-token client (kit_pick in ERP_OPERATION_POLICY). Unit tests tests/test_kit_pick.py (25) and the sandbox e2e tests/test_kit_pick_e2e.py pass (e2e needs ~/.config/assistant/env + env.sandbox sourced and KIT_E2E_ERP_URL/TOKEN).
  • 2026-10-19: Gotcha: a kit cannot be identified by trackable=False. InvenTree (Part.ensure_trackable / BomItem.clean) forces every assembly that contains a trackable component back to trackable whenever that component is saved, so the flag I set on 221/222 kept reverting (production 221 reads trackable=True again; 222 False). The kit mark is now part metadata {"kit": true} (GET/PATCH /api/metadata/part/pk/N/). Production parts 221 and 222 do not have the mark yet — set it before the merge goes live (a production write, waits for the owner).
  • 2026-10-19: Gotcha: InvenTree 1.5 POST /api/stock/ returns a LIST, not a dict. Sandbox leftovers: the zz_trk_* audit objects were dropped; sandbox ERP stock for Cow Remote/Finger/Cow was reset by the e2e (sandbox only).
  • 2026-10-19: kit pick independent review fixed (commit 4dd8f8c on sandbox-kit-pick): per-kit record + resume, per-order lock, lazy kit token, Sales-token order metadata for admin chat, quantity-growth, float tolerance. Sandbox ERP stopped. Production still lacks the kit mark; nothing merged or restarted.
  • 2026-10-19: Kit pick SOPs published (SOP-002 v2.7, SOP-006 v2.0, SOP-007 v5.1). BLOCKED by permission classifier, awaiting Angel: (1) production PATCH /api/metadata/part/pk/{221,222}/ {"metadata":{"kit":true}} (keep storefront:false); (2) in ~/assistant git merge --ff-only sandbox-kit-pick then systemctl --user restart assistant.service. Code is in 4dd8f8c on sandbox-kit-pick.
  • 2026-10-19: Kit pick LIVE: parts 221/222 marked kit, sandbox-kit-pick fast-forwarded into ~/assistant main (4dd8f8c), assistant.service restarted 04:05 and up. Supersedes the BLOCKED line above.
  • 2026-11-07: Open Robot Cow BOs cleanup. BO-0006 stalled 17 days: EE-C-0176 33R (part 190) book stock hit 5 (opening count 2026-09-19 was an estimate of ~600), so tester pretest consumption for 10260077-81 failed missing explicitly available build allocation. Fix: owner says ~2000 left -> stock count item 38 = 2000 (note: recount at next reel change), allocated +30 to BO-0006 line 194 (35 = 5 boards); tester-api 120 s retry drains them. Backups first: erp-backup 2026-11-07_1720, tester.sqlite3.backup-20261107-drain. Still open: 10260072 (latest required Pre-coating result FAIL from an 11-07 coated precheck; PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS blocks; complete:8:1365 stuck started), 10260007 (BO-0002) and 10260079 (BO-0006) quarantine cases need tester rework record + retest to release, 10260016 (BO-0003) no chip, one FAIL, never retested. 22 Sept serials 09260013-35 passed FINAL with no ERP item (pre-integration; likely the unserialized opening stock).
  • 2026-11-07 (later): All Robot Cow BOs closed. BO-0002 9/9, BO-0003 8/9 + 10260016 scrapped (closed by hand; its unused final-stage allocations released, not consumed), BO-0005 9/9, BO-0006 36/36. Owner disposition (no station retest): 10260007 and 10260079 released from quarantine and completed; 10260072 got an ERP Pre-coating result labelled OWNER DISPOSITION (not a measurement) because PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS blocked it after an 11-07 coated-precheck FAIL. Releases/scrap written as tester quarantine events from station plex-admin (bypassing only passing_rework_required), then the tester's own code consumed and completed. Check: 89/89 FINAL-pass boards since October are finished in ERP (62 Finished goods, 27 shipped in kits), no open erp_operations. Open: recount 33R reel (book 2000 is the owner's estimate); 22 Sept serials 09260013-35 have no ERP item.
  • 2026-11-07: Raised BO-0007..BO-0011 for EE-A-0002-A Robot Cow, 12 each (60 boards), via actions.draft/confirm_build_order (dest In test, take Kitting, issued, auto-allocated). All lines covered except EE-C-0131 USB-C: 10 on hand, allocated to BO-0007; 50 short (BO-0007 2, BO-0008..0011 12 each). Owner asked for 5×12.
  • 2026-11-07: EE-C-0131 USB-C counted to 200 on owner's word (stock item 51 at Neoden YY1, was 10); allocated 12 each to BO-0007..0011, all five now fully allocated.