fix(stock): skip ledger row on first-time pricing + include cost delta in notes
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.
This commit is contained in:
parent
202f5e73e2
commit
86eba97d33
2 changed files with 49 additions and 4 deletions
|
|
@ -143,4 +143,46 @@ describe("updateStockItemsPricing — stock_ledger audit on cost change", () =>
|
||||||
|
|
||||||
expect(ledgerCreates).toEqual([]);
|
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",
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -152,11 +152,14 @@ export async function updateStockItemsPricing(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.5. Cost audit — write a stock_ledger row ONLY when costPrice was
|
// 3.5. Cost audit — write a stock_ledger row ONLY when costPrice was
|
||||||
// supplied and differs from the existing cost_price. Side-effect:
|
// supplied AND the prior cost was a real number AND it differs.
|
||||||
// a failed ledger write aborts THIS row (we won't half-write the
|
// First-time pricing (before.cost_price undefined) is NOT a
|
||||||
// patch without recording the audit trail).
|
// 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 (
|
if (
|
||||||
update.costPrice !== undefined &&
|
update.costPrice !== undefined &&
|
||||||
|
Number.isFinite(before.cost_price) &&
|
||||||
before.cost_price !== update.costPrice
|
before.cost_price !== update.costPrice
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -166,7 +169,7 @@ export async function updateStockItemsPricing(
|
||||||
unit_cost: update.costPrice,
|
unit_cost: update.costPrice,
|
||||||
reason: "adjustment",
|
reason: "adjustment",
|
||||||
reference_type: "manual",
|
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,
|
created_by: input.actor.id,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue