diff --git a/scripts/admin-test-env-acceptance.mjs b/scripts/admin-test-env-acceptance.mjs index df4fc7c5e..60dd43ca8 100644 --- a/scripts/admin-test-env-acceptance.mjs +++ b/scripts/admin-test-env-acceptance.mjs @@ -335,7 +335,8 @@ export function parseArguments(argv) { channelCode: values.get('channel-code') || '', orderProfileMode: values.get('order-profile-mode') || 'fixed', waitSplitCount: Number(values.get('wait-split-count') || process.env.ADMIN_WAIT_SPLIT_COUNT - || (modules.includes('manual-order-splitting-fixture') ? 6 : 1)), + || (modules.includes('coload-groups') ? 8 + : modules.includes('manual-order-splitting-fixture') ? 6 : 1)), waitSplitPackageCount: Number(values.get('wait-split-package-count') || 0), waitSplitSequence: Number(values.get('wait-split-sequence') || 1), waitSplitRandomValue: Number(values.get('wait-split-random-value') || values.get('wait-split-sequence') || 1), @@ -826,7 +827,7 @@ export class AcceptanceRun { this.context.phase3LoadingIncrementalPackageCount = variant.packageCountPerSku const profile = this.options.orderProfileMode === 'randomized' ? this.randomizedOrderProfile(sequence) - : { destinationType: 'FBA', fulfillmentType: 'STANDARD', pickupMethod: 'PICKUP', + : { destinationType: 'FBA', fulfillmentType: 'STANDARD', pickupMethod: 'SELF_EXPRESS', taxMode: 'BUY_DECLARATION', containerMode: 'LCL', customsInspection: false } this.context.orderProfile = profile this.context.orderDestinationType = profile.destinationType @@ -1058,15 +1059,30 @@ export class AcceptanceRun { && /[A-Za-z]/.test(createData.temporaryPassword) && /\d/.test(createData.temporaryPassword), `generated=${typeof createData.temporaryPassword === 'string'}, length=${createData.temporaryPassword?.length || 0}`, ownerProvisioningOptions) - this.assert(module, '管理员用户名默认使用联系电话', createResponse, - createData.administratorUsername === phone, - `matches=${createData.administratorUsername === phone}`, + const administratorUsername = String(createData.administratorUsername || '') + this.assert(module, '管理员用户名作为独立登录标识返回', createResponse, + administratorUsername.length > 0, + `present=${administratorUsername.length > 0}`, ownerProvisioningOptions) this.assert(module, '待完善客户的审核字段保持为空', createResponse, customer.unifiedSocialCreditCode == null && customer.approvedAt == null, `creditCode=${customer.unifiedSocialCreditCode ?? 'omitted'}, approvedAt=${customer.approvedAt ?? 'omitted'}`) - await this.runCustomerDimensionRules(tenantId) + const dimensionInitializationPath = `/api/platform/v1/iam/tenants/${tenantId}/dimension-initialization` + let dimensionInitializationResponse = null + let dimensionInitialization = null + for (let attempt = 0; attempt < 30; attempt += 1) { + dimensionInitializationResponse = await this.request('GET', dimensionInitializationPath) + dimensionInitialization = dimensionInitializationResponse.http === 200 + && String(dimensionInitializationResponse.body?.code) === '200' + ? dimensionInitializationResponse.body?.data : null + if (dimensionInitialization?.status === 'READY') break + if (attempt < 29) await new Promise((resolve) => setTimeout(resolve, 1_000)) + } + this.assert(module, '客户默认尺寸异步初始化完成', dimensionInitializationResponse, + dimensionInitialization?.status === 'READY', + `status=${dimensionInitialization?.status || 'missing'}`, ownerProvisioningOptions) + if (dimensionInitialization?.status === 'READY') await this.runCustomerDimensionRules(tenantId) const listResponse = await this.request('GET', `/api/platform/v1/iam/tenants?page=1&size=20&keyword=${encodeURIComponent(payload.tenantName)}`) const page = this.apiSuccess(module, '查询客户列表', listResponse); const listed = page?.records?.find((item) => String(item.id) === tenantId) @@ -1136,9 +1152,9 @@ export class AcceptanceRun { } const memberResponse = await this.request('GET', `${detailPath}/members`); const members = this.apiSuccess(module, '业务概况查询成员', memberResponse) const owner = members?.find?.((item) => item.memberType === 'OWNER') - this.assert(module, '联系电话账号是企业管理员', memberResponse, - owner?.username === phone, - `ownerFound=${Boolean(owner)}, matches=${owner?.username === phone}`, + this.assert(module, '独立用户名账号是企业管理员且联系电话不承担用户名约束', memberResponse, + owner?.username === administratorUsername, + `ownerFound=${Boolean(owner)}, matches=${owner?.username === administratorUsername}`, ownerProvisioningOptions) const countResponse = await this.request('GET', `/api/platform/v1/orders/first-leg/tenant-counts?tenantIds=${tenantId}`); const counts = this.apiSuccess(module, '业务概况查询运单数量', countResponse); const count = counts?.find?.((item) => String(item.tenantId) === tenantId) this.assert(module, '业务概况返回当前客户运单数', countResponse, count && Number.isFinite(Number(count.orderCount)), `orderCount=${count?.orderCount ?? 'missing'}`) diff --git a/scripts/admin-test-env-modules/coload-groups-contract.test.mjs b/scripts/admin-test-env-modules/coload-groups-contract.test.mjs index 14d8fa4e7..7b7f33686 100644 --- a/scripts/admin-test-env-modules/coload-groups-contract.test.mjs +++ b/scripts/admin-test-env-modules/coload-groups-contract.test.mjs @@ -9,7 +9,7 @@ test('同柜不可拆组HTTP验收锁定公开接口与防绕过矩阵', async ( '/api/platform/v1/first-leg/coload-groups/by-inbound-code/', '/group-assignments', '/inbound-assignments', - '/push-preload', + '/plans/by-booking/', '创建同键同载荷精确重放', '创建同键异载荷返回并发冲突', '不同目的仓港仍可创建同柜不可拆组', @@ -31,7 +31,7 @@ test('同柜不可拆组HTTP验收锁定公开接口与防绕过矩阵', async ( '短拣完成拒绝后任务、父子树和组锁零漂移', '同柜组足量完成命令可靠受理', '足量完成整组已消费、全部入库完成且不生成余量子单', - '未成组普通单推送预装不回归', + '未成组普通单自动预装不回归', '未成组普通单仍按既有规则生成唯一余量子单', '拆分余量子单仍可与同仓同渠道订单创建同柜组', '拆分身份不影响当前WAIT_SPLIT片段完整纳组', @@ -42,4 +42,5 @@ test('同柜不可拆组HTTP验收锁定公开接口与防绕过矩阵', async ( assert.doesNotMatch(source, /跨仓库渠道或目的仓港创建整组被拒绝/) assert.doesNotMatch(source, /已有父子拆分订单创建同柜组被拒绝/) + assert.doesNotMatch(source, /push-preload/) }) diff --git a/scripts/admin-test-env-modules/coload-groups.mjs b/scripts/admin-test-env-modules/coload-groups.mjs index 5808234ef..fec970b37 100644 --- a/scripts/admin-test-env-modules/coload-groups.mjs +++ b/scripts/admin-test-env-modules/coload-groups.mjs @@ -99,10 +99,8 @@ async function createAssignedPushedPlan(context, fixture, groupId, suffix) { evidence(simulationResponse), OPTIONS) const bookingId = id(simulation?.id) if (!bookingId) return null - const planResponse = await context.request('POST', `${SMART}/plans`, { - bookingId, remark: `${context.marker}-${suffix}`, - }) - let plan = context.apiSuccess(MODULE, `${suffix} 创建组装柜计划`, planResponse, + const planResponse = await context.request('GET', `${SMART}/plans/by-booking/${encodeURIComponent(bookingId)}`) + let plan = context.apiSuccess(MODULE, `${suffix} 读取模拟柜自动创建的组装柜计划`, planResponse, evidence(planResponse), OPTIONS) const planId = id(plan?.id) if (!planId) return null @@ -112,10 +110,8 @@ async function createAssignedPushedPlan(context, fixture, groupId, suffix) { }) plan = context.apiSuccess(MODULE, `${suffix} 整组原子分配`, assignResponse, evidence(assignResponse, `groupId=${groupId}`), OPTIONS) - const pushResponse = await context.request('POST', `${SMART}/plans/${encodeURIComponent(planId)}/push-preload`, { - expectedVersion: Number(plan?.version), - }, undefined, { 'Idempotency-Key': `${context.marker}-${suffix}-push`.slice(0, 128) }) - const pushed = context.apiSuccess(MODULE, `${suffix} 整组完整推送预装`, pushResponse, + const pushResponse = await context.request('GET', `${SMART}/plans/${encodeURIComponent(planId)}`) + const pushed = context.apiSuccess(MODULE, `${suffix} 整组分配后自动建立预装关联`, pushResponse, evidence(pushResponse), OPTIONS) return pushed ? { containerBookingId: bookingId, planId, plan: pushed, simulation } : null } @@ -129,10 +125,8 @@ async function createNormalPushedPlan(context, fixture, suffix) { evidence(simulationResponse), OPTIONS) const containerBookingId = id(simulation?.id) if (!containerBookingId) return null - const planResponse = await context.request('POST', `${SMART}/plans`, { - bookingId: containerBookingId, remark: `${context.marker}-${suffix}`, - }) - let plan = context.apiSuccess(MODULE, `${suffix} 创建普通单装柜计划`, planResponse, + const planResponse = await context.request('GET', `${SMART}/plans/by-booking/${encodeURIComponent(containerBookingId)}`) + let plan = context.apiSuccess(MODULE, `${suffix} 读取模拟柜自动创建的普通单装柜计划`, planResponse, evidence(planResponse), OPTIONS) const planId = id(plan?.id) if (!planId) return null @@ -149,10 +143,8 @@ async function createNormalPushedPlan(context, fixture, suffix) { context.assert(MODULE, `${suffix} 普通单维持既有部分分配语义`, assignResponse, assignedTotal === assignedCount && assignedCount < Number(fixture.packages), `assigned=${assignedTotal}, source=${fixture.packages}`, OPTIONS) - const pushResponse = await context.request('POST', `${SMART}/plans/${encodeURIComponent(planId)}/push-preload`, { - expectedVersion: Number(plan?.version), - }, undefined, { 'Idempotency-Key': `${context.marker}-${suffix}-push`.slice(0, 128) }) - const pushed = context.apiSuccess(MODULE, `${suffix} 未成组普通单推送预装不回归`, pushResponse, + const pushResponse = await context.request('GET', `${SMART}/plans/${encodeURIComponent(planId)}`) + const pushed = context.apiSuccess(MODULE, `${suffix} 未成组普通单自动预装不回归`, pushResponse, evidence(pushResponse), OPTIONS) return pushed ? { containerBookingId, planId, plan: pushed, simulation, assignedCount } : null } @@ -344,7 +336,7 @@ async function createRouteVariantFixture(context, base, variant) { context.marker = `${originalMarker}-${variant === 'DESTINATION' ? 'DEST' : 'ROUTE'}-${attempt}` context.context.orderProfile = { destinationType: variant === 'DESTINATION' && attempt >= 3 ? 'OVERSEAS' : 'FBA', fulfillmentType: 'STANDARD', - pickupMethod: 'PICKUP', taxMode: 'BUY_DECLARATION', containerMode: 'LCL', customsInspection: false, + pickupMethod: 'SELF_EXPRESS', taxMode: 'BUY_DECLARATION', containerMode: 'LCL', customsInspection: false, } context.context.orderDestinationType = context.context.orderProfile.destinationType context.context.orderFulfillmentType = 'STANDARD' @@ -510,10 +502,8 @@ export async function run(context) { evidence(simulationResponse), OPTIONS) let bookingId = id(simulation?.id) if (!bookingId) return - const planResponse = await context.request('POST', '/api/platform/v1/first-leg/smart-loading/plans', { - bookingId, remark: `${context.marker} 同柜不可拆组验收`, - }) - let plan = context.apiSuccess(MODULE, '创建容量拒绝专用空计划', planResponse, + const planResponse = await context.request('GET', `${SMART}/plans/by-booking/${encodeURIComponent(bookingId)}`) + let plan = context.apiSuccess(MODULE, '读取容量拒绝专用自动空计划', planResponse, evidence(planResponse, `bookingId=${bookingId}`), OPTIONS) let planId = id(plan?.id) if (!planId) return @@ -580,10 +570,8 @@ export async function run(context) { recommendSimulationResponse, evidence(recommendSimulationResponse), OPTIONS) const recommendBookingId = id(recommendSimulation?.id) if (!recommendBookingId) return - const recommendPlanResponse = await context.request('POST', `${SMART}/plans`, { - bookingId: recommendBookingId, remark: `${context.marker} 智能推荐整组全取`, - }) - let recommendPlan = context.apiSuccess(MODULE, '创建智能推荐整组成功专用计划', recommendPlanResponse, + const recommendPlanResponse = await context.request('GET', `${SMART}/plans/by-booking/${encodeURIComponent(recommendBookingId)}`) + let recommendPlan = context.apiSuccess(MODULE, '读取智能推荐整组成功专用自动计划', recommendPlanResponse, evidence(recommendPlanResponse), OPTIONS) const recommendPlanId = id(recommendPlan?.id) if (!recommendPlanId) return @@ -628,10 +616,8 @@ export async function run(context) { evidence(workingSimulationResponse), OPTIONS) bookingId = id(workingSimulation?.id) if (!bookingId) return - const workingPlanResponse = await context.request('POST', `${SMART}/plans`, { - bookingId, remark: `${context.marker} 同柜不可拆组PDA主链`, - }) - plan = context.apiSuccess(MODULE, '创建PDA主链同柜装柜计划', workingPlanResponse, + const workingPlanResponse = await context.request('GET', `${SMART}/plans/by-booking/${encodeURIComponent(bookingId)}`) + plan = context.apiSuccess(MODULE, '读取PDA主链自动创建的同柜装柜计划', workingPlanResponse, evidence(workingPlanResponse, `bookingId=${bookingId}`), OPTIONS) planId = id(plan?.id) if (!planId) return @@ -718,12 +704,8 @@ export async function run(context) { evidence(manualGroupResponse), OPTIONS) if (!plan) return - const pushKey = `${context.marker}-coload-push`.slice(0, 128) - const pushResponse = await context.request('POST', - `/api/platform/v1/first-leg/smart-loading/plans/${encodeURIComponent(planId)}/push-preload`, { - expectedVersion: Number(plan.version), - }, undefined, { 'Idempotency-Key': pushKey }) - const pushed = context.apiSuccess(MODULE, '整组完整时推送预装成功', pushResponse, + const pushResponse = await context.request('GET', `${SMART}/plans/${encodeURIComponent(planId)}`) + const pushed = context.apiSuccess(MODULE, '整组完整时自动预装关联可读', pushResponse, evidence(pushResponse), OPTIONS) const pushedTotals = new Map() for (const item of pushed?.items || []) pushedTotals.set(id(item?.inboundId), @@ -956,10 +938,9 @@ export async function run(context) { const uiSimulation = context.apiSuccess(MODULE, '为Admin页面保留可编辑智能装柜柜次', uiSimulationResponse, evidence(uiSimulationResponse), OPTIONS) const uiBookingId = id(uiSimulation?.id) - const uiPlanResponse = uiBookingId ? await context.request('POST', `${SMART}/plans`, { - bookingId: uiBookingId, remark: `${context.marker} Admin整组候选人工验收保留`, - }) : null - const uiPlan = uiPlanResponse ? context.apiSuccess(MODULE, '为Admin页面保留未分配空计划', uiPlanResponse, + const uiPlanResponse = uiBookingId + ? await context.request('GET', `${SMART}/plans/by-booking/${encodeURIComponent(uiBookingId)}`) : null + const uiPlan = uiPlanResponse ? context.apiSuccess(MODULE, '为Admin页面保留自动创建的未分配空计划', uiPlanResponse, evidence(uiPlanResponse, `bookingId=${uiBookingId}`), OPTIONS) : null const uiMembers = memberFacts(uiGroup) context.assert(MODULE, '页面验收保留组仍为ACTIVE且未锁定', uiGroupResponse, diff --git a/scripts/admin-test-env-modules/manual-state-loading-pda-contract.test.mjs b/scripts/admin-test-env-modules/manual-state-loading-pda-contract.test.mjs index 0f3a60065..68e4197a6 100644 --- a/scripts/admin-test-env-modules/manual-state-loading-pda-contract.test.mjs +++ b/scripts/admin-test-env-modules/manual-state-loading-pda-contract.test.mjs @@ -7,7 +7,7 @@ const source = readFileSync(new URL('./manual-state-loading-pda.mjs', import.met test('retains all clickable container, loading-list and PDA workflow states through public APIs', () => { for (const status of ['WAIT_LOADING', 'EDITING', 'LOADING', 'COMPLETED', 'UNASSIGNED', 'IN_PROGRESS', 'AWAITING_REVIEW']) assert.match(source, new RegExp(`['"]${status}['"]`)) - for (const endpoint of ['/ocean-bookings', '/smart-loading', '/push-preload', '/inventory-snapshot', + for (const endpoint of ['/ocean-bookings', '/smart-loading', '/plans/by-booking/', '/inventory-snapshot', '/batch', '/complete', '/loading-plans', '/loading-lists']) assert.match(source, new RegExp(endpoint)) assert.match(source, /requiredStatuses/) assert.match(source, /detailPath/) @@ -18,7 +18,7 @@ test('retains all clickable container, loading-list and PDA workflow states thro assert.match(source, /\.\/inbound-measurement\.mjs/) assert.match(source, /context\.request\('POST', CONTAINERS/) assert.match(source, /summary\?\.processStatus === 'WAIT_SPLIT'/) - const pushIndex = source.indexOf('`${SMART}/plans/${plan.id}/push-preload`') + const pushIndex = source.indexOf('`${SMART}/plans/${plan.id}`') const transportIndex = source.indexOf('ocean-booking-link') assert.ok(transportIndex >= 0 && pushIndex > transportIndex) const unconfirmedTaskIndex = source.indexOf('`${PDA}/tasks?loadingPlanId=${planId}&warehouseId=${source.warehouseId}`', pushIndex) @@ -26,6 +26,7 @@ test('retains all clickable container, loading-list and PDA workflow states thro const canonicalTaskIndex = source.indexOf('`${PDA}/tasks?loadingPlanId=${planId}&warehouseId=${source.warehouseId}`', unconfirmedTaskIndex + 1) assert.ok(unconfirmedTaskIndex > pushIndex && confirmIndex > unconfirmedTaskIndex && canonicalTaskIndex > confirmIndex) + assert.doesNotMatch(source, /push-preload/) assert.match(source, /计划确认前不生成 PDA 任务/) assert.match(source, /summary\?\.readiness\?\.canConfirm === true/) assert.match(source, /Object\.freeze\(\{ expectedVersion: confirmableVersion \}\)/) diff --git a/scripts/admin-test-env-modules/manual-state-loading-pda.mjs b/scripts/admin-test-env-modules/manual-state-loading-pda.mjs index 7f63314c8..6851682de 100644 --- a/scripts/admin-test-env-modules/manual-state-loading-pda.mjs +++ b/scripts/admin-test-env-modules/manual-state-loading-pda.mjs @@ -193,18 +193,15 @@ async function createPlanTask(context, label) { : null if (!candidate?.inboundDetailId) throw new Error(`${label} 独立入库单未进入同渠道排柜候选`) - const planResponse = await context.request('POST', `${SMART}/plans`, - { bookingId: containerId, remark: `${context.marker}-${label}` }) - let plan = context.apiSuccess(MODULE, `${label} 创建智能排柜计划`, planResponse, '', OPTIONS) + const planResponse = await context.request('GET', `${SMART}/plans/by-booking/${containerId}`) + let plan = context.apiSuccess(MODULE, `${label} 读取模拟柜自动创建的智能排柜计划`, planResponse, '', OPTIONS) const assignResponse = await context.request('POST', `${SMART}/plans/${plan.id}/assignments`, { inboundId: source.inboundId, inboundDetailId: id(candidate.inboundDetailId), packageCount: 1, expectedVersion: Number(plan.version), operationId: operationId(context, `${label}-assign`), }) plan = context.apiSuccess(MODULE, `${label} 分配独立候选货物`, assignResponse, '', OPTIONS) - const containerNo = iso6346ContainerNumber(`${context.marker}-manual-${label}`) - const pushResponse = await post(context, `${SMART}/plans/${plan.id}/push-preload`, - { expectedVersion: Number(plan.version) }, `${label}-push`) - plan = context.apiSuccess(MODULE, `${label} 补真实柜号后推送预装`, pushResponse, '', OPTIONS) + const pushResponse = await context.request('GET', `${SMART}/plans/${plan.id}`) + plan = context.apiSuccess(MODULE, `${label} 分配后自动建立预装关联`, pushResponse, '', OPTIONS) const planId = id(plan?.id) const unconfirmedTaskResponse = await context.request('GET', `${PDA}/tasks?loadingPlanId=${planId}&warehouseId=${source.warehouseId}`) diff --git a/scripts/admin-test-env-modules/phase3-order-splitting-contract.test.mjs b/scripts/admin-test-env-modules/phase3-order-splitting-contract.test.mjs index 04278288b..89430670a 100644 --- a/scripts/admin-test-env-modules/phase3-order-splitting-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-order-splitting-contract.test.mjs @@ -23,7 +23,7 @@ test('each remainder uses a simulation linked to a released booking requirement' assert.match(source, /oceanBooking\?\.status === 'RELEASED' && releasedAllocationMatches/) assert.match(source, /id\(item\?\.containerBookingId\) === containerId/) assert.match(source, /candidates\?bookingId=\$\{encodeURIComponent\(containerId\)\}&warehouseId=\$\{encodeURIComponent\(warehouseId\)\}/) - assert.match(source, /`\$\{SMART\}\/plans`, \{ bookingId: containerId/) + assert.match(source, /`\$\{SMART\}\/plans\/by-booking\/\$\{containerId\}`/) assert.doesNotMatch(source, /candidates\?warehouseId=/) }) @@ -37,7 +37,7 @@ test('release freezes and detail replays the same complete transport snapshot', }) test('PDA opens only after linking a released requirement and uses stable plan identity', () => { - const pushIndex = source.indexOf("`${SMART}/plans/${planId}/push-preload`") + const pushIndex = source.indexOf("`${SMART}/plans/${planId}`") const linkIndex = source.indexOf("`${CONTAINERS}/${containerId}/ocean-booking-link`") const releaseIndex = source.indexOf("`/api/platform/v1/ocean-bookings/${oceanBookingId}/release`") const confirmIndex = source.indexOf("`/api/platform/v1/first-leg/loading-plans/${encodeURIComponent(planId)}/confirm`") @@ -60,6 +60,7 @@ test('PDA opens only after linking a released requirement and uses stable plan i assert.match(source, /id\(task\?\.loadingPlanId\) === planId/) assert.match(source, /id\(task\?\.containerBookingId\) === containerId/) assert.doesNotMatch(source, /\$\{PDA\}\/tasks\?containerNo=/) + assert.doesNotMatch(source, /push-preload/) }) test('each remainder settles only through inventory snapshot, atomic batch and explicit complete', () => { diff --git a/scripts/admin-test-env-modules/phase3-order-splitting.mjs b/scripts/admin-test-env-modules/phase3-order-splitting.mjs index 19732cb6a..8f5a4ce96 100644 --- a/scripts/admin-test-env-modules/phase3-order-splitting.mjs +++ b/scripts/admin-test-env-modules/phase3-order-splitting.mjs @@ -230,8 +230,8 @@ async function loadOneFragment(context, state) { `orderId=${sourceOrderId}, available=${candidate?.availablePackageCount ?? 'missing'}`, OPTIONS) if (!candidate?.inboundId || !candidate?.inboundDetailId) return null - const createPlan = await request(context, 'POST', `${SMART}/plans`, { bookingId: containerId, remark: context.marker }) - const plan = context.apiSuccess(MODULE, `为 ${sourceOrderNo} 创建装柜计划`, createPlan, '', OPTIONS) + const createPlan = await request(context, 'GET', `${SMART}/plans/by-booking/${containerId}`) + const plan = context.apiSuccess(MODULE, `读取 ${sourceOrderNo} 模拟柜自动创建的装柜计划`, createPlan, '', OPTIONS) const planId = id(plan?.id) if (!planId) return null context.createdResources.phase3OrderSplitting.retainedAuditRecordIds.push(planId) @@ -289,11 +289,11 @@ async function loadOneFragment(context, state) { `status=${bookingDetail?.oceanBooking?.status || 'missing'}, containerBookingId=${containerId}`, OPTIONS) if (bookingDetail?.oceanBooking?.status !== 'RELEASED' || !releasedAllocationMatches) return null - const push = await request(context, 'POST', `${SMART}/plans/${planId}/push-preload`, - { expectedVersion: Number(assigned.version) }, { 'Idempotency-Key': `${operationBase}-push` }) - const pushed = context.apiSuccess(MODULE, `推送 ${sourceOrderNo} 的预装计划`, push, '', OPTIONS) - context.assert(MODULE, `${sourceOrderNo} 补真实柜号后计划保持 EDITING`, push, - pushed?.status === 'EDITING', `status=${pushed?.status || 'missing'}`, OPTIONS) + const push = await request(context, 'GET', `${SMART}/plans/${planId}`) + const pushed = context.apiSuccess(MODULE, `读取 ${sourceOrderNo} 自动关联的预装计划`, push, '', OPTIONS) + context.assert(MODULE, `${sourceOrderNo} 补真实柜号后自动预装计划保持 EDITING`, push, + pushed?.status === 'EDITING' && Number(pushed?.version) === Number(assigned.version), + `status=${pushed?.status || 'missing'}, version=${pushed?.version ?? 'missing'}`, OPTIONS) if (pushed?.status !== 'EDITING') return null const unconfirmedTaskResponse = await context.request('GET',