Files
star-erp/resources/js/utils/inventory.ts
2025-12-30 15:03:19 +08:00

165 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 庫存計算相關工具函式
*/
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 | null | undefined
): SafetyStockStatus => {
if (!safetyStock || safetyStock === 0) return "正常";
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) => String(inv.warehouseId) === String(warehouseId)
);
// 取得該倉庫的安全庫存設定
const warehouseSettings = safetyStockSettings.filter(
(s) => String(s.warehouseId) === String(warehouseId)
);
// 計算有多少商品低於安全庫存
let lowStockCount = 0;
warehouseSettings.forEach((setting) => {
// 找出該設定對應的商品庫存
// 注意:這裡假設一個商品在同一個倉庫只有一種庫存紀錄 (warehouse_inventory table)
// 如果有批號區分,則需要加總所有該商品的數量
const productTotalStock = warehouseInventories
.filter(inv => String(inv.productId) === String(setting.productId))
.reduce((sum, inv) => sum + inv.quantity, 0);
// 只有當安全庫存設定存在時才進行判斷 (後端已過濾掉 null)
// 若設定為 0則表示允許庫存為 0不會觸發警告 (除非庫存為負)
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);
};