fix: clarify automatic pallet generation
This commit is contained in:
@@ -73,7 +73,7 @@
|
||||
|
||||
| 关键词 | 页面/路由入口 | API 与联查 |
|
||||
|---|---|---|
|
||||
| 尾程订单控制与柜级入仓计划转换 | `modules/overseas-warehouse/pages/LastMileOrdersView.vue`、`LastMileOrderDetailView.vue` | 柜子工作台按 `sourceContainerBookingId` 消费 `/orders/container-summaries`;服务端分页返回全部稳定来源柜,计划已经齐全的柜仍保留用于接柜仓查询、修改和补齐真实托盘,并按冻结 `plannedArrivalAt` 升序、空值最后排序。详情返回全柜订单且不受列表分页截断;是否展示生成动作只消费 `pendingPlanCount/resourcePendingPlanCount`,不得在浏览器拼柜或自行推导就绪。确认弹框先调用 `/inbound-plans/container-preview`,按服务端 FBA/一件代发/私人地址分组展示托数、`readiness/blockers`,再把同一 `snapshotVersion/snapshotHash` 原样提交 `/inbound-plans/convert-container`;前端不计算托数、不逐票建计划、不预占托盘,快照冲突后重新获取预览。服务端只为剩余订单逐票生成独立计划,并为已有人工计划补齐真实托盘,整批原子且重放不得产生第二批资源。单票完整入库计划、暂扣/解除和八行人工节点均从 `api/lastMileOrders.ts` 提交订单版本与幂等键;入仓码只读展示并提交订单冻结值,不允许人工改写,暂扣资格、接柜仓锁定原因和操作能力只消费服务端结果,人工节点一经保存不得回退柜级节点。转仓候选只取 `modules/iam/api/platformIam.ts` 的平台公共 `ACTIVE + FBA` 仓主档;Backend `lastleg-service/` |
|
||||
| 尾程订单控制与柜级入仓计划转换 | `modules/overseas-warehouse/pages/LastMileOrdersView.vue`、`LastMileOrderDetailView.vue` | 柜子工作台按 `sourceContainerBookingId` 消费 `/orders/container-summaries`;服务端分页返回全部稳定来源柜,计划已经齐全的柜仍保留用于接柜仓查询和修改,并按冻结 `plannedArrivalAt` 升序、空值最后排序。详情返回全柜订单且不受列表分页截断;单票手工计划保存后,由服务端在整柜最后一票计划齐全时自动生成并绑定全柜托盘码,一票柜立即生成;`resourcePendingPlanCount` 只表示自动生成异常的重试入口,不是正常人工步骤。柜级确认弹框先调用 `/inbound-plans/container-preview`,按服务端 FBA/一件代发/私人地址分组展示托数、`readiness/blockers`,再把同一 `snapshotVersion/snapshotHash` 原样提交 `/inbound-plans/convert-container`;前端不计算托数、不逐票建计划、不预占托盘,快照冲突后重新获取预览。服务端只为剩余订单逐票生成独立计划,并为异常遗留的人工计划补齐真实托盘,整批原子且重放不得产生第二批资源。单票完整入库计划、暂扣/解除和八行人工节点均从 `api/lastMileOrders.ts` 提交订单版本与幂等键;入仓码只读展示并提交订单冻结值,不允许人工改写,暂扣资格、接柜仓锁定原因和操作能力只消费服务端结果,人工节点一经保存不得回退柜级节点。转仓候选只取 `modules/iam/api/platformIam.ts` 的平台公共 `ACTIVE + FBA` 仓主档;Backend `lastleg-service/` |
|
||||
| 入仓单、入仓计划、FBA 预约 | `modules/overseas-warehouse/pages/InboundOrdersView.vue`、`InboundOrderDetailView.vue` | 列表/预约 `modules/overseas-warehouse/api/inboundOrders.ts`,计划列表/详情 `inboundPlans.ts`;Backend `lastleg-service/inboundplan/` |
|
||||
| 排车查询、智能预排与转派送 | `modules/overseas-warehouse/pages/DispatchesView.vue`、`DispatchDetailView.vue`、`components/SmartDispatchDialog.vue` | `modules/overseas-warehouse/api/dispatches.ts`;Backend `lastleg-service/dispatch/`、`delivery/`;覆盖详情、候选、草稿柜、托盘锁、`pre-arrange` 和 `transfer-delivery`。页面能力必须由正式路由、API 调用和测试共同证明 |
|
||||
| 海外出库与派送 | `pages/OutboundsView.vue`、`DeliveryOrdersView.vue`、`DeliveryOrderDetailView.vue` | `api/outbounds.ts`、`deliveryOrders.ts`;一件代发出库、取消、派送列表/详情及退回入口对应 Backend `lastleg-service/delivery/`、`dropship/` |
|
||||
|
||||
@@ -387,7 +387,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
||||
expect(listContainerPlanProgress).toHaveBeenLastCalledWith({ page: 1, size: 10, containerNo: undefined, createdFrom: undefined, createdTo: undefined })
|
||||
expect(wrapper.text()).not.toContain('柜子工作台(2)')
|
||||
expect(wrapper.text()).toContain('货柜工作台')
|
||||
expect(wrapper.text()).toContain('按柜查看和修改海外接柜仓,并为剩余入仓码生成独立入库计划')
|
||||
expect(wrapper.text()).toContain('按柜查看和修改海外接柜仓;整柜计划齐全后自动生成托盘码')
|
||||
expect(wrapper.text()).toContain('5个入仓码待生成计划')
|
||||
expect(wrapper.text()).toContain('共 15 个入仓码')
|
||||
expect(wrapper.text()).not.toContain('待补入仓码')
|
||||
@@ -398,7 +398,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
||||
expect(wrapper.text().indexOf('CONT-15')).toBeLessThan(wrapper.text().indexOf('CONT-NULL'))
|
||||
})
|
||||
|
||||
it('keeps an all-plans-ready cabinet available when its real pallets are still pending', async () => {
|
||||
it('presents a resource-pending cabinet as an exceptional pallet-code retry', async () => {
|
||||
const ready = {
|
||||
...summary,
|
||||
planCount: summary.inboundCodeCount,
|
||||
@@ -413,8 +413,9 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
||||
await openHiddenContainerWorkbench(wrapper)
|
||||
expect(wrapper.text()).toContain('CONT-15')
|
||||
expect(wrapper.text()).toContain('0个入仓码待生成计划')
|
||||
expect(wrapper.text()).toContain('1 票待补真实托盘')
|
||||
expect((button(wrapper, '生成入库计划')!.element as HTMLButtonElement).disabled).toBe(false)
|
||||
expect(wrapper.text()).toContain('托盘码自动生成异常,可重试')
|
||||
expect(wrapper.text()).not.toContain('待补真实托盘')
|
||||
expect((button(wrapper, '重试生成托盘码')!.element as HTMLButtonElement).disabled).toBe(false)
|
||||
expect((button(wrapper, '修改接柜仓')!.element as HTMLButtonElement).disabled).toBe(false)
|
||||
})
|
||||
|
||||
|
||||
@@ -502,7 +502,7 @@ async function submitInboundPlan() {
|
||||
}
|
||||
actionLoading.value = true; actionError.value = null
|
||||
try {
|
||||
await createManualInboundPlan({
|
||||
const result = await createManualInboundPlan({
|
||||
customerId: row.customerId, orderId: row.id, expectedOrderVersion: row.version,
|
||||
plannedArrivalDate: inboundPlanForm.plannedArrivalDate,
|
||||
palletCount: inboundPlanForm.palletCount,
|
||||
@@ -510,7 +510,13 @@ async function submitInboundPlan() {
|
||||
mergeRequired: inboundPlanForm.mergeRequired, urgent: inboundPlanForm.urgent,
|
||||
remark: inboundPlanForm.remark.trim() || undefined,
|
||||
}, idempotencyKey('manual-inbound-plan'))
|
||||
inboundPlanOpen.value = false; selectedRows.value = []; ElMessage.success('入库计划已生成'); await load()
|
||||
const palletCodesReady = result.pallets?.length === result.palletCount
|
||||
&& result.pallets.every((pallet) => Boolean(pallet.warehousePalletId && pallet.palletNo))
|
||||
inboundPlanOpen.value = false; selectedRows.value = []
|
||||
ElMessage.success(palletCodesReady
|
||||
? '入库计划和托盘码已自动生成'
|
||||
: '入库计划已保存,整柜计划齐全后将自动生成托盘码')
|
||||
await load()
|
||||
} catch (error) { actionError.value = getApiErrorDetails(error, '生成入库计划失败,请重试') }
|
||||
finally { actionLoading.value = false }
|
||||
}
|
||||
@@ -754,7 +760,7 @@ onMounted(() => { void load() })
|
||||
</LoadErrorState>
|
||||
<section v-if="!loadError && statusTab === '柜子'" class="surface-panel container-workbench" :aria-busy="loading">
|
||||
<header class="container-workbench__header">
|
||||
<div><strong>货柜工作台</strong><span>按柜查看和修改海外接柜仓,并为剩余入仓码生成独立入库计划</span></div>
|
||||
<div><strong>货柜工作台</strong><span>按柜查看和修改海外接柜仓;整柜计划齐全后自动生成托盘码</span></div>
|
||||
<el-tag type="warning" effect="plain">{{ containerTotal }} 个货柜</el-tag>
|
||||
</header>
|
||||
<div v-if="containerRows.length" class="container-workbench__grid">
|
||||
@@ -771,14 +777,14 @@ onMounted(() => { void load() })
|
||||
</span>
|
||||
<span class="container-workbench-card__facts">
|
||||
<span>共 {{ container.inboundCodeCount }} 个入仓码</span>
|
||||
<span v-if="container.resourcePendingPlanCount">{{ container.resourcePendingPlanCount }} 票待补真实托盘</span>
|
||||
<span v-if="container.resourcePendingPlanCount">托盘码自动生成异常,可重试</span>
|
||||
</span>
|
||||
<span class="container-workbench-card__actions">
|
||||
<el-button class="container-workbench-card__detail" plain @click="openContainerDetail(container)">查看详情</el-button>
|
||||
<el-tooltip :content="receivingWarehouseChangeDisabledReason(container)" :disabled="!receivingWarehouseChangeDisabledReason(container)" placement="top">
|
||||
<span><el-button plain :title="receivingWarehouseChangeDisabledReason(container) || '修改接柜仓'" :disabled="Boolean(receivingWarehouseChangeDisabledReason(container))" @click="openReceivingWarehouseChange(container)"><Pencil :size="15" />修改接柜仓</el-button></span>
|
||||
</el-tooltip>
|
||||
<el-button v-if="access.can(permission.inboundOrderCreate)" type="primary" :disabled="actionLoading || !canGenerateContainerPlans(container)" @click="openContainerConversionFor(container)">生成入库计划</el-button>
|
||||
<el-button v-if="access.can(permission.inboundOrderCreate)" type="primary" :disabled="actionLoading || !canGenerateContainerPlans(container)" @click="openContainerConversionFor(container)">{{ container.resourcePendingPlanCount && !container.pendingPlanCount ? '重试生成托盘码' : '生成入库计划' }}</el-button>
|
||||
</span>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user