Split audits into Weigh Ins (bulk) and Bin Checks (discrete)
Build and push image / build (push) Successful in 57s

Replaces the unified audit system with two purpose-built flows:
- Weigh Ins: rebranded audit flow for bulk products (Flower, Concentrate,
  Tincture) with scale weigh, container weigh, and estimate modes
- Bin Checks: new bin-level presence check — select a bin, scan every item,
  resolve discrepancies (wrong bin, unknown, missing), auto-records presence
  audits on verified items

Adds cadence_days and last_checked to bins table, with per-bin overdue
tracking on the dashboard and bins view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 18:28:55 -04:00
parent d7da7afe5e
commit 5fa1e34914
19 changed files with 769 additions and 134 deletions
+13 -8
View File
@@ -103,20 +103,21 @@ catalogRouter.delete("/shops/:id", (req, res) => {
});
catalogRouter.post("/bins", (req, res) => {
const { name, capacity } = req.body as { name: string; capacity?: number };
const { name, capacity, cadenceDays } = req.body as { name: string; capacity?: number; cadenceDays?: number };
if (!name?.trim()) return res.status(400).json({ error: "name required" });
const id = nextId("bin", "bins");
const cap = Number.isFinite(capacity) && (capacity as number) > 0 ? Math.floor(capacity as number) : 10;
db.prepare("INSERT INTO bins (id, name, capacity) VALUES (?, ?, ?)").run(id, name.trim(), cap);
res.json({ id, name: name.trim(), capacity: cap });
const cad = Number.isFinite(cadenceDays) && (cadenceDays as number) > 0 ? Math.floor(cadenceDays as number) : 30;
db.prepare("INSERT INTO bins (id, name, capacity, cadence_days) VALUES (?, ?, ?, ?)").run(id, name.trim(), cap, cad);
res.json({ id, name: name.trim(), capacity: cap, cadenceDays: cad, lastChecked: null });
});
catalogRouter.patch("/bins/:id", (req, res) => {
const { id } = req.params;
const { name, capacity } = req.body as { name?: string; capacity?: number };
const { name, capacity, cadenceDays } = req.body as { name?: string; capacity?: number; cadenceDays?: number };
const existing = db
.prepare<[string], { id: string; name: string; capacity: number }>(
"SELECT id, name, capacity FROM bins WHERE id = ?",
.prepare<[string], { id: string; name: string; capacity: number; cadence_days: number; last_checked: string | null }>(
"SELECT id, name, capacity, cadence_days, last_checked FROM bins WHERE id = ?",
)
.get(id);
if (!existing) return res.status(404).json({ error: "bin not found" });
@@ -126,9 +127,13 @@ catalogRouter.patch("/bins/:id", (req, res) => {
Number.isFinite(capacity) && (capacity as number) > 0
? Math.floor(capacity as number)
: existing.capacity;
const nextCadence =
Number.isFinite(cadenceDays) && (cadenceDays as number) > 0
? Math.floor(cadenceDays as number)
: existing.cadence_days;
db.prepare("UPDATE bins SET name = ?, capacity = ? WHERE id = ?").run(nextName, nextCapacity, id);
res.json({ id, name: nextName, capacity: nextCapacity });
db.prepare("UPDATE bins SET name = ?, capacity = ?, cadence_days = ? WHERE id = ?").run(nextName, nextCapacity, nextCadence, id);
res.json({ id, name: nextName, capacity: nextCapacity, cadenceDays: nextCadence, lastChecked: existing.last_checked });
});
// Deleting a bin unassigns any inventory items that reference it (bin_id → NULL),