first commit

This commit is contained in:
2025-12-30 15:03:19 +08:00
commit c735c36009
902 changed files with 83591 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
/**
* 格式化相關工具函式
*/
/**
* 格式化數字為千分位格式
*/
export const formatNumber = (num: number): string => {
return num.toLocaleString();
};
/**
* 格式化日期
*/
export const formatDate = (date: string): string => {
if (!date) return "-";
return new Date(date).toLocaleDateString("zh-TW");
};
/**
* 獲取當前日期YYYY-MM-DD 格式)
*/
export const getCurrentDate = (): string => {
return new Date().toISOString().split("T")[0];
};
/**
* 生成唯一 ID
*/
export const generateId = (): string => {
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
};
/**
* 生成撥補單號
*/
export const generateOrderNumber = (): string => {
return `TO${Date.now().toString().slice(-8)}`;
};
/**
* 生成批號
* 格式:{倉庫代碼}-{日期YYYYMMDD}-{流水號}
* 例如WH1-20251128-001
*/
export const generateBatchNumber = (
warehouseId: string,
date?: string,
sequence?: number
): string => {
const targetDate = date || getCurrentDate();
const dateStr = targetDate.replace(/-/g, "");
const seq = sequence || Math.floor(Math.random() * 1000);
const seqStr = seq.toString().padStart(3, "0");
return `WH${warehouseId}-${dateStr}-${seqStr}`;
};
/**
* 獲取當前日期時間YYYY-MM-DDTHH:mm 格式,用於 datetime-local input
*/
export const getCurrentDateTime = (): string => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day}T${hours}:${minutes}`;
};
/**
* 格式化日期時間顯示
*/
export const formatDateTime = (datetime: string): string => {
if (!datetime) return "-";
return new Date(datetime).toLocaleString("zh-TW", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
};

View File

@@ -0,0 +1,159 @@
/**
* 庫存計算相關工具函式
*/
import { WarehouseInventory, WarehouseStats, SafetyStockSetting, SafetyStockStatus } from "../types/warehouse";
/**
* 計算倉庫的總庫存數量
*/
export const calculateTotalQuantity = (
inventories: WarehouseInventory[],
warehouseId: string
): number => {
return inventories
.filter((inv) => inv.warehouseId === warehouseId)
.reduce((sum, inv) => sum + inv.quantity, 0);
};
/**
* 按商品分組計算總庫存
*/
export const calculateProductTotalStock = (
inventories: WarehouseInventory[],
productId: string
): number => {
return inventories
.filter((inv) => inv.productId === productId)
.reduce((sum, inv) => sum + inv.quantity, 0);
};
/**
* 計算安全庫存狀態
*/
export const getSafetyStockStatus = (
currentStock: number,
safetyStock: number
): SafetyStockStatus => {
const ratio = currentStock / safetyStock;
if (ratio >= 1.2) return "正常";
if (ratio >= 1.0) return "接近";
return "低於";
};
/**
* 檢查商品是否低於安全庫存
*/
export const isProductLowStock = (
inventories: WarehouseInventory[],
productId: string,
safetyStockSettings: SafetyStockSetting[]
): boolean => {
const setting = safetyStockSettings.find((s) => s.productId === productId);
if (!setting) return false;
const totalStock = calculateProductTotalStock(inventories, productId);
return totalStock < setting.safetyStock;
};
/**
* 計算低庫存警告數量(按商品計算)
*/
export const calculateLowStockCount = (
inventories: WarehouseInventory[],
warehouseId: string,
safetyStockSettings: SafetyStockSetting[]
): number => {
// 取得該倉庫的所有庫存
const warehouseInventories = inventories.filter(
(inv) => inv.warehouseId === warehouseId
);
// 取得該倉庫的安全庫存設定
const warehouseSettings = safetyStockSettings.filter(
(s) => s.warehouseId === warehouseId
);
// 計算有多少商品低於安全庫存
let lowStockCount = 0;
warehouseSettings.forEach((setting) => {
const productTotalStock = calculateProductTotalStock(
warehouseInventories,
setting.productId
);
if (productTotalStock < setting.safetyStock) {
lowStockCount++;
}
});
return lowStockCount;
};
/**
* 計算待撥補需求數量(按商品計算)
*/
export const calculateReplenishmentNeeded = (
inventories: WarehouseInventory[],
warehouseId: string,
safetyStockSettings: SafetyStockSetting[]
): number => {
// 取得該倉庫的所有庫存
const warehouseInventories = inventories.filter(
(inv) => inv.warehouseId === warehouseId
);
// 取得該倉庫的安全庫存設定
const warehouseSettings = safetyStockSettings.filter(
(s) => s.warehouseId === warehouseId
);
// 計算需要撥補的總量
let replenishmentNeeded = 0;
warehouseSettings.forEach((setting) => {
const productTotalStock = calculateProductTotalStock(
warehouseInventories,
setting.productId
);
if (productTotalStock < setting.safetyStock) {
replenishmentNeeded += setting.safetyStock - productTotalStock;
}
});
return replenishmentNeeded;
};
/**
* 計算倉庫統計資訊
*/
export const calculateWarehouseStats = (
inventories: WarehouseInventory[],
warehouseId: string,
safetyStockSettings: SafetyStockSetting[]
): WarehouseStats => {
return {
totalQuantity: calculateTotalQuantity(inventories, warehouseId),
lowStockCount: calculateLowStockCount(inventories, warehouseId, safetyStockSettings),
replenishmentNeeded: calculateReplenishmentNeeded(inventories, warehouseId, safetyStockSettings),
};
};
/**
* 檢查倉庫是否有庫存警告
*/
export const hasWarehouseWarning = (
inventories: WarehouseInventory[],
warehouseId: string,
safetyStockSettings: SafetyStockSetting[]
): boolean => {
return calculateLowStockCount(inventories, warehouseId, safetyStockSettings) > 0;
};
/**
* 過濾倉庫的庫存
*/
export const filterWarehouseInventories = (
inventories: WarehouseInventory[],
warehouseId: string
): WarehouseInventory[] => {
return inventories.filter((inv) => inv.warehouseId === warehouseId);
};

View File

@@ -0,0 +1,71 @@
/**
* 驗證相關工具函式
*/
import { TransferOrder } from "../types/warehouse";
/**
* 驗證撥補單表單資料
*/
export const validateTransferOrder = (formData: {
sourceWarehouseId: string;
targetWarehouseId: string;
productId: string;
quantity: number;
}): { isValid: boolean; error?: string } => {
if (!formData.sourceWarehouseId) {
return { isValid: false, error: "請選擇來源倉庫" };
}
if (!formData.targetWarehouseId) {
return { isValid: false, error: "請選擇目標倉庫" };
}
if (formData.sourceWarehouseId === formData.targetWarehouseId) {
return { isValid: false, error: "來源倉庫與目標倉庫不能相同" };
}
if (!formData.productId) {
return { isValid: false, error: "請選擇撥補商品" };
}
if (formData.quantity <= 0) {
return { isValid: false, error: "撥補數量必須大於0" };
}
return { isValid: true };
};
/**
* 驗證撥補數量是否超過可用庫存
*/
export const validateTransferQuantity = (
quantity: number,
availableQuantity: number
): { isValid: boolean; error?: string } => {
if (quantity > availableQuantity) {
return {
isValid: false,
error: `撥補數量不能超過可用庫存 (${availableQuantity})`,
};
}
return { isValid: true };
};
/**
* 驗證倉庫表單資料
*/
export const validateWarehouse = (formData: {
name: string;
address: string;
}): { isValid: boolean; error?: string } => {
if (!formData.name.trim()) {
return { isValid: false, error: "倉庫名稱為必填欄位" };
}
if (!formData.address.trim()) {
return { isValid: false, error: "倉庫地址為必填欄位" };
}
return { isValid: true };
};