import { permissions } from "@mrp/shared"; import type { PurchaseOrderDetailDto, PurchaseOrderStatus } from "@mrp/shared"; import type { WarehouseLocationOptionDto } from "@mrp/shared/dist/inventory/types.js"; import type { PurchaseReceiptInput } from "@mrp/shared/dist/purchasing/types.js"; import { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { useAuth } from "../../auth/AuthProvider"; import { FileAttachmentsPanel } from "../../components/FileAttachmentsPanel"; import { api, ApiError } from "../../lib/api"; import { emptyPurchaseReceiptInput, purchaseStatusOptions } from "./config"; import { PurchaseStatusBadge } from "./PurchaseStatusBadge"; export function PurchaseDetailPage() { const { token, user } = useAuth(); const { orderId } = useParams(); const [document, setDocument] = useState(null); const [locationOptions, setLocationOptions] = useState([]); const [receiptForm, setReceiptForm] = useState(emptyPurchaseReceiptInput); const [receiptQuantities, setReceiptQuantities] = useState>({}); const [receiptStatus, setReceiptStatus] = useState("Receive ordered material into inventory against this purchase order."); const [isSavingReceipt, setIsSavingReceipt] = useState(false); const [status, setStatus] = useState("Loading purchase order..."); const [isUpdatingStatus, setIsUpdatingStatus] = useState(false); const [isOpeningPdf, setIsOpeningPdf] = useState(false); const canManage = user?.permissions.includes("purchasing.write") ?? false; const canReceive = canManage && (user?.permissions.includes(permissions.inventoryWrite) ?? false); useEffect(() => { if (!token || !orderId) { return; } api.getPurchaseOrder(token, orderId) .then((nextDocument) => { setDocument(nextDocument); setStatus("Purchase order loaded."); }) .catch((error: unknown) => { const message = error instanceof ApiError ? error.message : "Unable to load purchase order."; setStatus(message); }); if (!canReceive) { return; } api.getWarehouseLocationOptions(token) .then((options) => { setLocationOptions(options); setReceiptForm((current: PurchaseReceiptInput) => { if (current.locationId) { return current; } const firstOption = options[0]; return firstOption ? { ...current, warehouseId: firstOption.warehouseId, locationId: firstOption.locationId, } : current; }); }) .catch(() => setLocationOptions([])); }, [canReceive, orderId, token]); useEffect(() => { if (!document) { return; } setReceiptQuantities((current) => { const next: Record = {}; for (const line of document.lines) { if (line.remainingQuantity > 0) { next[line.id] = current[line.id] ?? 0; } } return next; }); }, [document]); if (!document) { return
{status}
; } const activeDocument = document; const openLines = activeDocument.lines.filter((line) => line.remainingQuantity > 0); function updateReceiptField(key: Key, value: PurchaseReceiptInput[Key]) { setReceiptForm((current: PurchaseReceiptInput) => ({ ...current, [key]: value })); } function updateReceiptQuantity(lineId: string, quantity: number) { setReceiptQuantities((current: Record) => ({ ...current, [lineId]: quantity, })); } async function handleStatusChange(nextStatus: PurchaseOrderStatus) { if (!token) { return; } setIsUpdatingStatus(true); setStatus("Updating purchase order status..."); try { const nextDocument = await api.updatePurchaseOrderStatus(token, activeDocument.id, nextStatus); setDocument(nextDocument); setStatus("Purchase order status updated."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to update purchase order status."; setStatus(message); } finally { setIsUpdatingStatus(false); } } async function handleReceiptSubmit(event: React.FormEvent) { event.preventDefault(); if (!token || !canReceive) { return; } setIsSavingReceipt(true); setReceiptStatus("Posting purchase receipt..."); try { const payload: PurchaseReceiptInput = { ...receiptForm, lines: openLines .map((line) => ({ purchaseOrderLineId: line.id, quantity: Math.max(0, Math.floor(receiptQuantities[line.id] ?? 0)), })) .filter((line) => line.quantity > 0), }; const nextDocument = await api.createPurchaseReceipt(token, activeDocument.id, payload); setDocument(nextDocument); setReceiptQuantities({}); setReceiptForm((current: PurchaseReceiptInput) => ({ ...current, receivedAt: new Date().toISOString(), notes: "", })); setReceiptStatus("Purchase receipt recorded."); setStatus("Purchase order updated after receipt."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to record purchase receipt."; setReceiptStatus(message); } finally { setIsSavingReceipt(false); } } async function handleOpenPdf() { if (!token) { return; } setIsOpeningPdf(true); setStatus("Rendering purchase order PDF..."); try { const blob = await api.getPurchaseOrderPdf(token, activeDocument.id); const objectUrl = URL.createObjectURL(blob); window.open(objectUrl, "_blank", "noopener,noreferrer"); window.setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000); setStatus("Purchase order PDF ready."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to render purchase order PDF."; setStatus(message); } finally { setIsOpeningPdf(false); } } return (

Purchase Order

{activeDocument.documentNumber}

{activeDocument.vendorName}

Back to purchase orders {canManage ? ( Edit purchase order ) : null}
{canManage ? (

Quick Actions

Update purchase-order status without opening the full editor.

{purchaseStatusOptions.map((option) => ( ))}
) : null}

Issue Date

{new Date(activeDocument.issueDate).toLocaleDateString()}

Lines

{activeDocument.lineCount}

Receipts

{activeDocument.receipts.length}

Qty Remaining

{activeDocument.lines.reduce((sum, line) => sum + line.remainingQuantity, 0)}

Subtotal

${activeDocument.subtotal.toFixed(2)}

Total

${activeDocument.total.toFixed(2)}

Tax

${activeDocument.taxAmount.toFixed(2)}
{activeDocument.taxPercent.toFixed(2)}%

Freight

${activeDocument.freightAmount.toFixed(2)}

Payment Terms

{activeDocument.paymentTerms || "N/A"}

Currency

{activeDocument.currencyCode || "USD"}

Vendor

Account
{activeDocument.vendorName}
Email
{activeDocument.vendorEmail}

Notes

{activeDocument.notes || "No notes recorded for this document."}

Line Items

{activeDocument.lines.length === 0 ? (
No line items have been added yet.
) : (
{activeDocument.lines.map((line: PurchaseOrderDetailDto["lines"][number]) => ( ))}
ItemDescriptionOrderedReceivedRemainingUOMUnit CostTotal
{line.itemSku}
{line.itemName}
{line.description} {line.quantity} {line.receivedQuantity} {line.remainingQuantity} {line.unitOfMeasure} ${line.unitCost.toFixed(2)} ${line.lineTotal.toFixed(2)}
)}
{canReceive ? (

Purchase Receiving

Receive material

Post received quantities to inventory and retain a receipt record against this order.

{openLines.length === 0 ? (
All ordered quantities have been received for this purchase order.
) : (