Consumables

Stuff that runs out.

Toner. Spare drum kits. UPS batteries. Replacement keyboards. The stuff Inventory shouldn't track per-serial because it's interchangeable. Distinct table, distinct nav entry — but the same audit-trail muscle Resolvd uses everywhere. Every quantity change carries actor, reason, timestamp, and optional ticket link.

+5 received -1 issued LOW
Shape of a consumable

Per-SKU, per vendor.

A consumable row is one part-number you keep on hand. Linked to the vendor company that supplies it so reorder context is one click away. Stock never gets edited directly — direct PATCH /current_stock is rejected with 400 "use /:id/move". The audit trail can't be bypassed.

part_no

Unique vendor SKU — HP-58A-CF258A, APC-RBC110. Becomes the lead line on the printed label.

vendor_company_id

FK to the Resolvd company row that supplies the part. Surfaces the vendor name in the list + on labels.

current_stock

Computed running total. Never mutated directly — every change goes through the ledger.

low_stock_threshold

Trip-point for the red LOW chip on the list. Informational; no notification fires yet.

notes

Free-text — "For HP LJ Pro M404n at Reception", "Datacenter A, UPS row 3".

archive

Soft-delete flag. Archived rows hidden from the default list, kept in storage so the ledger never orphans.

Append-only ledger

Every delta. Who ran it. When.

Every stock change runs UPDATE consumables SET current_stock = current_stock + $delta + INSERT consumable_movements … in the same transaction. Inserts never get edited or deleted. A bad count? Log a count_correction row with the delta — the discrepancy stays on the timeline.

delta

Signed int. +5 on receive, -1 on issuance, -2 on disposal. Sum across all rows equals current_stock.

reason

Free-text but the seeded list is received | issued | returned | disposed | count_correction | loss.

ticket_id

Optional — link an issuance back to the ticket that consumed it. -1 Toner for ACME-0042.

by_user_id

Actor. Every adjustment carries who ran it.

at

Timestamp, server clock. Ledger is append-only — corrections happen via a new count_correction row, not by editing history.

note

Free-text — Newegg PO #4521, recall — supplier swap, inventory recount mismatch.

Common workflows

Five buttons. All audited.

Stock adjustment is one endpoint — POST /api/consumables/:id/move — with a delta, a reason, and optional context. The UI surfaces five common shapes; the API accepts arbitrary reasons so you can grow the vocabulary without a schema migration.

Receive shipment

Detail → + N with reason received. Note the PO.

Issue to ticket

Detail → - 1 with reason issued, link the ticket id. Trail flows back to the consuming ticket.

Returnable loan

- 1 reason issued on issue, + 1 reason returned on the way back. Sum nets zero on net-zero loans.

Disposal / recall

- N reason disposed. Keeps the deduction on the audit trail; the absence of a received counterpart explains the spend.

Count correction

When a manual recount disagrees with on-screen — log the delta as count_correction. Discrepancy stays visible rather than hiding behind an edit.

Printable labels

A label, a QR, a scan back to the row.

Consumable detail carries a Print label button alongside its asset cousin. Same Zebra-backed pipeline, same raw TCP :9100 path. Lead line is the part_no, vendor on the property line, QR encodes FRONTEND_URL/consumables/<id>. Stick it on the shelf; scan it from a phone; you're at the right Resolvd row to log an issuance.

Stop the spreadsheet.

Define the SKUs that matter, set a reorder threshold, log a movement every time you touch the shelf. Stock numbers stop drifting because every change has an audit row.