+
+ Stock value (excl. VAT)
+ {grn.lines.reduce((s, l) => s + l.receivedValue, 0).toFixed(2)}
+
+
+ VAT
+ {grn.lines.reduce((s, l) => s + l.vatAmount, 0).toFixed(2)}
+
+
+ Document total
+ {grn.lines.reduce((s, l) => s + l.lineTotal, 0).toFixed(2)}
+
+
)
}
diff --git a/Frontend/erp-system/app/dashboard/receiving/grn/new/page.tsx b/Frontend/erp-system/app/dashboard/receiving/grn/new/page.tsx
index b230d3a..99ce3ef 100644
--- a/Frontend/erp-system/app/dashboard/receiving/grn/new/page.tsx
+++ b/Frontend/erp-system/app/dashboard/receiving/grn/new/page.tsx
@@ -37,12 +37,28 @@ interface DraftLine {
binId: number | null
qty: string
unitCost: string
+ /** PO line price when prefilled from a PO; drives the variance hint. */
+ poUnitPrice: number | null
+ discountPct: string
+ vatPct: string
holdStatus: HoldStatus
batchNo: string
expiryDate: string
serialNumbersText: string
}
+/** Mirror of the server's line arithmetic — display only (docs/20 §3, server stays authoritative). */
+function computeLine(l: DraftLine) {
+ const qty = Number(l.qty) || 0
+ const gross = Number(l.unitCost) || 0
+ const disc = Number(l.discountPct) || 0
+ const vat = Number(l.vatPct) || 0
+ const netUnitCost = gross * (1 - disc / 100)
+ const receivedValue = qty * netUnitCost
+ const vatAmount = receivedValue * (vat / 100)
+ return { netUnitCost, receivedValue, vatAmount, lineTotal: receivedValue + vatAmount }
+}
+
let keySeq = 0
function newKey() {
keySeq += 1
@@ -58,6 +74,9 @@ function emptyLine(): DraftLine {
binId: null,
qty: "",
unitCost: "",
+ poUnitPrice: null,
+ discountPct: "0",
+ vatPct: "0",
holdStatus: "Available",
batchNo: "",
expiryDate: "",
@@ -152,6 +171,9 @@ export default function NewGrnPage() {
binId: null,
qty: String(l.qty - l.qtyReceived),
unitCost: String(l.unitPrice),
+ poUnitPrice: l.unitPrice,
+ discountPct: "0",
+ vatPct: "0",
holdStatus: "Available",
batchNo: "",
expiryDate: "",
@@ -211,6 +233,8 @@ export default function NewGrnPage() {
uomId: line.uomId,
qty: line.qty,
unitCost: line.unitCost,
+ discountPct: line.discountPct,
+ vatPct: line.vatPct,
trackingMode: itemFor(line.itemId)?.trackingMode ?? null,
batchNo: line.batchNo,
serialNumbersText: line.serialNumbersText,
@@ -232,6 +256,8 @@ export default function NewGrnPage() {
binId: l.binId,
qty: Number(l.qty),
unitCost: Number(l.unitCost),
+ discountPct: Number(l.discountPct) || 0,
+ vatPct: Number(l.vatPct) || 0,
holdStatus: l.holdStatus,
batch: trackingMode === "Batch" ? { batchNo: l.batchNo.trim(), expiryDate: l.expiryDate || null } : null,
serialNumbers: trackingMode === "Serial" ? splitSerials(l.serialNumbersText) : null,
@@ -371,16 +397,20 @@ export default function NewGrnPage() {
{poLoading &&