|
|
/**
|
|
|
* 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");
|
|
|
}
|