feat(pdf): include VAT calculation in stock purchase document

- Added VAT calculation to the stock purchase document PDF, displaying VAT (15%) alongside the subtotal and total.
- Updated the layout to reflect the new totals structure, ensuring consistency with customer PO documents.
- Revised comments for clarity on the handling of total amounts and VAT reconciliation.
This commit is contained in:
Dave 2026-08-19 11:45:10 +02:00
parent 43bbdfc458
commit d194603dce

View file

@ -1,7 +1,7 @@
import { Document, Page, View, Text, Image, StyleSheet } from "@react-pdf/renderer";
import type { Letterhead } from "@/lib/letterhead";
import type { StockPurchase, StockPurchaseItem, Supplier } from "@/lib/types";
import { formatZAR, formatDateLong } from "./format";
import { formatZAR, formatDateLong, vatAmount } from "./format";
import { defaultAccentFor } from "@/lib/letterhead";
/**
@ -57,14 +57,15 @@ export interface StockPurchaseDocumentProps {
* Cost / Line Total). The SKU column is new vs. customer POs —
* stock POs reference stock_items by SKU so the supplier can
* cross-check the line.
* - right-aligned totals (Subtotal + Total)
* - right-aligned totals (Subtotal + VAT (15%) + Total — rule above Total)
*
* Intentionally stripped (vs. the customer PO PDF):
* - **No Order Ref line** — stock POs have no customer order. The
* supplier must NOT learn anything about the end customer. Same
* confidentiality rule as customer POs.
* - **No banking details / no VAT line** — same rationale as the
* customer PO: we pay what the supplier quotes, no markup math.
* - **No banking details** — supplier invoices via the normal
* commercial channel; the PO is an instruction to supply, not
* a payment instruction.
*
* The `STK` suffix on the PO number (e.g. `PO26071601STK`) is the
* in-band signal to the supplier that this is a stock order, not a
@ -96,11 +97,20 @@ export function StockPurchaseDocument({
0
) * 100
) / 100;
// Re-use the supplied snapshot if it differs from the live
// computation by < 0.01 — caller might have included an explicit
// total_amount that we want to honour (e.g. from the regenerate
// API where the PO record already has total_amount set).
const total = Math.abs(totalAmount - subtotal) < 0.01 ? totalAmount : subtotal;
// 15% VAT on top of the supplier-quoted subtotal — same helper the
// customer PO and quote PDFs use, so all three documents compute
// VAT identically. Total = subtotal + vat, rounded to 2dp so the
// line matches the printed figures exactly.
//
// The `totalAmount` prop is carried for API backwards-compat with
// the existing route (the stock_purchases row stores the VAT-ex
// subtotal there); we deliberately ignore it for the printed Total
// because the office reconciles VAT returns from the printed PDF,
// not the stored field. Mirrors the customer PO behaviour
// (`PurchaseOrderDocument` does the same).
void totalAmount;
const vat = vatAmount(subtotal);
const totalInclVat = Math.round((subtotal + vat) * 100) / 100;
return (
<Document
@ -212,18 +222,25 @@ export function StockPurchaseDocument({
})}
</View>
{/* Totals — just Subtotal + Total. No VAT (we're buying, the
supplier's price is what we pay). Rule above Total mirrors
the customer-PO totals block so the two feel like siblings. */}
{/* Totals — Subtotal + VAT (15%) + rule + Total. Mirrors the
customer-PO totals block so the two PDFs feel like
siblings when filed side-by-side. `Subtotal` is the VAT-ex
sum (stored on `stock_purchases.total_amount`); the printed
`Total` is `subtotal × 1.15` — the figure the office uses
for budget / VAT-return reconciliation. */}
<View style={styles.totals}>
<View style={styles.totalLine}>
<Text style={styles.totalLabel}>Subtotal:</Text>
<Text style={styles.totalValue}>{formatZAR(subtotal)}</Text>
</View>
<View style={styles.totalLine}>
<Text style={styles.totalLabel}>VAT (15%):</Text>
<Text style={styles.totalValue}>{formatZAR(vat)}</Text>
</View>
<View style={styles.totalRule} />
<View style={styles.totalLine}>
<Text style={styles.totalFinalLabel}>Total:</Text>
<Text style={styles.totalFinalValue}>{formatZAR(total)}</Text>
<Text style={styles.totalFinalValue}>{formatZAR(totalInclVat)}</Text>
</View>
</View>
</Page>