|
|
/**
|
|
|
* MotherShip 登录态官网硬约束(与 dashboard Create shipment 对齐)
|
|
|
*
|
|
|
* - 单件平均重量(Weight each)> 5000 lb → 官网不再提供报价
|
|
|
* - 整票总重 > 53 英尺干货厢式挂车最大载重(45000 lb)→ 不可询价
|
|
|
* - 不以托盘数量 1–25 为硬拒(各路径均取消该上限)
|
|
|
*/
|
|
|
export const MOTHERSHIP_MAX_AVG_WEIGHT_EACH_LB = 5000;
|
|
|
|
|
|
/** 53 英尺干货厢式挂车最大载重(lb) */
|
|
|
export const MOTHERSHIP_MAX_TOTAL_WEIGHT_LB = 45_000;
|
|
|
|
|
|
/** @deprecated 使用 MOTHERSHIP_MAX_AVG_WEIGHT_EACH_LB */
|
|
|
export const MOTHERSHIP_MAX_WEIGHT_EACH_LB = MOTHERSHIP_MAX_AVG_WEIGHT_EACH_LB;
|
|
|
|
|
|
export const MOTHERSHIP_AVG_WEIGHT_BLOCK_MESSAGE =
|
|
|
`单件平均重量超过 ${MOTHERSHIP_MAX_AVG_WEIGHT_EACH_LB} 磅时,无法提供报价`;
|
|
|
|
|
|
export const MOTHERSHIP_TOTAL_WEIGHT_BLOCK_MESSAGE =
|
|
|
`整票总重超过 53 英尺货车最大载重(${MOTHERSHIP_MAX_TOTAL_WEIGHT_LB.toLocaleString("en-US")} 磅),无法提供报价`;
|
|
|
|
|
|
/** 重量/尺寸仅接受整数;有小数须向上取整 */
|
|
|
export const MOTHERSHIP_INTEGER_HINT =
|
|
|
"重量与尺寸仅支持整数。有小数时系统将自动向上取整(进一)";
|
|
|
|
|
|
/** 一级「货物」标题旁录入限制提示(提前告知,勿等到查价页) */
|
|
|
export const MOTHERSHIP_CARGO_ENTRY_HINT_TITLE =
|
|
|
"录入提示 (提前告知,勿等到查价页)";
|
|
|
|
|
|
export const MOTHERSHIP_CARGO_ENTRY_HINT_ITEMS = [
|
|
|
"重量/尺寸支持小数录入 (lb / inch);点「开始询价」时自动向上取整 (进一) 再提交,例 25.68→26",
|
|
|
`「单件重」= Weight each,须 ≤ ${MOTHERSHIP_MAX_AVG_WEIGHT_EACH_LB} lb,超出不可询价`,
|
|
|
`整票总重 = Σ(单件重×数量) 须 ≤ ${MOTHERSHIP_MAX_TOTAL_WEIGHT_LB.toLocaleString("en-US")} lb (53 英尺干货厢式挂车最大载重),超出不可询价`,
|
|
|
] as const;
|
|
|
|
|
|
/**
|
|
|
* 官网不接受小数:正数向上取整(ceil)。
|
|
|
* 例:25.68 → 26,40.1 → 41,25 → 25
|
|
|
*/
|
|
|
export function ceilMothershipNumeric(value: number): number {
|
|
|
if (!Number.isFinite(value) || value <= 0) return value;
|
|
|
return Math.ceil(value);
|
|
|
}
|
|
|
|
|
|
export function hasMothershipFractionalPart(value: number): boolean {
|
|
|
return Number.isFinite(value) && value > 0 && !Number.isInteger(value);
|
|
|
}
|
|
|
|
|
|
/** YYYY-MM-DD → 本地 noon 解析,避免 UTC 偏移改星期 */
|
|
|
export function parseIsoDateLocal(iso: string): Date | null {
|
|
|
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso.trim());
|
|
|
if (!m) return null;
|
|
|
const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), 12, 0, 0);
|
|
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
|
}
|
|
|
|
|
|
export function toIsoDateLocal(d: Date): string {
|
|
|
const yyyy = d.getFullYear();
|
|
|
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
|
const dd = String(d.getDate()).padStart(2, "0");
|
|
|
return `${yyyy}-${mm}-${dd}`;
|
|
|
}
|
|
|
|
|
|
/** 周六=6 / 周日=0 不可选 */
|
|
|
export function isMothershipWeekendIso(iso: string): boolean {
|
|
|
const d = parseIsoDateLocal(iso);
|
|
|
if (!d) return false;
|
|
|
const day = d.getDay();
|
|
|
return day === 0 || day === 6;
|
|
|
}
|
|
|
|
|
|
/** 若落在周末,拨到下一工作日(周一) */
|
|
|
export function snapMothershipReadyDateToWeekday(iso: string): string {
|
|
|
const d = parseIsoDateLocal(iso);
|
|
|
if (!d) return iso;
|
|
|
const day = d.getDay();
|
|
|
if (day === 6) d.setDate(d.getDate() + 2);
|
|
|
else if (day === 0) d.setDate(d.getDate() + 1);
|
|
|
return toIsoDateLocal(d);
|
|
|
}
|
|
|
|
|
|
/** 今天 YYYY-MM-DD(本地) */
|
|
|
export function todayIsoDateLocal(): string {
|
|
|
return toIsoDateLocal(new Date());
|
|
|
}
|
|
|
|
|
|
/** 默认可提货日:今天若为周末则拨到周一 */
|
|
|
export function defaultMothershipReadyDateIso(): string {
|
|
|
return snapMothershipReadyDateToWeekday(todayIsoDateLocal());
|
|
|
}
|
|
|
|
|
|
/** date input min:与官网一致,不可早于最近工作日 */
|
|
|
export function minMothershipReadyDateIso(): string {
|
|
|
return defaultMothershipReadyDateIso();
|
|
|
}
|
|
|
|
|
|
/** 所有 MotherShip 日期字段统一规范化 */
|
|
|
export function normalizeMothershipReadyDateIso(
|
|
|
iso: string | undefined,
|
|
|
): string | undefined {
|
|
|
const trimmed = iso?.trim();
|
|
|
if (!trimmed) return undefined;
|
|
|
return snapMothershipReadyDateToWeekday(trimmed);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 单件平均重量(lb)。
|
|
|
* 登录态表单「单件重量 / Weight each」即均重;quantity 仅表示件数,不参与均重计算。
|
|
|
*/
|
|
|
export function resolveMothershipAverageWeightEachLb(
|
|
|
weightEachLb: number,
|
|
|
_quantity?: number,
|
|
|
): number {
|
|
|
return weightEachLb;
|
|
|
}
|
|
|
|
|
|
/** 单件平均重量 ≤ 5000 lb 才允许询价 */
|
|
|
export function isMothershipAverageWeightEachAllowed(
|
|
|
weightEachLb: number,
|
|
|
): boolean {
|
|
|
return (
|
|
|
Number.isFinite(weightEachLb) &&
|
|
|
weightEachLb > 0 &&
|
|
|
weightEachLb <= MOTHERSHIP_MAX_AVG_WEIGHT_EACH_LB
|
|
|
);
|
|
|
}
|
|
|
|
|
|
/** @deprecated 使用 isMothershipAverageWeightEachAllowed */
|
|
|
export function isMothershipWeightEachAllowed(weightEachLb: number): boolean {
|
|
|
return isMothershipAverageWeightEachAllowed(weightEachLb);
|
|
|
}
|
|
|
|
|
|
export type MothershipCargoWeightLine = {
|
|
|
weightLb: number;
|
|
|
quantity?: number;
|
|
|
};
|
|
|
|
|
|
/** 整票总重 = Σ(单件重量 × 数量) */
|
|
|
export function sumMothershipTotalWeightLb(
|
|
|
lines: MothershipCargoWeightLine[],
|
|
|
): number {
|
|
|
return lines.reduce((acc, line) => {
|
|
|
const qty = Math.max(1, Math.round(line.quantity ?? 1));
|
|
|
const wt = line.weightLb;
|
|
|
if (!Number.isFinite(wt) || wt <= 0) return acc;
|
|
|
return acc + wt * qty;
|
|
|
}, 0);
|
|
|
}
|
|
|
|
|
|
export function isMothershipTotalWeightAllowed(totalLb: number): boolean {
|
|
|
return (
|
|
|
Number.isFinite(totalLb) &&
|
|
|
totalLb > 0 &&
|
|
|
totalLb <= MOTHERSHIP_MAX_TOTAL_WEIGHT_LB
|
|
|
);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 校验多行货物:任一单件平均重量超限则返回阻断文案,否则 null。
|
|
|
*/
|
|
|
export function findMothershipAvgWeightBlockMessage(
|
|
|
lines: MothershipCargoWeightLine[],
|
|
|
): string | null {
|
|
|
for (const line of lines) {
|
|
|
const avg = resolveMothershipAverageWeightEachLb(
|
|
|
line.weightLb,
|
|
|
line.quantity,
|
|
|
);
|
|
|
if (!isMothershipAverageWeightEachAllowed(avg)) {
|
|
|
return MOTHERSHIP_AVG_WEIGHT_BLOCK_MESSAGE;
|
|
|
}
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
/** 整票总重超限则返回阻断文案,否则 null */
|
|
|
export function findMothershipTotalWeightBlockMessage(
|
|
|
lines: MothershipCargoWeightLine[],
|
|
|
): string | null {
|
|
|
const total = sumMothershipTotalWeightLb(lines);
|
|
|
if (!isMothershipTotalWeightAllowed(total)) {
|
|
|
return MOTHERSHIP_TOTAL_WEIGHT_BLOCK_MESSAGE;
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
/** 均重或总重任一超限则返回阻断文案 */
|
|
|
export function findMothershipCargoWeightBlockMessage(
|
|
|
lines: MothershipCargoWeightLine[],
|
|
|
): string | null {
|
|
|
return (
|
|
|
findMothershipAvgWeightBlockMessage(lines) ??
|
|
|
findMothershipTotalWeightBlockMessage(lines)
|
|
|
);
|
|
|
}
|