import * as db from "./db"; import { materialEventInventory, materialEventItems, materialEventReplacementValues, type MaterialEventItemKey, } from "@shared/materialEvent"; export const LOGISTICS_SETTINGS_KEY = "system.logistics.settings"; export type LogisticsSettings = { materialReturnLeadDays: number; materialReturnGraceDays: number; inventory: Record; replacementValues: Record; updatedAt: string | null; }; export const defaultLogisticsSettings: Omit = { materialReturnLeadDays: 4, materialReturnGraceDays: 3, inventory: { ...materialEventInventory, }, replacementValues: { ...materialEventReplacementValues, }, }; function clampWholeNumber(value: unknown, fallback: number, min: number, max: number) { const parsed = Number.parseInt(String(value ?? ""), 10); if (!Number.isFinite(parsed)) return fallback; return Math.min(max, Math.max(min, parsed)); } function sanitizeInventory(rawInventory: unknown) { const nextInventory = { ...defaultLogisticsSettings.inventory } as Record; if (!rawInventory || typeof rawInventory !== "object") { return nextInventory; } for (const item of materialEventItems) { const rawValue = (rawInventory as Record)[item.key]; if (rawValue === null || rawValue === "") { nextInventory[item.key] = null; continue; } const parsed = Number.parseInt(String(rawValue ?? ""), 10); if (Number.isFinite(parsed) && parsed >= 0) { nextInventory[item.key] = parsed; } } return nextInventory; } function sanitizeReplacementValues(rawReplacementValues: unknown) { const nextReplacementValues = { ...defaultLogisticsSettings.replacementValues, } as Record; if (!rawReplacementValues || typeof rawReplacementValues !== "object") { return nextReplacementValues; } for (const item of materialEventItems) { const rawValue = (rawReplacementValues as Record)[item.key]; if (rawValue === null || rawValue === "") { nextReplacementValues[item.key] = null; continue; } const parsed = Number.parseInt(String(rawValue ?? ""), 10); if (Number.isFinite(parsed) && parsed >= 0) { nextReplacementValues[item.key] = parsed; } } return nextReplacementValues; } function sanitizeLogisticsSettings(rawValue: unknown, updatedAt: string | null): LogisticsSettings { const source = rawValue && typeof rawValue === "object" ? rawValue as Record : {}; return { materialReturnLeadDays: clampWholeNumber( source.materialReturnLeadDays, defaultLogisticsSettings.materialReturnLeadDays, 1, 30 ), materialReturnGraceDays: clampWholeNumber( source.materialReturnGraceDays, defaultLogisticsSettings.materialReturnGraceDays, 0, 30 ), inventory: sanitizeInventory(source.inventory), replacementValues: sanitizeReplacementValues(source.replacementValues), updatedAt, }; } export async function getLogisticsSettings(): Promise { const record = await db.getPortalSettingRecord(LOGISTICS_SETTINGS_KEY); if (!record?.valeur) { return { ...defaultLogisticsSettings, updatedAt: null, }; } try { const parsed = JSON.parse(record.valeur); return sanitizeLogisticsSettings(parsed, record.updatedAt?.toISOString?.() || null); } catch { return { ...defaultLogisticsSettings, updatedAt: record.updatedAt?.toISOString?.() || null, }; } } export async function saveLogisticsSettings(input: { materialReturnLeadDays?: number; materialReturnGraceDays?: number; inventory?: Partial>; replacementValues?: Partial>; }) { const current = await getLogisticsSettings(); const next = sanitizeLogisticsSettings( { materialReturnLeadDays: input.materialReturnLeadDays ?? current.materialReturnLeadDays, materialReturnGraceDays: input.materialReturnGraceDays ?? current.materialReturnGraceDays, inventory: { ...current.inventory, ...(input.inventory || {}), }, replacementValues: { ...current.replacementValues, ...(input.replacementValues || {}), }, }, new Date().toISOString() ); await db.setPortalSetting( LOGISTICS_SETTINGS_KEY, JSON.stringify({ materialReturnLeadDays: next.materialReturnLeadDays, materialReturnGraceDays: next.materialReturnGraceDays, inventory: next.inventory, replacementValues: next.replacementValues, }), "Réglages logistiques globaux du portail" ); return getLogisticsSettings(); }