export const formatNum = (n: number) => Number.isFinite(n) ? n.toLocaleString(undefined, { maximumFractionDigits: 4 }) : "—"; /** Packaging / compound stock unit (e.g. 1包X25千克) — not a simple unit like 斤 or kg. */ export const isPackagingUom = (uom?: string | null): boolean => { const unit = uom?.trim(); if (!unit) return false; if (/^\d/.test(unit)) return true; if (/[xX×]/.test(unit) && /\d/.test(unit)) return true; return false; }; /** Join qty with UOM; packaging units use parentheses to avoid "6 1包…" ambiguity. */ export const joinQtyUom = (num: string, unit: string): string => isPackagingUom(unit) ? `${num}(${unit})` : `${num} ${unit}`; /** Format a quantity with its unit when available. */ export const formatQty = (qty: number | null | undefined, uom?: string | null): string => { if (qty == null || !Number.isFinite(Number(qty))) return "—"; const num = formatNum(Number(qty)); const unit = uom?.trim(); return unit ? joinQtyUom(num, unit) : num; }; /** Signed variance for stock adjustments: IN → +qty, OUT → −qty. */ export const formatSignedQty = ( qty: number | null | undefined, direction: string | null | undefined, uom?: string | null, ): string => { if (qty == null || !Number.isFinite(Number(qty))) return "—"; const abs = Math.abs(Number(qty)); const sign = (direction ?? "").trim().toUpperCase() === "OUT" ? "-" : "+"; const num = formatNum(abs); const unit = uom?.trim(); const signed = `${sign}${num}`; return unit ? joinQtyUom(signed, unit) : signed; };