fix(po): store the price from the form so POs stop printing £0.00
Every purchase order created from a stock item printed a £0.00 unit
price, even though a cost price was on the record and the form showed
the right number.
Cause: the PO snapshot took costPriceGBP from the stock item's stored
cost_price_gbp instead of from the form. The price the user sees (and
types) into the "Unit Price (£ GBP)" field landed in snapshot.costPrice,
which nothing renders, while the PO PDF renders snapshot.costPriceGBP
(pdf.ts: `£${item.costPriceGBP}`). So a price typed into the form was
discarded and the stock item's cached value was printed instead.
Because the stock item's cost_price_gbp was only written back to the
database AFTER the PO was created, the first PO of an item always showed
£0.00 and only the next one showed the price. Verified in production:
PO-20261023-002 stored costPrice 333.78 / costPriceGBP 0 and printed
£0.00, while the linked stock item's cost_price_gbp was 0 at the time
and was updated to 333.78 by the sync step three minutes later.
Fixes:
- PurchaseOrderForm: snapshot.costPriceGBP is now the price from the
form; snapshot.costPrice is the stock item's ZAR landed cost.
- PurchaseOrderForm: selecting a stock item with no GBP cost price
leaves the price field blank instead of showing £0.00, with a hint
that the supplier's GBP price still has to be entered. Most stock
items only carry a ZAR cost price, so £0.00 looked like a real price.
- EditPurchaseOrderDialog: preserve the ZAR cost price per row instead
of zeroing it when a PO is edited.
This commit is contained in:
parent
2b56053e1c
commit
2682eeb360
2 changed files with 27 additions and 6 deletions
|
|
@ -134,13 +134,18 @@ export function EditPurchaseOrderDialog({ open, onOpenChange, order, refreshData
|
|||
}))
|
||||
|
||||
// Build updated snapshot — keep stable ids + entry mode so the audit
|
||||
// trail remains meaningful.
|
||||
// trail remains meaningful. costPrice is the ZAR landed cost, so carry
|
||||
// the existing value over per row instead of zeroing it; costPriceGBP is
|
||||
// the price the user sees in the form and is what the PO PDF renders.
|
||||
const existingSnapshot = order.snapshotItems || []
|
||||
const updatedSnapshot = normalizedRows.map((r) => ({
|
||||
stockItemId: r.stockItemId,
|
||||
itemName: r.itemName,
|
||||
sku: r.sku,
|
||||
quantity: r.quantity,
|
||||
costPrice: 0,
|
||||
costPrice: Number(
|
||||
existingSnapshot.find((s) => s.stockItemId === r.stockItemId)?.costPrice || 0,
|
||||
),
|
||||
costPriceGBP: r.unitPriceGBP,
|
||||
entryMode: r.entryMode,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
|||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
|
|
@ -260,8 +261,12 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
|
|||
form.setValue(`items.${index}.stockItemId`, selectedStockItem.id, { shouldDirty: true });
|
||||
form.setValue(`items.${index}.itemName`, selectedStockItem.name, { shouldDirty: true });
|
||||
form.setValue(`items.${index}.sku`, selectedStockItem.sku, { shouldDirty: true });
|
||||
// Use GBP price for purchase orders
|
||||
form.setValue(`items.${index}.costPrice`, selectedStockItem.costPriceGBP, { shouldDirty: true, shouldValidate: true });
|
||||
// Use GBP price for purchase orders. Most stock items only carry a ZAR
|
||||
// cost price (cost_price), so leave the field BLANK instead of silently
|
||||
// showing £0.00 — a blank field tells the user the supplier's GBP price
|
||||
// still has to be entered, whereas "£0.00" looks like a real price.
|
||||
const gbpPrice = Number(selectedStockItem.costPriceGBP || 0);
|
||||
form.setValue(`items.${index}.costPrice`, gbpPrice > 0 ? gbpPrice : ("" as any), { shouldDirty: true, shouldValidate: true });
|
||||
// Leave quantity blank — user types the amount they want.
|
||||
form.setValue(`items.${index}.quantity`, "", { shouldDirty: true, shouldValidate: true });
|
||||
}
|
||||
|
|
@ -357,8 +362,14 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
|
|||
itemName: item.itemName,
|
||||
sku: item.sku,
|
||||
quantity: item.quantity,
|
||||
costPrice: Number(item.costPrice || 0),
|
||||
costPriceGBP: Number(stockItem?.costPriceGBP || 0),
|
||||
// ZAR landed cost, for reference only — the PO itself is priced in GBP.
|
||||
costPrice: Number(stockItem?.costPrice ?? 0),
|
||||
// The GBP unit price taken from the FORM. This is the only price
|
||||
// field the PO PDF renders (£${item.costPriceGBP}), so it must be the
|
||||
// value the user sees and edits. Reading it from the stock item
|
||||
// instead silently discarded every price typed into the form and
|
||||
// printed £0.00 on the PO.
|
||||
costPriceGBP: Number(item.costPrice || 0),
|
||||
entryMode: 'stock',
|
||||
});
|
||||
}
|
||||
|
|
@ -755,6 +766,11 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
|
|||
onChange={(v) => field.onChange(v)}
|
||||
/>
|
||||
</FormControl>
|
||||
{!(Number(field.value || 0) > 0) && (
|
||||
<FormDescription className="text-xs text-amber-600 dark:text-amber-400">
|
||||
No GBP cost price on this stock item — enter the supplier's GBP price.
|
||||
</FormDescription>
|
||||
)}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue