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.
115 lines
3.7 KiB
115 lines
3.7 KiB
/**
|
|
* MotherShip fill-only 结账:选承运商 + 保障 → 入队 RPA
|
|
*/
|
|
import { prisma } from "@/lib/prisma";
|
|
import {
|
|
validateMsCheckoutSelection,
|
|
type MsCoverageKind,
|
|
} from "@/lib/mothership/ms-checkout-selection";
|
|
import { saveMsCheckoutStatus, readMsCheckoutStatus } from "@/lib/mothership/ms-checkout-store";
|
|
import { enqueueMsCheckoutFillJob } from "@/modules/mothership/checkout-queue";
|
|
import { validateQuoteInput } from "@/modules/quote/validation";
|
|
import type { QuoteRequestBody } from "@/lib/frontend/types";
|
|
|
|
export async function submitMsCheckoutFill(input: {
|
|
customerId: string;
|
|
quoteId: string;
|
|
preferredCarrier: string;
|
|
coverage: MsCoverageKind;
|
|
cargoValueUsd?: number;
|
|
mothershipDetails?: QuoteRequestBody["mothership_details"];
|
|
pickupAccessorials?: string[];
|
|
deliveryAccessorials?: string[];
|
|
}): Promise<{ quote_id: string; status: "processing" }> {
|
|
const err = validateMsCheckoutSelection({
|
|
preferredCarrier: input.preferredCarrier,
|
|
coverage: input.coverage,
|
|
cargoValueUsd: input.cargoValueUsd,
|
|
});
|
|
if (err) throw new Error(err);
|
|
|
|
const record = await prisma.quoteRecord.findUnique({
|
|
where: { quoteId: input.quoteId },
|
|
});
|
|
if (!record || record.customerId !== input.customerId) {
|
|
throw new Error("报价单不存在");
|
|
}
|
|
if (record.status !== "done") {
|
|
throw new Error("请等待初步报价完成后再结账");
|
|
}
|
|
|
|
const existing = await readMsCheckoutStatus(input.quoteId);
|
|
if (existing?.status === "processing") {
|
|
throw new Error("结账同步进行中,请稍候");
|
|
}
|
|
|
|
const pickup =
|
|
record.pickupJson as unknown as QuoteRequestBody["pickup_address"];
|
|
const delivery =
|
|
record.deliveryJson as unknown as QuoteRequestBody["delivery_address"];
|
|
|
|
const body = {
|
|
request_id: record.requestId,
|
|
customer_id: input.customerId,
|
|
pickup_address: pickup,
|
|
delivery_address: delivery,
|
|
weight: { value: Number(record.weightLb), unit: "lb" as const },
|
|
dimensions: {
|
|
length: Number(record.dimLIn),
|
|
width: Number(record.dimWIn),
|
|
height: Number(record.dimHIn),
|
|
unit: "in" as const,
|
|
},
|
|
pallet_count: record.palletCount,
|
|
cargo_type: record.cargoType as QuoteRequestBody["cargo_type"],
|
|
mothership_details: input.mothershipDetails,
|
|
pickup_accessorials: input.pickupAccessorials,
|
|
delivery_accessorials: input.deliveryAccessorials,
|
|
preferred_carrier: input.preferredCarrier,
|
|
coverage: input.coverage,
|
|
cargo_value_usd: input.cargoValueUsd,
|
|
};
|
|
|
|
const cargo = validateQuoteInput(body);
|
|
cargo.preferredCarrier = input.preferredCarrier;
|
|
cargo.coverage = input.coverage;
|
|
cargo.cargoValueUsd = input.cargoValueUsd;
|
|
|
|
await saveMsCheckoutStatus({
|
|
quote_id: input.quoteId,
|
|
status: "processing",
|
|
coverage: input.coverage,
|
|
cargo_value_usd: input.cargoValueUsd ?? null,
|
|
message: "正在官网选择承运商与保障并填齐详情…",
|
|
updated_at_ms: Date.now(),
|
|
});
|
|
|
|
try {
|
|
await enqueueMsCheckoutFillJob({
|
|
quoteId: input.quoteId,
|
|
requestId: record.requestId,
|
|
customerId: input.customerId,
|
|
cargo,
|
|
preferredCarrier: input.preferredCarrier,
|
|
coverage: input.coverage,
|
|
cargoValueUsd: input.cargoValueUsd,
|
|
});
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : String(err);
|
|
if (/already exists|JobId/i.test(msg)) {
|
|
throw new Error("结账同步进行中,请稍候");
|
|
}
|
|
await saveMsCheckoutStatus({
|
|
quote_id: input.quoteId,
|
|
status: "failed",
|
|
coverage: input.coverage,
|
|
cargo_value_usd: input.cargoValueUsd ?? null,
|
|
message: "官网同步失败,请稍后重试",
|
|
updated_at_ms: Date.now(),
|
|
});
|
|
throw err;
|
|
}
|
|
|
|
return { quote_id: input.quoteId, status: "processing" };
|
|
}
|