|
|
/**
|
|
|
* 登录后一级 payload → 现有 QuoteRequestBody(复用匿名询价 API)
|
|
|
*/
|
|
|
import { applyMothershipCandidate } from "@/lib/frontend/mothership-address";
|
|
|
import { uuidV4 } from "@/lib/frontend/format";
|
|
|
import type {
|
|
|
AddressInput,
|
|
|
MothershipAddressCandidate,
|
|
|
QuoteRequestBody,
|
|
|
} from "@/lib/frontend/types";
|
|
|
import type { MothershipLoggedInShipmentPayload } from "@/components/mothership/mothership-logged-in-shipment-form";
|
|
|
import type { MothershipLoggedInDetailsPayload } from "@/components/mothership/mothership-logged-in-details-form";
|
|
|
import { normalizeMothershipReadyDateIso } from "@/lib/mothership/logged-in-constraints";
|
|
|
|
|
|
function candidateToAddress(
|
|
|
candidate: MothershipAddressCandidate,
|
|
|
): AddressInput {
|
|
|
return applyMothershipCandidate(
|
|
|
{
|
|
|
street: candidate.street,
|
|
|
city: candidate.city,
|
|
|
state: candidate.state,
|
|
|
zip: candidate.zip || "",
|
|
|
place_id: candidate.option_id,
|
|
|
formatted_address:
|
|
|
candidate.formatted_address || candidate.display_label,
|
|
|
selected_from_suggestions: true,
|
|
|
},
|
|
|
candidate,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
/** 托盘数:各行 quantity 合计(登录后官网可多行) */
|
|
|
export function resolveLoggedInPalletCount(
|
|
|
payload: MothershipLoggedInShipmentPayload,
|
|
|
): number {
|
|
|
const sum = payload.cargo.reduce((acc, row) => acc + row.quantity, 0);
|
|
|
return Math.max(1, Math.round(sum));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 用一级货件 + 已确认联想地址组询价请求。
|
|
|
* 托盘数取 quantity 合计;重量/尺寸保留首行供匿名路径兼容;
|
|
|
* cargo_lines 供登录态 RPA 按行填官网(cargo-add-button)。
|
|
|
*/
|
|
|
export function buildQuoteRequestBodyFromLoggedIn(
|
|
|
payload: MothershipLoggedInShipmentPayload,
|
|
|
customerId: string,
|
|
|
details?: MothershipLoggedInDetailsPayload,
|
|
|
): QuoteRequestBody {
|
|
|
const primary = payload.cargo[0];
|
|
|
if (!primary) {
|
|
|
throw new Error("货物明细不能为空");
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
request_id: uuidV4(),
|
|
|
customer_id: customerId,
|
|
|
pickup_address: candidateToAddress(payload.pickupConfirmed),
|
|
|
delivery_address: candidateToAddress(payload.deliveryConfirmed),
|
|
|
weight: { value: primary.weightLb, unit: "lb" },
|
|
|
dimensions: {
|
|
|
length: primary.lengthIn,
|
|
|
width: primary.widthIn,
|
|
|
height: primary.heightIn,
|
|
|
unit: "in",
|
|
|
},
|
|
|
pallet_count: resolveLoggedInPalletCount(payload),
|
|
|
cargo_type: "general_freight",
|
|
|
service_level: "standard",
|
|
|
rate_option: "lowest",
|
|
|
display_unit: "imperial",
|
|
|
pickup_accessorials: [...payload.pickupAccessorials],
|
|
|
delivery_accessorials: [...payload.deliveryAccessorials],
|
|
|
ready_date: normalizeMothershipReadyDateIso(payload.readyDate),
|
|
|
ready_time: payload.readyTime || undefined,
|
|
|
cargo_lines: payload.cargo.map((row) => ({
|
|
|
cargo_type: row.cargoType,
|
|
|
quantity: row.quantity,
|
|
|
weight_lb: row.weightLb,
|
|
|
length_in: row.lengthIn,
|
|
|
width_in: row.widthIn,
|
|
|
height_in: row.heightIn,
|
|
|
})),
|
|
|
mothership_details: details
|
|
|
? {
|
|
|
pickup: {
|
|
|
company_name: details.pickup.companyName || undefined,
|
|
|
suite: details.pickup.suite || undefined,
|
|
|
contact_first: details.pickup.contactFirst || undefined,
|
|
|
contact_last: details.pickup.contactLast || undefined,
|
|
|
contact_email: details.pickup.contactEmail || undefined,
|
|
|
contact_phone: details.pickup.contactPhone || undefined,
|
|
|
reference: details.pickup.reference || undefined,
|
|
|
notes: details.pickup.notes || undefined,
|
|
|
opens_at: details.pickup.opensAt || undefined,
|
|
|
closes_at: details.pickup.closesAt || undefined,
|
|
|
},
|
|
|
delivery: {
|
|
|
company_name: details.delivery.companyName || undefined,
|
|
|
suite: details.delivery.suite || undefined,
|
|
|
contact_first: details.delivery.contactFirst || undefined,
|
|
|
contact_last: details.delivery.contactLast || undefined,
|
|
|
contact_email: details.delivery.contactEmail || undefined,
|
|
|
contact_phone: details.delivery.contactPhone || undefined,
|
|
|
reference: details.delivery.reference || undefined,
|
|
|
notes: details.delivery.notes || undefined,
|
|
|
opens_at: details.delivery.opensAt || undefined,
|
|
|
closes_at: details.delivery.closesAt || undefined,
|
|
|
},
|
|
|
request_delivery_appointment: details.requestDeliveryAppointment,
|
|
|
fba_number: details.fbaNumber || undefined,
|
|
|
fba_po_number: details.fbaPoNumber || undefined,
|
|
|
cargo: details.cargo.map((line) => ({
|
|
|
piece_count_type: line.pieceCountType || undefined,
|
|
|
piece_count_qty: Number.isFinite(line.pieceCountQty)
|
|
|
? line.pieceCountQty
|
|
|
: undefined,
|
|
|
description: line.description || undefined,
|
|
|
nmfc: line.nmfc || undefined,
|
|
|
hazmat: line.hazmat,
|
|
|
alcohol: line.alcohol,
|
|
|
tobacco: line.tobacco,
|
|
|
})),
|
|
|
}
|
|
|
: undefined,
|
|
|
};
|
|
|
}
|