161 lines
4.8 KiB
TypeScript
161 lines
4.8 KiB
TypeScript
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<MaterialEventItemKey, number | null>;
|
|
replacementValues: Record<MaterialEventItemKey, number | null>;
|
|
updatedAt: string | null;
|
|
};
|
|
|
|
export const defaultLogisticsSettings: Omit<LogisticsSettings, "updatedAt"> = {
|
|
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<MaterialEventItemKey, number | null>;
|
|
|
|
if (!rawInventory || typeof rawInventory !== "object") {
|
|
return nextInventory;
|
|
}
|
|
|
|
for (const item of materialEventItems) {
|
|
const rawValue = (rawInventory as Record<string, unknown>)[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<MaterialEventItemKey, number | null>;
|
|
|
|
if (!rawReplacementValues || typeof rawReplacementValues !== "object") {
|
|
return nextReplacementValues;
|
|
}
|
|
|
|
for (const item of materialEventItems) {
|
|
const rawValue = (rawReplacementValues as Record<string, unknown>)[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<string, unknown> : {};
|
|
|
|
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<LogisticsSettings> {
|
|
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<Record<MaterialEventItemKey, number | null>>;
|
|
replacementValues?: Partial<Record<MaterialEventItemKey, number | null>>;
|
|
}) {
|
|
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();
|
|
}
|