Add container weight tracking for weigh-based concentrate audits
Build and push image / build (push) Successful in 1m6s

Record the total weight of a jar (product + container) at acquisition so
audits can be done by simply re-weighing the sealed jar. The tare is
derived (containerWeight − productWeight), and the audit flow offers a
"Weigh container" toggle that auto-calculates remaining product from the
scale reading.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 23:11:39 -04:00
parent e9e66ab1cb
commit a1be29ab6e
11 changed files with 205 additions and 26 deletions
+9
View File
@@ -14,6 +14,7 @@ db.pragma("foreign_keys = ON");
archiveLegacyIfPresent();
archiveV1IfPresent();
migrateAddCheckoutDate();
migrateAddContainerWeight();
const schema = readFileSync(join(__dirname, "schema.sql"), "utf8");
db.exec(schema);
@@ -26,6 +27,14 @@ function migrateAddCheckoutDate(): void {
db.exec(`ALTER TABLE inventory_items ADD COLUMN checkout_date TEXT`);
}
function migrateAddContainerWeight(): void {
const cols = db
.prepare(`PRAGMA table_info(inventory_items)`)
.all() as { name: string }[];
if (cols.length === 0 || cols.some((c) => c.name === "container_weight")) return;
db.exec(`ALTER TABLE inventory_items ADD COLUMN container_weight REAL`);
}
// One-shot migration: the original schema put per-instance fields (weight,
// bin_id, etc.) directly on `products`. The split schema separates products
// (catalog) from inventory_items (instance). When we detect the old shape,
+2
View File
@@ -24,6 +24,7 @@ type InventoryRow = {
cbd: number;
total_cannabinoids: number;
weight: number;
container_weight: number | null;
last_audit_weight: number | null;
count_original: number;
count_last_audit: number | null;
@@ -101,6 +102,7 @@ bootstrapRouter.get("/bootstrap", (_req, res) => {
cbd: i.cbd,
totalCannabinoids: i.total_cannabinoids,
weight: i.weight,
containerWeight: i.container_weight,
lastAuditWeight: i.last_audit_weight,
countOriginal: i.count_original,
countLastAudit: i.count_last_audit,
+16 -4
View File
@@ -15,6 +15,7 @@ type CreateBody = {
weight?: number;
countOriginal?: number;
unitWeight?: number;
containerWeight?: number | null;
purchaseDate: string;
};
@@ -53,14 +54,14 @@ inventoryRouter.post("/inventory", (req, res) => {
id, asset_id, product_id,
shop_id, bin_id,
price, thc, cbd, total_cannabinoids,
weight, last_audit_weight,
weight, container_weight, last_audit_weight,
count_original, count_last_audit, unit_weight,
purchase_date, status
) VALUES (
@id, @assetId, @productId,
@shopId, @binId,
@price, @thc, @cbd, @totalCannabinoids,
@weight, @lastAuditWeight,
@weight, @containerWeight, @lastAuditWeight,
@countOriginal, @countLastAudit, @unitWeight,
@purchaseDate, 'active'
)`,
@@ -75,6 +76,7 @@ inventoryRouter.post("/inventory", (req, res) => {
cbd: body.cbd ?? 0,
totalCannabinoids: body.totalCannabinoids ?? 0,
weight: isDiscrete ? 0 : body.weight ?? 0,
containerWeight: isDiscrete ? null : body.containerWeight ?? null,
lastAuditWeight: isDiscrete ? null : body.weight ?? 0,
countOriginal: isDiscrete ? body.countOriginal ?? 0 : 0,
countLastAudit: isDiscrete ? body.countOriginal ?? 0 : null,
@@ -95,6 +97,7 @@ type UpdateBody = Partial<{
cbd: number;
totalCannabinoids: number;
weight: number;
containerWeight: number | null;
countOriginal: number;
unitWeight: number;
purchaseDate: string;
@@ -110,6 +113,7 @@ type ItemRow = {
cbd: number;
total_cannabinoids: number;
weight: number;
container_weight: number | null;
last_audit_weight: number | null;
count_original: number;
count_last_audit: number | null;
@@ -121,8 +125,8 @@ function doUpdate(id: string, body: UpdateBody): void {
const existing = db
.prepare<[string], ItemRow>(
`SELECT id, shop_id, bin_id, product_id, price, thc, cbd,
total_cannabinoids, weight, last_audit_weight, count_original,
count_last_audit, unit_weight, purchase_date
total_cannabinoids, weight, container_weight, last_audit_weight,
count_original, count_last_audit, unit_weight, purchase_date
FROM inventory_items WHERE id = ?`,
)
.get(id);
@@ -161,6 +165,12 @@ function doUpdate(id: string, body: UpdateBody): void {
!isDiscrete && Number.isFinite(body.weight) && (body.weight as number) >= 0
? (body.weight as number)
: existing.weight;
const nextContainerWeight =
body.containerWeight === undefined
? existing.container_weight
: body.containerWeight != null && Number.isFinite(body.containerWeight)
? body.containerWeight
: null;
const nextCountOriginal =
isDiscrete && Number.isFinite(body.countOriginal) && (body.countOriginal as number) >= 0
? Math.floor(body.countOriginal as number)
@@ -184,6 +194,7 @@ function doUpdate(id: string, body: UpdateBody): void {
cbd = @cbd,
total_cannabinoids = @totalCannabinoids,
weight = @weight,
container_weight = @containerWeight,
last_audit_weight = @lastAuditWeight,
count_original = @countOriginal,
count_last_audit = @countLastAudit,
@@ -199,6 +210,7 @@ function doUpdate(id: string, body: UpdateBody): void {
cbd: nextCbd,
totalCannabinoids: nextTotalCanna,
weight: nextWeight,
containerWeight: nextContainerWeight,
lastAuditWeight: nextLastAuditWeight,
countOriginal: nextCountOriginal,
countLastAudit: nextCountLastAudit,
+1
View File
@@ -64,6 +64,7 @@ CREATE TABLE IF NOT EXISTS inventory_items (
cbd REAL DEFAULT 0,
total_cannabinoids REAL DEFAULT 0,
weight REAL DEFAULT 0,
container_weight REAL,
last_audit_weight REAL,
count_original INTEGER DEFAULT 0,
count_last_audit INTEGER,