Track inventory at the instance level, not by product
Build and push image / build (push) Successful in 46s
Build and push image / build (push) Successful in 46s
The products table conflated catalog ("kind of thing you scan") with
instance ("this jar I bought") — splitting it lets us record every
purchase as its own asset and autofill brand/shop/price/THC from the
last instance when scanning a known SKU.
- products: sku + strain + name + type + kind (catalog only)
- inventory_items: physical jars with short-UUID asset ids, per-batch
brand/shop/bin/price/cannabinoids/weight, audits, lifecycle
- audits now key on inventory_id; strains lose brand_id and type
- migration: rename existing products/audits/strains to *_legacy on
first boot so users keep historical reference, fresh start otherwise
- two-step add flow: scan SKU → select/create product → instance
details (autofilled from last instance) → generated asset id shown
- ScanField matches asset id first, falls back to SKU
- inventory list defaults flat, "By product" toggle groups instances
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,13 +35,12 @@ catalogRouter.patch("/brands/:id", (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Deleting a brand unparents any products and strains that reference it
|
||||
// (brand_id → NULL), so users never lose products when reorganizing.
|
||||
// Deleting a brand unparents any inventory items that reference it
|
||||
// (brand_id → NULL), so users never lose inventory when reorganizing.
|
||||
catalogRouter.delete("/brands/:id", (req, res) => {
|
||||
const { id } = req.params;
|
||||
const tx = db.transaction(() => {
|
||||
db.prepare("UPDATE products SET brand_id = NULL WHERE brand_id = ?").run(id);
|
||||
db.prepare("UPDATE strains SET brand_id = NULL WHERE brand_id = ?").run(id);
|
||||
db.prepare("UPDATE inventory_items SET brand_id = NULL WHERE brand_id = ?").run(id);
|
||||
const result = db.prepare("DELETE FROM brands WHERE id = ?").run(id);
|
||||
if (result.changes === 0) throw new Error("not found");
|
||||
});
|
||||
@@ -87,11 +86,11 @@ catalogRouter.patch("/shops/:id", (req, res) => {
|
||||
res.json({ id, name: nextName, location: nextLocation });
|
||||
});
|
||||
|
||||
// Deleting a shop unparents any products that reference it (shop_id → NULL).
|
||||
// Deleting a shop unparents any inventory items that reference it (shop_id → NULL).
|
||||
catalogRouter.delete("/shops/:id", (req, res) => {
|
||||
const { id } = req.params;
|
||||
const tx = db.transaction(() => {
|
||||
db.prepare("UPDATE products SET shop_id = NULL WHERE shop_id = ?").run(id);
|
||||
db.prepare("UPDATE inventory_items SET shop_id = NULL WHERE shop_id = ?").run(id);
|
||||
const result = db.prepare("DELETE FROM shops WHERE id = ?").run(id);
|
||||
if (result.changes === 0) throw new Error("not found");
|
||||
});
|
||||
@@ -132,12 +131,12 @@ catalogRouter.patch("/bins/:id", (req, res) => {
|
||||
res.json({ id, name: nextName, capacity: nextCapacity });
|
||||
});
|
||||
|
||||
// Deleting a bin unassigns any products that reference it (bin_id → NULL),
|
||||
// so users never lose products when reorganizing storage.
|
||||
// Deleting a bin unassigns any inventory items that reference it (bin_id → NULL),
|
||||
// so users never lose inventory when reorganizing storage.
|
||||
catalogRouter.delete("/bins/:id", (req, res) => {
|
||||
const { id } = req.params;
|
||||
const tx = db.transaction(() => {
|
||||
db.prepare("UPDATE products SET bin_id = NULL WHERE bin_id = ?").run(id);
|
||||
db.prepare("UPDATE inventory_items SET bin_id = NULL WHERE bin_id = ?").run(id);
|
||||
const result = db.prepare("DELETE FROM bins WHERE id = ?").run(id);
|
||||
if (result.changes === 0) throw new Error("not found");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user