From 86eba97d33843046771e8b53aecbdd0154ae4660 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 16:46:42 +0200 Subject: [PATCH] fix(stock): skip ledger row on first-time pricing + include cost delta in notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code-review feedback from Task 2: - Code-review Issue #1 (Important): the strict-equality guard fired on 'before.cost_price = undefined' vs 'update.costPrice = 0' — exactly the population this feature targets (fresh catalogue rows the operator is pricing for the first time). No real cost movement was happening, but a spurious ledger row was getting written. Gate the ledger write on Number.isFinite(before.cost_price) so first-time pricing doesn't pollute audit history with a 0 -> 0 'movement'. - Code-review Issue #4 (Minor, audit-readability): add the actual before -> after delta to the ledger notes so operators scrolling the ledger history see 'Gary edited cost 2.50 -> 3.20 from /stock' rather than the prior opaque 'Gary edited cost from /stock'. - Added two regression tests: one for the first-time-pricing case, one pinning the notes delta format. --- src/lib/server/stock-pricing-service.test.ts | 42 ++++++++++++++++++++ src/lib/server/stock-pricing-service.ts | 11 +++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/lib/server/stock-pricing-service.test.ts b/src/lib/server/stock-pricing-service.test.ts index 4997001..b49dfae 100644 --- a/src/lib/server/stock-pricing-service.test.ts +++ b/src/lib/server/stock-pricing-service.test.ts @@ -143,4 +143,46 @@ describe("updateStockItemsPricing — stock_ledger audit on cost change", () => expect(ledgerCreates).toEqual([]); }); + + it("does NOT write a stock_ledger row when prior cost_price is undefined (first-time pricing)", async () => { + const before = { + id: "abcdefghijklmno", + // cost_price intentionally missing — fresh catalogue row. + selling_price: 0, + markup: 0, + }; + const updated = { ...before, cost_price: 2.5 }; + const { pb, ledgerCreates } = stubPb(before, updated); + + await updateStockItemsPricing({ + pb: pb as never, + updates: [{ id: "abcdefghijklmno", costPrice: 2.5 }], + actor: { id: "useropid1234567", name: "Gary" }, + } as UpdateStockItemsPricingInput); + + expect(ledgerCreates).toEqual([]); + }); + + it("records an old → new delta in the notes string when costPrice changes", async () => { + const before = { + id: "abcdefghijklmno", + cost_price: 2.5, + selling_price: 3.25, + markup: 30, + }; + const updated = { ...before, cost_price: 3.2 }; + const { pb, ledgerCreates } = stubPb(before, updated); + + await updateStockItemsPricing({ + pb: pb as never, + updates: [{ id: "abcdefghijklmno", costPrice: 3.2 }], + actor: { id: "useropid1234567", name: "Gary" }, + } as UpdateStockItemsPricingInput); + + expect(ledgerCreates).toEqual([ + expect.objectContaining({ + notes: "Gary edited cost 2.5 → 3.2 from /stock", + }), + ]); + }); }); diff --git a/src/lib/server/stock-pricing-service.ts b/src/lib/server/stock-pricing-service.ts index c3ffe2b..8707a4b 100644 --- a/src/lib/server/stock-pricing-service.ts +++ b/src/lib/server/stock-pricing-service.ts @@ -152,11 +152,14 @@ export async function updateStockItemsPricing( } // 3.5. Cost audit — write a stock_ledger row ONLY when costPrice was - // supplied and differs from the existing cost_price. Side-effect: - // a failed ledger write aborts THIS row (we won't half-write the - // patch without recording the audit trail). + // supplied AND the prior cost was a real number AND it differs. + // First-time pricing (before.cost_price undefined) is NOT a + // cost movement to audit — it's a field initialisation. + // Side-effect: a failed ledger write aborts THIS row (we won't + // half-write the patch without recording the audit trail). if ( update.costPrice !== undefined && + Number.isFinite(before.cost_price) && before.cost_price !== update.costPrice ) { try { @@ -166,7 +169,7 @@ export async function updateStockItemsPricing( unit_cost: update.costPrice, reason: "adjustment", reference_type: "manual", - notes: `${input.actor.name} edited cost from /stock`, + notes: `${input.actor.name} edited cost ${before.cost_price} → ${update.costPrice} from /stock`, created_by: input.actor.id, }); } catch (err) {