You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
4.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* Flock Freight 登录态选项组合约束
* 依据:官网表单校验(仅冷藏须温控)、调度重量锁、批量探测易无价组合
*/
import { FLOCK_LIMITS } from "@/lib/constants/flock-limits";
export type FlockCompatIssue = {
severity: "block" | "warn";
code: string;
message: string;
};
/** 探测与官网实测:易无报价的地点类型(仍可选,但提示) */
export const FLOCK_LOCATION_QUOTE_RISK: Readonly<Record<string, string>> = {
limited_construction:
"「施工工地」地点类型询价常无结果,建议改选「商业地址(无月台)」并勾选尾板/托盘车",
limited_farm:
"「农场 / 种养殖地」询价常无结果,建议改选「住宅」或「商业地址(无月台)」",
limited_military:
"「军事基地」等受限通道常需通行证,询价易失败,请确认承运商可进出后再选",
limited_airport:
"「机场」受限通道询价易失败或需额外手续,建议优先商业地址",
limited_port:
"「港口码头」受限通道询价易失败或需额外手续,建议优先商业地址",
};
/** 易无价包装 */
export const FLOCK_PACKAGING_QUOTE_RISK: Readonly<Record<string, string>> = {
units: "「单件 / 整机」包装询价常无结果,建议改用纸箱、木箱或标准托盘",
pails: "「小桶 / 提桶」单独作为主包装时询价不稳定,建议改用纸箱或托盘",
};
export function isFlockReeferOnlyWithoutTemp(input: {
vehicleTypes: readonly string[];
additionalServices: readonly string[];
}): boolean {
const vehicles = input.vehicleTypes.map((v) => v.trim()).filter(Boolean);
if (vehicles.length === 0) return false;
const onlyReefer = vehicles.every((v) => v === "refrigerated");
if (!onlyReefer) return false;
return !input.additionalServices.includes("temperature_control");
}
export function isFlockTempWithoutReefer(input: {
vehicleTypes: readonly string[];
additionalServices: readonly string[];
}): boolean {
if (!input.additionalServices.includes("temperature_control")) return false;
return !input.vehicleTypes.includes("refrigerated");
}
export function isFlockSprinterOnly(
vehicleTypes: readonly string[],
): boolean {
const vehicles = vehicleTypes.map((v) => v.trim()).filter(Boolean);
return vehicles.length > 0 && vehicles.every((v) => v === "sprinter");
}
export function evaluateFlockOptionCompat(input: {
pickupLocationType: string;
deliveryLocationType: string;
packagingTypes: readonly string[];
vehicleTypes: readonly string[];
additionalServices: readonly string[];
shipmentWeightLb?: number;
}): FlockCompatIssue[] {
const issues: FlockCompatIssue[] = [];
if (
isFlockReeferOnlyWithoutTemp({
vehicleTypes: input.vehicleTypes,
additionalServices: input.additionalServices,
})
) {
issues.push({
severity: "block",
code: "flock_reefer_needs_temp",
message:
"仅允许「冷藏车」时须同时勾选附加服务「温控 / 冷链」,否则官网无法提交询价",
});
}
if (
isFlockTempWithoutReefer({
vehicleTypes: input.vehicleTypes,
additionalServices: input.additionalServices,
})
) {
issues.push({
severity: "block",
code: "flock_temp_needs_reefer",
message: "已勾选「温控 / 冷链」时,允许车型中须包含「冷藏车」",
});
}
if (isFlockSprinterOnly(input.vehicleTypes)) {
issues.push({
severity: "warn",
code: "flock_sprinter_only",
message:
"仅选「小型厢车」时易无报价,建议同时勾选厢式货车或干货挂车",
});
}
for (const side of ["pickup", "delivery"] as const) {
const typeId =
side === "pickup" ? input.pickupLocationType : input.deliveryLocationType;
const risk = FLOCK_LOCATION_QUOTE_RISK[typeId];
if (risk) {
issues.push({
severity: "warn",
code: `flock_loc_risk_${side}_${typeId}`,
message: `${side === "pickup" ? "提货" : "派送"}${risk}`,
});
}
}
for (const pack of input.packagingTypes) {
const risk = FLOCK_PACKAGING_QUOTE_RISK[pack];
if (risk) {
issues.push({
severity: "warn",
code: `flock_pack_risk_${pack}`,
message: risk,
});
}
}
const wt = input.shipmentWeightLb;
if (
typeof wt === "number" &&
Number.isFinite(wt) &&
wt > 0 &&
wt <= FLOCK_LIMITS.schedulingWeightMinLb
) {
issues.push({
severity: "warn",
code: "flock_scheduling_weight",
message: `整票总重不超过 ${FLOCK_LIMITS.schedulingWeightMinLb} lb 时,部分预约/时间窗调度选项不可用(官网灰色)`,
});
}
return issues;
}
export function flockOptionHasBlock(
issues: readonly FlockCompatIssue[],
): boolean {
return issues.some((i) => i.severity === "block");
}