Files
Apothecary/server/src/routes/bootstrap.ts
T
josh 5fa1e34914
Build and push image / build (push) Successful in 57s
Split audits into Weigh Ins (bulk) and Bin Checks (discrete)
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>
2026-06-06 18:28:55 -04:00

154 lines
3.9 KiB
TypeScript

import { Router } from "express";
import { db } from "../db.js";
export const bootstrapRouter: Router = Router();
type ProductRow = {
id: string;
sku: string;
strain_id: string;
brand_id: string | null;
type: string;
kind: string;
created_at: string;
};
type InventoryRow = {
id: string;
asset_id: string;
product_id: string;
shop_id: string | null;
bin_id: string | null;
price: number;
thc: number;
cbd: number;
total_cannabinoids: number;
weight: number;
container_weight: number | null;
last_audit_weight: number | null;
count_original: number;
count_last_audit: number | null;
unit_weight: number;
purchase_date: string;
status: string;
consumed_date: string | null;
gone_date: string | null;
checkout_date: string | null;
prev_bin_id: string | null;
rating: number | null;
notes: string | null;
};
type StrainRow = {
id: string;
name: string;
default_thc: number | null;
default_cbd: number | null;
default_total_cannabinoids: number | null;
notes: string | null;
};
type AuditRow = {
id: number;
inventory_id: string;
date: string;
mode: string;
value: number;
prev_value: number | null;
confirmed_by: string | null;
};
bootstrapRouter.get("/bootstrap", (_req, res) => {
const products = db
.prepare<[], ProductRow>("SELECT * FROM products ORDER BY id")
.all();
const inventory = db
.prepare<[], InventoryRow>("SELECT * FROM inventory_items ORDER BY id")
.all();
const audits = db
.prepare<[], AuditRow>("SELECT * FROM audits ORDER BY inventory_id, date")
.all();
const shops = db.prepare("SELECT * FROM shops ORDER BY id").all();
const brands = db.prepare("SELECT * FROM brands ORDER BY id").all();
const binsRaw = db.prepare("SELECT id, name, capacity, cadence_days, last_checked FROM bins ORDER BY id").all() as { id: string; name: string; capacity: number; cadence_days: number; last_checked: string | null }[];
const bins = binsRaw.map((b) => ({
id: b.id,
name: b.name,
capacity: b.capacity,
cadenceDays: b.cadence_days,
lastChecked: b.last_checked,
}));
const strains = db
.prepare<[], StrainRow>("SELECT * FROM strains ORDER BY name COLLATE NOCASE")
.all();
const auditsByInventory = new Map<string, AuditRow[]>();
for (const a of audits) {
const arr = auditsByInventory.get(a.inventory_id) ?? [];
arr.push(a);
auditsByInventory.set(a.inventory_id, arr);
}
const productsOut = products.map((p) => ({
id: p.id,
sku: p.sku,
strainId: p.strain_id,
brandId: p.brand_id,
type: p.type,
kind: p.kind,
createdAt: p.created_at,
}));
const inventoryOut = inventory.map((i) => ({
id: i.id,
assetId: i.asset_id,
productId: i.product_id,
shopId: i.shop_id,
binId: i.bin_id,
price: i.price,
thc: i.thc,
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,
unitWeight: i.unit_weight,
purchaseDate: i.purchase_date,
status: i.status,
consumedDate: i.consumed_date,
goneDate: i.gone_date,
checkoutDate: i.checkout_date,
prevBinId: i.prev_bin_id,
rating: i.rating,
notes: i.notes,
audits: (auditsByInventory.get(i.id) ?? []).map((a) => ({
date: a.date,
mode: a.mode,
value: a.value,
prev: a.prev_value,
confirmedBy: a.confirmed_by,
})),
}));
const strainsOut = strains.map((s) => ({
id: s.id,
name: s.name,
defaultThc: s.default_thc,
defaultCbd: s.default_cbd,
defaultTotalCannabinoids: s.default_total_cannabinoids,
notes: s.notes,
}));
res.json({
products: productsOut,
inventoryItems: inventoryOut,
shops,
brands,
bins,
strains: strainsOut,
today: new Date().toISOString().slice(0, 10),
});
});