export type StockQtyRoundMode = "CEILING" | "FLOOR"; export type StockQtyRoundSource = "CREATE" | "QC"; export type StockQtyRoundChoice = { mode: StockQtyRoundMode; before: number; after: number; }; /** FP-MTMS Version Checklist | Functions Ref. No. 56 | v1.0.3 | 2026-09-10 */ export function previewPoBatchStockQty( orderM18Qty: number, orderStockQty: number, batchM18Qty: number, ): number { if (!Number.isFinite(orderM18Qty) || orderM18Qty === 0) { return Number(batchM18Qty.toFixed(2)); } return Number(((batchM18Qty * orderStockQty) / orderM18Qty).toFixed(2)); } export function isNotIntegerQty(qty: number): boolean { if (!Number.isFinite(qty)) return false; return Math.abs(qty - Math.round(qty)) > 1e-9; } /** * FP-MTMS Version Checklist | Functions Ref. No. 56 | v1.0.3 | 2026-09-10 * PO 收貨/品檢:pending 或 escalated 且庫存數量非整數時需選擇 CEILING / FLOOR */ export function needsPoQcStockQtyRound( purchaseOrderLineId: number | null | undefined, status: string | null | undefined, acceptedQty: number | null | undefined, ): boolean { if (!purchaseOrderLineId) return false; const silStatus = (status ?? "").toLowerCase().trim(); if (silStatus !== "pending" && silStatus !== "escalated") return false; return isNotIntegerQty(Number(acceptedQty ?? 0)); } /** FP-MTMS Version Checklist | Functions Ref. No. 56 | v1.0.3 | 2026-09-10 */ export function roundStockQty(before: number, mode: StockQtyRoundMode): number { if (mode === "CEILING") return Math.ceil(before); return Math.floor(before); }