feat(overseas): manage last-mile order controls
This commit is contained in:
@@ -87,12 +87,15 @@ describe('last-mile order overseas API', () => {
|
|||||||
it('uses the manual inbound-plan endpoint with the complete form and idempotency evidence', async () => {
|
it('uses the manual inbound-plan endpoint with the complete form and idempotency evidence', async () => {
|
||||||
vi.mocked(overseasHttp.post).mockResolvedValue({ code: '200', message: 'success', data: {} })
|
vi.mocked(overseasHttp.post).mockResolvedValue({ code: '200', message: 'success', data: {} })
|
||||||
const payload = { orderId: '190000000000000099', customerId: '190000000000000001', expectedOrderVersion: 4,
|
const payload = { orderId: '190000000000000099', customerId: '190000000000000001', expectedOrderVersion: 4,
|
||||||
plannedWarehouseId: '193000000000200003', plannedWarehouseCode: 'US-LAX-01', plannedWarehouseName: '洛杉矶仓',
|
plannedArrivalDate: '2026-09-08', palletCount: 8, palletType: 'STANDARD' as const,
|
||||||
plannedArrivalDate: '2026-09-08', inboundCode: 'NYEXX2609060063', palletCount: 8, palletType: 'STANDARD' as const,
|
|
||||||
assigned: false, mergeRequired: false, urgent: true, remark: '加急' }
|
assigned: false, mergeRequired: false, urgent: true, remark: '加急' }
|
||||||
await createManualInboundPlan(payload, 'plan-key-1')
|
await createManualInboundPlan(payload, 'plan-key-1')
|
||||||
expect(overseasHttp.post).toHaveBeenCalledWith('/platform/v1/last-leg/inbound-plans/manual', payload,
|
expect(overseasHttp.post).toHaveBeenCalledWith('/platform/v1/last-leg/inbound-plans/manual', payload,
|
||||||
{ headers: { 'Idempotency-Key': 'plan-key-1' } })
|
{ headers: { 'Idempotency-Key': 'plan-key-1' } })
|
||||||
|
expect(payload).not.toHaveProperty('inboundCode')
|
||||||
|
expect(payload).not.toHaveProperty('plannedWarehouseId')
|
||||||
|
expect(payload).not.toHaveProperty('plannedWarehouseCode')
|
||||||
|
expect(payload).not.toHaveProperty('plannedWarehouseName')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('uses versioned idempotent order hold and complete manual-node snapshot endpoints', async () => {
|
it('uses versioned idempotent order hold and complete manual-node snapshot endpoints', async () => {
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ export interface LastMileOrderSummaryDto {
|
|||||||
sourceOrderNo?: string | null
|
sourceOrderNo?: string | null
|
||||||
sourceInboundId?: string | null
|
sourceInboundId?: string | null
|
||||||
customerName?: string | null
|
customerName?: string | null
|
||||||
|
overseasWarehouseId?: string | null
|
||||||
|
overseasWarehouseCode?: string | null
|
||||||
overseasWarehouseName?: string | null
|
overseasWarehouseName?: string | null
|
||||||
transferWarehouseName?: string | null
|
transferWarehouseName?: string | null
|
||||||
transferWarehouseId?: string | null
|
transferWarehouseId?: string | null
|
||||||
@@ -224,11 +226,7 @@ export interface ManualInboundPlanPayload {
|
|||||||
customerId: string
|
customerId: string
|
||||||
orderId: string
|
orderId: string
|
||||||
expectedOrderVersion: number
|
expectedOrderVersion: number
|
||||||
plannedWarehouseId: string
|
|
||||||
plannedWarehouseCode: string
|
|
||||||
plannedWarehouseName: string
|
|
||||||
plannedArrivalDate: string
|
plannedArrivalDate: string
|
||||||
inboundCode: string
|
|
||||||
palletCount: number
|
palletCount: number
|
||||||
palletType: 'STANDARD' | 'WOOD' | 'PAPER'
|
palletType: 'STANDARD' | 'WOOD' | 'PAPER'
|
||||||
assigned: boolean
|
assigned: boolean
|
||||||
|
|||||||
@@ -52,6 +52,17 @@ describe('last-mile order presentation adapter', () => {
|
|||||||
}).owner).toBe('—')
|
}).owner).toBe('—')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('preserves the frozen overseas receiving warehouse id and code as strings', () => {
|
||||||
|
const row = toLastMileOrderListItem({
|
||||||
|
id: '190000000000000099', customerId: '190000000000000001', orderNo: 'LMO-260908-0001',
|
||||||
|
overseasWarehouseId: '190000000000000123', overseasWarehouseCode: 'LAX01', overseasWarehouseName: '洛杉矶接柜仓',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(row.overseasWarehouseId).toBe('190000000000000123')
|
||||||
|
expect(row.overseasWarehouseCode).toBe('LAX01')
|
||||||
|
expect(row.warehouse).toBe('洛杉矶接柜仓')
|
||||||
|
})
|
||||||
|
|
||||||
it('preserves the exact server LMO short number without padding, prefixing or deriving it from id', () => {
|
it('preserves the exact server LMO short number without padding, prefixing or deriving it from id', () => {
|
||||||
const serverOrderNo = 'LMO-9A7'
|
const serverOrderNo = 'LMO-9A7'
|
||||||
const row = toLastMileOrderListItem({ id: '2092873463568596994', customerId: '100', orderNo: serverOrderNo })
|
const row = toLastMileOrderListItem({ id: '2092873463568596994', customerId: '100', orderNo: serverOrderNo })
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ export function toLastMileOrderListItem(dto: LastMileOrderSummaryDto): LastMileO
|
|||||||
inboundCode: text(dto.inboundCode),
|
inboundCode: text(dto.inboundCode),
|
||||||
lxCode: text(dto.lxCode),
|
lxCode: text(dto.lxCode),
|
||||||
owner: text(dto.customerName),
|
owner: text(dto.customerName),
|
||||||
|
overseasWarehouseId: String(dto.overseasWarehouseId ?? '').trim(),
|
||||||
|
overseasWarehouseCode: String(dto.overseasWarehouseCode ?? '').trim(),
|
||||||
warehouse: text(dto.overseasWarehouseName),
|
warehouse: text(dto.overseasWarehouseName),
|
||||||
transferWarehouse: text(dto.transferWarehouseName),
|
transferWarehouse: text(dto.transferWarehouseName),
|
||||||
fbaWarehouse: text(dto.fbaWarehouseCode),
|
fbaWarehouse: text(dto.fbaWarehouseCode),
|
||||||
|
|||||||
@@ -73,7 +73,12 @@ const currentReceivingWarehouse = {
|
|||||||
id: summary.overseasWarehouseId, warehouseCode: summary.overseasWarehouseCode, warehouseName: summary.overseasWarehouseName,
|
id: summary.overseasWarehouseId, warehouseCode: summary.overseasWarehouseCode, warehouseName: summary.overseasWarehouseName,
|
||||||
}
|
}
|
||||||
function selectedRow(overrides: Record<string, unknown> = {}) {
|
function selectedRow(overrides: Record<string, unknown> = {}) {
|
||||||
return { id: '2092873463568596994', customerId, sourceContainerBookingId: bookingId, orderNo: 'LMO-260827-0001', ...overrides }
|
return {
|
||||||
|
id: '2092873463568596994', customerId, sourceContainerBookingId: bookingId, orderNo: 'LMO-260827-0001',
|
||||||
|
overseasWarehouseId: summary.overseasWarehouseId, overseasWarehouseCode: summary.overseasWarehouseCode,
|
||||||
|
warehouse: summary.overseasWarehouseName, inboundCode: 'IN-LOCKED', version: 3, pallets: 2,
|
||||||
|
...overrides,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enableAutoUnmount(afterEach)
|
enableAutoUnmount(afterEach)
|
||||||
@@ -195,11 +200,44 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
|||||||
expect(document.body.textContent).toContain('计划入库仓')
|
expect(document.body.textContent).toContain('计划入库仓')
|
||||||
expect(document.body.textContent).toContain('预计到仓日期')
|
expect(document.body.textContent).toContain('预计到仓日期')
|
||||||
expect(document.body.textContent).toContain('托盘类型')
|
expect(document.body.textContent).toContain('托盘类型')
|
||||||
|
const inboundCodeInput = wrapper.findAllComponents({ name: 'ElInput' })
|
||||||
|
.find((input) => input.props('placeholder') === '订单冻结入仓码')
|
||||||
|
expect(inboundCodeInput?.props('disabled')).toBe(true)
|
||||||
|
const receivingWarehouseInput = wrapper.findAllComponents({ name: 'ElInput' })
|
||||||
|
.find((input) => input.props('placeholder') === '当前柜接柜仓')
|
||||||
|
expect(receivingWarehouseInput?.props('disabled')).toBe(true)
|
||||||
|
expect(receivingWarehouseInput?.props('modelValue')).toBe('LAX01 · 洛杉矶接柜仓')
|
||||||
|
expect(document.body.textContent).toContain('接柜仓按柜统一')
|
||||||
|
expect(listAllPlatformWarehouses).not.toHaveBeenCalled()
|
||||||
expect(createManualInboundPlan).not.toHaveBeenCalled()
|
expect(createManualInboundPlan).not.toHaveBeenCalled()
|
||||||
expect(convertLastMileContainerToInboundPlans).not.toHaveBeenCalled()
|
expect(convertLastMileContainerToInboundPlans).not.toHaveBeenCalled()
|
||||||
expect(listContainerPlanProgress).not.toHaveBeenCalled()
|
expect(listContainerPlanProgress).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('shows the order-frozen inbound code but never sends it or a per-order receiving warehouse override', async () => {
|
||||||
|
const wrapper = await openPending()
|
||||||
|
await selectRows(wrapper, [selectedRow()])
|
||||||
|
await button(wrapper, '生成入库计划')!.trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
const datePicker = wrapper.find('.command-form').findComponent({ name: 'ElDatePicker' })
|
||||||
|
datePicker.vm.$emit('update:modelValue', '2026-09-08')
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
const confirm = [...document.body.querySelectorAll('button')]
|
||||||
|
.find((item) => item.textContent?.includes('保存并生成入库单')) as HTMLButtonElement
|
||||||
|
confirm.click()
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(createManualInboundPlan).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
customerId, orderId: '2092873463568596994', expectedOrderVersion: 3,
|
||||||
|
plannedArrivalDate: '2026-09-08', palletCount: 2,
|
||||||
|
}), expect.stringContaining('manual-inbound-plan-'))
|
||||||
|
const payload = vi.mocked(createManualInboundPlan).mock.calls[0][0] as unknown as Record<string, unknown>
|
||||||
|
expect(payload).not.toHaveProperty('inboundCode')
|
||||||
|
expect(payload).not.toHaveProperty('plannedWarehouseId')
|
||||||
|
expect(payload).not.toHaveProperty('plannedWarehouseCode')
|
||||||
|
expect(payload).not.toHaveProperty('plannedWarehouseName')
|
||||||
|
})
|
||||||
|
|
||||||
it('opens a cabinet selector without requiring order selection and loads server summaries only then', async () => {
|
it('opens a cabinet selector without requiring order selection and loads server summaries only then', async () => {
|
||||||
const wrapper = await openPending()
|
const wrapper = await openPending()
|
||||||
await openContainerDialog(wrapper)
|
await openContainerDialog(wrapper)
|
||||||
|
|||||||
@@ -42,9 +42,10 @@ async function exportOrders() { exportLoading.value = true; try { const { create
|
|||||||
const actionError = ref<ApiErrorDetails | null>(null)
|
const actionError = ref<ApiErrorDetails | null>(null)
|
||||||
const actionTarget = ref<LastMileOrderListItem | null>(null)
|
const actionTarget = ref<LastMileOrderListItem | null>(null)
|
||||||
const inboundPlanOpen = ref(false)
|
const inboundPlanOpen = ref(false)
|
||||||
const inboundPlanWarehouseLoading = ref(false)
|
const inboundPlanWarehouseLabel = computed(() => [actionTarget.value?.overseasWarehouseCode, actionTarget.value?.warehouse]
|
||||||
const inboundPlanWarehouses = ref<TransferWarehouseOption[]>([])
|
.filter((value) => value && value !== '—')
|
||||||
const inboundPlanForm = reactive({ warehouseId: '', plannedArrivalDate: '', inboundCode: '', palletCount: 1, palletType: 'STANDARD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' })
|
.join(' · '))
|
||||||
|
const inboundPlanForm = reactive({ plannedArrivalDate: '', inboundCode: '', palletCount: 1, palletType: 'STANDARD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' })
|
||||||
const holdOpen = ref(false)
|
const holdOpen = ref(false)
|
||||||
const holdForm = reactive({ held: true, chargeable: true, reason: '' })
|
const holdForm = reactive({ held: true, chargeable: true, reason: '' })
|
||||||
const manualNodesOpen = ref(false)
|
const manualNodesOpen = ref(false)
|
||||||
@@ -436,43 +437,28 @@ async function submitTransfer() {
|
|||||||
function idempotencyKey(prefix: string) {
|
function idempotencyKey(prefix: string) {
|
||||||
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`
|
||||||
}
|
}
|
||||||
async function loadInboundPlanWarehouses() {
|
|
||||||
inboundPlanWarehouseLoading.value = true
|
|
||||||
try {
|
|
||||||
inboundPlanWarehouses.value = (await listAllPlatformWarehouses({ warehouseType: 'OVERSEAS', status: 'ACTIVE' })).map(toTransferWarehouseOption)
|
|
||||||
const targetWarehouse = inboundPlanWarehouses.value.find((item) => actionTarget.value?.warehouse.includes(item.warehouseName))
|
|
||||||
if (targetWarehouse) inboundPlanForm.warehouseId = targetWarehouse.warehouseId
|
|
||||||
} catch (error) {
|
|
||||||
actionError.value = getApiErrorDetails(error, '入库仓候选加载失败,请重试')
|
|
||||||
} finally {
|
|
||||||
inboundPlanWarehouseLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function openInboundPlan(source?: object) {
|
function openInboundPlan(source?: object) {
|
||||||
const target = source ? source as LastMileOrderListItem : (selectedRows.value.length === 1 ? selectedRows.value[0] : null)
|
const target = source ? source as LastMileOrderListItem : (selectedRows.value.length === 1 ? selectedRows.value[0] : null)
|
||||||
if (!target) { ElMessage.warning('生成入库计划时请选择 1 条尾程订单'); return }
|
if (!target) { ElMessage.warning('生成入库计划时请选择 1 条尾程订单'); return }
|
||||||
if (target.detained) { ElMessage.warning('订单处于暂扣状态,解除暂扣后才能生成入库计划'); return }
|
if (target.detained) { ElMessage.warning('订单处于暂扣状态,解除暂扣后才能生成入库计划'); return }
|
||||||
actionTarget.value = target
|
actionTarget.value = target
|
||||||
Object.assign(inboundPlanForm, { warehouseId: '', plannedArrivalDate: '', inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: typeof target.pallets === 'number' && target.pallets > 0 ? target.pallets : 1, palletType: 'STANDARD', assigned: false, mergeRequired: false, urgent: false, remark: '' })
|
Object.assign(inboundPlanForm, { plannedArrivalDate: '', inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: typeof target.pallets === 'number' && target.pallets > 0 ? target.pallets : 1, palletType: 'STANDARD', assigned: false, mergeRequired: false, urgent: false, remark: '' })
|
||||||
inboundPlanWarehouses.value = []
|
|
||||||
actionError.value = null
|
actionError.value = null
|
||||||
inboundPlanOpen.value = true
|
inboundPlanOpen.value = true
|
||||||
void loadInboundPlanWarehouses()
|
|
||||||
}
|
}
|
||||||
async function submitInboundPlan() {
|
async function submitInboundPlan() {
|
||||||
const row = actionTarget.value
|
const row = actionTarget.value
|
||||||
const warehouse = inboundPlanWarehouses.value.find((item) => item.warehouseId === inboundPlanForm.warehouseId)
|
const frozenInboundCode = String(row?.inboundCode || '').trim()
|
||||||
if (!row || !warehouse || !inboundPlanForm.plannedArrivalDate || !inboundPlanForm.inboundCode.trim() || inboundPlanForm.palletCount < 1) {
|
if (!row || !row.overseasWarehouseId || !inboundPlanForm.plannedArrivalDate || !frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm.palletCount < 1) {
|
||||||
ElMessage.warning('请完整填写计划入库仓、预计到仓日期、入仓码和托数')
|
ElMessage.warning('当前柜须已分配接柜仓,并完整填写预计到仓日期和托数')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actionLoading.value = true; actionError.value = null
|
actionLoading.value = true; actionError.value = null
|
||||||
try {
|
try {
|
||||||
await createManualInboundPlan({
|
await createManualInboundPlan({
|
||||||
customerId: row.customerId, orderId: row.id, expectedOrderVersion: row.version,
|
customerId: row.customerId, orderId: row.id, expectedOrderVersion: row.version,
|
||||||
plannedWarehouseId: warehouse.warehouseId, plannedWarehouseCode: warehouse.warehouseCode,
|
plannedArrivalDate: inboundPlanForm.plannedArrivalDate,
|
||||||
plannedWarehouseName: warehouse.warehouseName, plannedArrivalDate: inboundPlanForm.plannedArrivalDate,
|
palletCount: inboundPlanForm.palletCount,
|
||||||
inboundCode: inboundPlanForm.inboundCode.trim(), palletCount: inboundPlanForm.palletCount,
|
|
||||||
palletType: inboundPlanForm.palletType, assigned: inboundPlanForm.assigned,
|
palletType: inboundPlanForm.palletType, assigned: inboundPlanForm.assigned,
|
||||||
mergeRequired: inboundPlanForm.mergeRequired, urgent: inboundPlanForm.urgent,
|
mergeRequired: inboundPlanForm.mergeRequired, urgent: inboundPlanForm.urgent,
|
||||||
remark: inboundPlanForm.remark.trim() || undefined,
|
remark: inboundPlanForm.remark.trim() || undefined,
|
||||||
@@ -715,10 +701,10 @@ onMounted(() => { void load() })
|
|||||||
</LoadErrorState>
|
</LoadErrorState>
|
||||||
</el-form>
|
</el-form>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
<AppDialog v-model="inboundPlanOpen" title="入库计划" width="min(720px, calc(100vw - 28px))" confirm-text="保存并生成入库单" :loading="inboundPlanWarehouseLoading" :confirm-loading="actionLoading" @confirm="submitInboundPlan">
|
<AppDialog v-model="inboundPlanOpen" title="入库计划" width="min(720px, calc(100vw - 28px))" confirm-text="保存并生成入库单" :confirm-loading="actionLoading" @confirm="submitInboundPlan">
|
||||||
<el-alert title="保存后生成入库单;清关完成后入库计划不可更改。" type="info" :closable="false" show-icon />
|
<el-alert title="保存后生成入库单;清关完成后入库计划不可更改。" type="info" :closable="false" show-icon />
|
||||||
<el-form class="command-form" label-position="top">
|
<el-form class="command-form" label-position="top">
|
||||||
<div class="command-form__grid"><el-form-item label="计划入库仓" required><el-select v-model="inboundPlanForm.warehouseId" filterable placeholder="请选择海外仓"><el-option v-for="warehouse in inboundPlanWarehouses" :key="warehouse.warehouseId" :label="warehouse.label" :value="warehouse.warehouseId" /></el-select></el-form-item><el-form-item label="预计到仓日期" required><el-date-picker v-model="inboundPlanForm.plannedArrivalDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" /></el-form-item><el-form-item label="入仓码" required><el-input v-model="inboundPlanForm.inboundCode" maxlength="128" placeholder="请输入入仓码" /></el-form-item><el-form-item label="托数" required><el-input-number v-model="inboundPlanForm.palletCount" :min="1" :max="999" controls-position="right" placeholder="请输入托数" /></el-form-item><el-form-item label="托盘类型"><el-select v-model="inboundPlanForm.palletType" placeholder="请选择托盘类型"><el-option label="标准托盘" value="STANDARD" /><el-option label="木托盘" value="WOOD" /><el-option label="纸托盘" value="PAPER" /></el-select></el-form-item><el-form-item label="是否分配"><el-switch v-model="inboundPlanForm.assigned" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="是否合托"><el-switch v-model="inboundPlanForm.mergeRequired" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="是否加急"><el-switch v-model="inboundPlanForm.urgent" inline-prompt active-text="是" inactive-text="否" /></el-form-item></div>
|
<div class="command-form__grid"><el-form-item label="计划入库仓"><el-input :model-value="inboundPlanWarehouseLabel" disabled placeholder="当前柜接柜仓" /><small>接柜仓按柜统一;如需变更,请在柜子工作台执行“变更接柜仓”。</small></el-form-item><el-form-item label="预计到仓日期" required><el-date-picker v-model="inboundPlanForm.plannedArrivalDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" /></el-form-item><el-form-item label="入仓码"><el-input :model-value="inboundPlanForm.inboundCode" disabled placeholder="订单冻结入仓码" /></el-form-item><el-form-item label="托数" required><el-input-number v-model="inboundPlanForm.palletCount" :min="1" :max="999" controls-position="right" placeholder="请输入托数" /></el-form-item><el-form-item label="托盘类型"><el-select v-model="inboundPlanForm.palletType" placeholder="请选择托盘类型"><el-option label="标准托盘" value="STANDARD" /><el-option label="木托盘" value="WOOD" /><el-option label="纸托盘" value="PAPER" /></el-select></el-form-item><el-form-item label="是否分配"><el-switch v-model="inboundPlanForm.assigned" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="是否合托"><el-switch v-model="inboundPlanForm.mergeRequired" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="是否加急"><el-switch v-model="inboundPlanForm.urgent" inline-prompt active-text="是" inactive-text="否" /></el-form-item></div>
|
||||||
<el-form-item label="备注"><el-input v-model="inboundPlanForm.remark" type="textarea" :rows="3" maxlength="1000" show-word-limit placeholder="请输入备注(选填)" /></el-form-item>
|
<el-form-item label="备注"><el-input v-model="inboundPlanForm.remark" type="textarea" :rows="3" maxlength="1000" show-word-limit placeholder="请输入备注(选填)" /></el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export interface LastMileOrderListItem {
|
|||||||
inboundCode: string
|
inboundCode: string
|
||||||
lxCode: string
|
lxCode: string
|
||||||
owner: string
|
owner: string
|
||||||
|
overseasWarehouseId: string
|
||||||
|
overseasWarehouseCode: string
|
||||||
warehouse: string
|
warehouse: string
|
||||||
transferWarehouse: string
|
transferWarehouse: string
|
||||||
fbaWarehouse: string
|
fbaWarehouse: string
|
||||||
|
|||||||
Reference in New Issue
Block a user