Add checkout/custody feature for tracking items in personal possession
Build and push image / build (push) Successful in 1m8s
Build and push image / build (push) Successful in 1m8s
Items can now be checked out of their bin into "my custody" and later checked back in or marked consumed. Adds checkout/checkin API endpoints, a My Custody sidebar page, CheckoutFlow and CheckinFlow modals, and updates ProductDetail, Inventory, ConsumeFlow, and MarkGoneFlow to handle the new checked-out status. Bulk items prompt for remaining weight on check-in. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -212,6 +212,67 @@ inventoryRouter.patch("/inventory/:id", (req, res) => {
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
inventoryRouter.post("/inventory/:id/checkout", (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { date } = req.body as { date: string };
|
||||
const result = db
|
||||
.prepare(
|
||||
`UPDATE inventory_items
|
||||
SET status = 'checked-out', checkout_date = ?, bin_id = NULL
|
||||
WHERE id = ? AND status = 'active'`,
|
||||
)
|
||||
.run(date, id);
|
||||
if (result.changes === 0) return res.status(404).json({ error: "not found or not active" });
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
inventoryRouter.post("/inventory/:id/checkin", (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { date, binId, remainingWeight } = req.body as {
|
||||
date: string;
|
||||
binId: string;
|
||||
remainingWeight?: number;
|
||||
};
|
||||
|
||||
const item = db
|
||||
.prepare<
|
||||
[string],
|
||||
{ product_id: string; last_audit_weight: number | null; weight: number }
|
||||
>(
|
||||
`SELECT product_id, last_audit_weight, weight
|
||||
FROM inventory_items WHERE id = ? AND status = 'checked-out'`,
|
||||
)
|
||||
.get(id);
|
||||
if (!item) return res.status(404).json({ error: "not found or not checked-out" });
|
||||
|
||||
const product = db
|
||||
.prepare<[string], { kind: string }>(`SELECT kind FROM products WHERE id = ?`)
|
||||
.get(item.product_id);
|
||||
const isBulk = product?.kind === "bulk";
|
||||
|
||||
const tx = db.transaction(() => {
|
||||
db.prepare(
|
||||
`UPDATE inventory_items
|
||||
SET status = 'active', bin_id = ?, checkout_date = NULL
|
||||
WHERE id = ?`,
|
||||
).run(binId, id);
|
||||
|
||||
if (isBulk && remainingWeight != null && Number.isFinite(remainingWeight)) {
|
||||
const prev = item.last_audit_weight ?? item.weight;
|
||||
db.prepare(
|
||||
`INSERT INTO audits (inventory_id, date, mode, value, prev_value, confirmed_by)
|
||||
VALUES (?, ?, 'weigh', ?, ?, 'checkin')`,
|
||||
).run(id, date, remainingWeight, prev);
|
||||
db.prepare(`UPDATE inventory_items SET last_audit_weight = ? WHERE id = ?`).run(
|
||||
remainingWeight,
|
||||
id,
|
||||
);
|
||||
}
|
||||
});
|
||||
tx();
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
inventoryRouter.post("/inventory/:id/finish", (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { date, rating, notes } = req.body as {
|
||||
@@ -222,8 +283,8 @@ inventoryRouter.post("/inventory/:id/finish", (req, res) => {
|
||||
const result = db
|
||||
.prepare(
|
||||
`UPDATE inventory_items
|
||||
SET status = 'consumed', consumed_date = ?, rating = ?, notes = ?, bin_id = NULL
|
||||
WHERE id = ? AND status = 'active'`,
|
||||
SET status = 'consumed', consumed_date = ?, rating = ?, notes = ?, bin_id = NULL, checkout_date = NULL
|
||||
WHERE id = ? AND status IN ('active', 'checked-out')`,
|
||||
)
|
||||
.run(date, rating ?? null, notes ?? null, id);
|
||||
if (result.changes === 0) return res.status(404).json({ error: "not found or not active" });
|
||||
@@ -242,8 +303,8 @@ inventoryRouter.post("/inventory/:id/gone", (req, res) => {
|
||||
const result = db
|
||||
.prepare(
|
||||
`UPDATE inventory_items
|
||||
SET status = 'gone', gone_date = ?, notes = ?, bin_id = NULL
|
||||
WHERE id = ? AND status = 'active'`,
|
||||
SET status = 'gone', gone_date = ?, notes = ?, bin_id = NULL, checkout_date = NULL
|
||||
WHERE id = ? AND status IN ('active', 'checked-out')`,
|
||||
)
|
||||
.run(date, combinedNotes, id);
|
||||
if (result.changes === 0) throw new Error("not found");
|
||||
|
||||
Reference in New Issue
Block a user