updated validations
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
const UOM_LABELS: Record<string, string> = {
|
||||||
|
BAG: "Bag",
|
||||||
|
BOX: "Box",
|
||||||
|
BTL: "Bottle",
|
||||||
|
CAN: "Can",
|
||||||
|
CM: "Centimeter",
|
||||||
|
CTN: "Carton",
|
||||||
|
DOZ: "Dozen",
|
||||||
|
EA: "Each",
|
||||||
|
G: "Gram",
|
||||||
|
KG: "Kilogram",
|
||||||
|
L: "Litre",
|
||||||
|
M: "Meter",
|
||||||
|
ML: "Millilitre",
|
||||||
|
MM: "Millimeter",
|
||||||
|
PACK: "Pack",
|
||||||
|
PCS: "Pieces",
|
||||||
|
PK: "Pack",
|
||||||
|
PKT: "Packet",
|
||||||
|
ROLL: "Roll",
|
||||||
|
SET: "Set",
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatUomName(name: string | null | undefined): string {
|
||||||
|
const normalized = name?.trim()
|
||||||
|
if (!normalized) return ""
|
||||||
|
|
||||||
|
const mapped = UOM_LABELS[normalized.toUpperCase()]
|
||||||
|
if (mapped) return mapped
|
||||||
|
|
||||||
|
if (/^[A-Z0-9/_-]+$/.test(normalized)) {
|
||||||
|
return normalized.charAt(0) + normalized.slice(1).toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findUomLabel(uoms: Array<{ uomId: number; name: string }>, uomId: number): string {
|
||||||
|
const name = uoms.find((uom) => uom.uomId === uomId)?.name
|
||||||
|
return name ? formatUomName(name) : `#${uomId}`
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
type SalesEditableLine = {
|
||||||
|
itemId: number
|
||||||
|
uomId: number
|
||||||
|
warehouseId: number
|
||||||
|
qty: number
|
||||||
|
freeQty: number
|
||||||
|
unitPrice?: number | null
|
||||||
|
discountPct?: number
|
||||||
|
taxPct?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type SalesDocumentValidationInput = {
|
||||||
|
customerId: number | null
|
||||||
|
warehouseId: number | null
|
||||||
|
cashierUserId?: number | null
|
||||||
|
requireCashierUser?: boolean
|
||||||
|
lines: SalesEditableLine[]
|
||||||
|
lineLabel?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BundleEditableLine = {
|
||||||
|
itemId: number
|
||||||
|
uomId: number
|
||||||
|
qty: number
|
||||||
|
unitPrice: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type BundleValidationInput = {
|
||||||
|
customerId: number | null
|
||||||
|
warehouseId: number | null
|
||||||
|
cashierUserId: number | null
|
||||||
|
templateId: number | null
|
||||||
|
bundleName: string
|
||||||
|
bundlePrice: number
|
||||||
|
lines: BundleEditableLine[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function invalidNumber(value: number | null | undefined) {
|
||||||
|
return value === null || value === undefined || !Number.isFinite(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateSalesDocument(input: SalesDocumentValidationInput): string | null {
|
||||||
|
const {
|
||||||
|
customerId,
|
||||||
|
warehouseId,
|
||||||
|
cashierUserId,
|
||||||
|
requireCashierUser = false,
|
||||||
|
lines,
|
||||||
|
lineLabel = "line",
|
||||||
|
} = input
|
||||||
|
|
||||||
|
if (!customerId) return "Select a customer."
|
||||||
|
if (!warehouseId) return "Select a warehouse."
|
||||||
|
if (requireCashierUser && !cashierUserId) return "Select a cashier."
|
||||||
|
if (lines.length === 0) return `Add at least one ${lineLabel}.`
|
||||||
|
|
||||||
|
for (const [index, line] of lines.entries()) {
|
||||||
|
const row = index + 1
|
||||||
|
if (!line.itemId) return `Select an item for ${lineLabel} ${row}.`
|
||||||
|
if (!line.uomId) return `Select a valid UOM for ${lineLabel} ${row}.`
|
||||||
|
if (!line.warehouseId) return `Select a warehouse for ${lineLabel} ${row}.`
|
||||||
|
if (warehouseId && line.warehouseId !== warehouseId) {
|
||||||
|
return `${lineLabel[0]?.toUpperCase() ?? "L"}${lineLabel.slice(1)} ${row} warehouse must match the selected header warehouse.`
|
||||||
|
}
|
||||||
|
if (invalidNumber(line.qty) || line.qty <= 0) return `Enter a quantity greater than zero for ${lineLabel} ${row}.`
|
||||||
|
if (invalidNumber(line.freeQty) || line.freeQty < 0) return `Enter a valid free quantity for ${lineLabel} ${row}.`
|
||||||
|
if (line.unitPrice !== null && line.unitPrice !== undefined && (invalidNumber(line.unitPrice) || line.unitPrice < 0)) {
|
||||||
|
return `Enter a valid unit price for ${lineLabel} ${row}.`
|
||||||
|
}
|
||||||
|
if (line.discountPct !== undefined && (invalidNumber(line.discountPct) || line.discountPct < 0 || line.discountPct > 100)) {
|
||||||
|
return `Enter a discount percentage between 0 and 100 for ${lineLabel} ${row}.`
|
||||||
|
}
|
||||||
|
if (line.taxPct !== undefined && (invalidNumber(line.taxPct) || line.taxPct < 0 || line.taxPct > 100)) {
|
||||||
|
return `Enter a tax percentage between 0 and 100 for ${lineLabel} ${row}.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateBundleSale(input: BundleValidationInput): string | null {
|
||||||
|
const { customerId, warehouseId, cashierUserId, templateId, bundleName, bundlePrice, lines } = input
|
||||||
|
|
||||||
|
if (!customerId) return "Select a customer."
|
||||||
|
if (!warehouseId) return "Select a warehouse."
|
||||||
|
if (!cashierUserId) return "Select a cashier."
|
||||||
|
if (!templateId) return "Select a bundle template."
|
||||||
|
if (!bundleName.trim()) return "Enter a bundle name."
|
||||||
|
if (invalidNumber(bundlePrice) || bundlePrice < 0) return "Enter a valid bundle price."
|
||||||
|
if (lines.length === 0) return "Add at least one bundle component line."
|
||||||
|
|
||||||
|
for (const [index, line] of lines.entries()) {
|
||||||
|
const row = index + 1
|
||||||
|
if (!line.itemId) return `Select an item for component line ${row}.`
|
||||||
|
if (!line.uomId) return `Select a valid UOM for component line ${row}.`
|
||||||
|
if (invalidNumber(line.qty) || line.qty <= 0) return `Enter a quantity greater than zero for component line ${row}.`
|
||||||
|
if (invalidNumber(line.unitPrice) || line.unitPrice < 0) return `Enter a valid unit price for component line ${row}.`
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user