Remove estimated remaining decay, use audit values directly
Build and push image / build (push) Successful in 1m6s
Build and push image / build (push) Successful in 1m6s
Replace burn-rate estimation (linear decay between audits) with actual last audit values. Remaining is now always the last weigh-in value or original weight if no audits exist — no more speculative daily decay. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+11
-14
@@ -1,5 +1,5 @@
|
||||
// computeStats — derives daily/weekly/monthly grams from purchase + audit
|
||||
// history, using estimated remaining for active items and full weight for
|
||||
// history, using last audit values for active items and full weight for
|
||||
// consumed. Gone items contribute spend but NOT grams (so daily averages
|
||||
// stay clean). Operates on the enriched Item[] view, not raw products.
|
||||
|
||||
@@ -88,8 +88,8 @@ export function computeStats(data: Bootstrap): Stats {
|
||||
const bulkGramsUsedSoFar = (p: Item): number => {
|
||||
if (p.type === "Tincture" || p.type === "Edible") return 0;
|
||||
if (p.kind === "bulk") {
|
||||
const est = helpers.estimatedRemaining(p, todayStr);
|
||||
return Math.max(0, p.weight - est);
|
||||
const rem = helpers.remaining(p);
|
||||
return Math.max(0, p.weight - rem);
|
||||
}
|
||||
const cur = p.countLastAudit ?? p.countOriginal;
|
||||
return Math.max(0, p.countOriginal - cur) * (p.unitWeight || 0);
|
||||
@@ -152,16 +152,13 @@ export function computeStats(data: Bootstrap): Stats {
|
||||
const spend90 = last90p.reduce((s, p) => s + p.price, 0);
|
||||
|
||||
const inventoryValue = active.reduce(
|
||||
(s, p) => s + p.price * helpers.pctRemaining(p, todayStr),
|
||||
(s, p) => s + p.price * helpers.pctRemaining(p),
|
||||
0,
|
||||
);
|
||||
|
||||
// Grams currently on hand: bulk uses estimated remaining; discrete uses
|
||||
// (units × per-unit weight). Tincture (ml) and edibles (count) are excluded
|
||||
// to match the existing `bulkGrams` convention used for $/g and totals.
|
||||
const inventoryGrams = active.reduce((s, p) => {
|
||||
if (p.type === "Tincture" || p.type === "Edible") return s;
|
||||
if (p.kind === "bulk") return s + helpers.estimatedRemaining(p, todayStr);
|
||||
if (p.kind === "bulk") return s + helpers.remaining(p);
|
||||
const cur = p.countLastAudit ?? p.countOriginal;
|
||||
return s + cur * (p.unitWeight || 0);
|
||||
}, 0);
|
||||
@@ -198,10 +195,10 @@ export function computeStats(data: Bootstrap): Stats {
|
||||
const typeBreakdown: Record<string, number> = {};
|
||||
active.forEach((p) => {
|
||||
let g: number;
|
||||
if (p.type === "Tincture") g = helpers.estimatedRemaining(p, todayStr) * 0.5;
|
||||
if (p.type === "Tincture") g = helpers.remaining(p) * 0.5;
|
||||
else if (p.type === "Edible")
|
||||
g = (p.countLastAudit ?? p.countOriginal) * 0.3;
|
||||
else if (p.kind === "bulk") g = helpers.estimatedRemaining(p, todayStr);
|
||||
else if (p.kind === "bulk") g = helpers.remaining(p);
|
||||
else g = (p.countLastAudit ?? p.countOriginal) * (p.unitWeight || 0);
|
||||
if (g > 0) typeBreakdown[p.type] = (typeBreakdown[p.type] || 0) + g;
|
||||
});
|
||||
@@ -209,7 +206,7 @@ export function computeStats(data: Bootstrap): Stats {
|
||||
const flowerEquivalent = active
|
||||
.filter((p) => p.type === "Flower" || p.type === "Pre-roll")
|
||||
.reduce((s, p) => {
|
||||
if (p.kind === "bulk") return s + helpers.estimatedRemaining(p, todayStr);
|
||||
if (p.kind === "bulk") return s + helpers.remaining(p);
|
||||
return s + (p.countLastAudit ?? p.countOriginal) * (p.unitWeight || 0);
|
||||
}, 0);
|
||||
const daysOfSupply = dailyAvg > 0 ? flowerEquivalent / dailyAvg : 0;
|
||||
@@ -226,7 +223,7 @@ export function computeStats(data: Bootstrap): Stats {
|
||||
const overdueAudits = active.filter((p) => helpers.auditOverdue(p, todayStr));
|
||||
|
||||
const lowStockBulk = active.filter(
|
||||
(p) => p.kind === "bulk" && helpers.pctRemaining(p, todayStr) < 0.25,
|
||||
(p) => p.kind === "bulk" && helpers.pctRemaining(p) < 0.25,
|
||||
);
|
||||
|
||||
// Group discrete instances by product so multiple jars of the same
|
||||
@@ -299,7 +296,7 @@ export function remainingShort(p: Item): string {
|
||||
const cur = p.countLastAudit ?? p.countOriginal;
|
||||
return `${cur} ct`;
|
||||
}
|
||||
const est = helpers.estimatedRemaining(p);
|
||||
const trimmed = est.toFixed(2).replace(/\.?0+$/, "") || "0";
|
||||
const rem = helpers.remaining(p);
|
||||
const trimmed = rem.toFixed(2).replace(/\.?0+$/, "") || "0";
|
||||
return `${trimmed} ${cfg?.unit ?? "g"}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user