add product stock preview
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import type { ProductStock } from "@/types/Product";
|
||||
import { addPackageQuantity, comparePackageQuantity } from "@/utils/package";
|
||||
|
||||
export type ProductStockRequirement = {
|
||||
skuId: string;
|
||||
baseQuantity: string;
|
||||
productName: string;
|
||||
unitName: string;
|
||||
stockEnabled: boolean;
|
||||
};
|
||||
|
||||
export type ProductStockPreviewItem = ProductStockRequirement & {
|
||||
availableQuantity: string;
|
||||
shortageQuantity?: string;
|
||||
};
|
||||
|
||||
/** 合并同一 SKU 的基础库存需求,避免多单位订单拆开校验造成误判。 */
|
||||
export function mergeProductStockRequirements(requirementList: ProductStockRequirement[]): ProductStockRequirement[] {
|
||||
const requirementMap = new Map<string, ProductStockRequirement>();
|
||||
for (const requirement of requirementList) {
|
||||
const existing = requirementMap.get(requirement.skuId);
|
||||
if (!existing) {
|
||||
requirementMap.set(requirement.skuId, { ...requirement });
|
||||
continue;
|
||||
}
|
||||
const baseQuantity = addPackageQuantity(existing.baseQuantity, requirement.baseQuantity);
|
||||
if (baseQuantity) {
|
||||
existing.baseQuantity = baseQuantity;
|
||||
}
|
||||
}
|
||||
return [...requirementMap.values()];
|
||||
}
|
||||
|
||||
/** 返回库存不足提示;库存单位统一为 SKU 基础单位。 */
|
||||
export function getProductStockShortageMessage(stockList: ProductStock[], requirementList: ProductStockRequirement[]): string | undefined {
|
||||
const stockMap = new Map(stockList.map(stock => [stock.skuId, stock]));
|
||||
const shortageList: string[] = [];
|
||||
for (const requirement of requirementList) {
|
||||
if (!requirement.stockEnabled) {
|
||||
continue;
|
||||
}
|
||||
const availableQuantity = stockMap.get(requirement.skuId)?.availableQuantity ?? "0";
|
||||
if (!(0 < comparePackageQuantity(requirement.baseQuantity, availableQuantity))) {
|
||||
continue;
|
||||
}
|
||||
const shortageQuantity = subtractPackageQuantity(requirement.baseQuantity, availableQuantity);
|
||||
shortageList.push(`${requirement.productName} 可用 ${availableQuantity} ${requirement.unitName},缺少 ${shortageQuantity} ${requirement.unitName}`);
|
||||
}
|
||||
return shortageList.length ? `库存不足:${shortageList.join(";")}` : undefined;
|
||||
}
|
||||
|
||||
/** 生成启用库存管理 SKU 的库存预检明细。 */
|
||||
export function buildProductStockPreview(stockList: ProductStock[], requirementList: ProductStockRequirement[]): ProductStockPreviewItem[] {
|
||||
const stockMap = new Map(stockList.map(stock => [stock.skuId, stock]));
|
||||
return requirementList.filter(requirement => requirement.stockEnabled).map(requirement => {
|
||||
const availableQuantity = String(stockMap.get(requirement.skuId)?.availableQuantity ?? "0");
|
||||
const shortageQuantity = 0 < comparePackageQuantity(requirement.baseQuantity, availableQuantity)
|
||||
? subtractPackageQuantity(requirement.baseQuantity, availableQuantity)
|
||||
: undefined;
|
||||
return { ...requirement, availableQuantity, shortageQuantity };
|
||||
});
|
||||
}
|
||||
|
||||
function subtractPackageQuantity(left: string, right: number | string): string {
|
||||
const leftParts = toQuantityParts(left);
|
||||
const rightParts = toQuantityParts(right);
|
||||
if (!leftParts || !rightParts) {
|
||||
return left;
|
||||
}
|
||||
const scale = Math.max(leftParts.scale, rightParts.scale);
|
||||
const result = leftParts.value * 10n ** BigInt(scale - leftParts.scale)
|
||||
- rightParts.value * 10n ** BigInt(scale - rightParts.scale);
|
||||
const text = result.toString().padStart(scale + 1, "0");
|
||||
const integerText = scale ? text.slice(0, -scale) : text;
|
||||
const fractionText = scale ? text.slice(-scale).replace(/0+$/, "") : "";
|
||||
return fractionText ? `${integerText}.${fractionText}` : integerText;
|
||||
}
|
||||
|
||||
function toQuantityParts(value: number | string): { value: bigint; scale: number } | undefined {
|
||||
const text = String(value).trim();
|
||||
if (!/^\d+(?:\.\d{1,3})?$/.test(text)) {
|
||||
return undefined;
|
||||
}
|
||||
const [integerText, fractionText = ""] = text.split(".");
|
||||
return { value: BigInt(`${integerText}${fractionText}`), scale: fractionText.length };
|
||||
}
|
||||
Reference in New Issue
Block a user