fix: preserve dropship sku loading details
This commit is contained in:
+54
-12
@@ -39,13 +39,34 @@ import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate; import org.apache.iba
|
|||||||
AND m.id=(SELECT MAX(m2.id) FROM fl_inbound_measurement m2
|
AND m.id=(SELECT MAX(m2.id) FROM fl_inbound_measurement m2
|
||||||
WHERE m2.tenant_id=o.tenant_id AND m2.inbound_id=o.id AND m2.status='APPROVED')
|
WHERE m2.tenant_id=o.tenant_id AND m2.inbound_id=o.id AND m2.status='APPROVED')
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT tenant_id,measurement_id,inbound_detail_id,SUM(package_count) package_count,
|
SELECT measured.tenant_id,batch.inbound_id,measured.measurement_id,measured.inbound_detail_id,
|
||||||
COALESCE(SUM(COALESCE(total_volume_cbm,COALESCE(unit_volume_cbm,0)*package_count))
|
measured.package_count,measured.unit_volume_cbm
|
||||||
|
FROM (
|
||||||
|
SELECT tenant_id,measurement_id,inbound_detail_id,SUM(package_count) package_count,
|
||||||
|
COALESCE(SUM(COALESCE(total_volume_cbm,COALESCE(unit_volume_cbm,0)*package_count))
|
||||||
/ NULLIF(SUM(package_count),0),0) unit_volume_cbm
|
/ NULLIF(SUM(package_count),0),0) unit_volume_cbm
|
||||||
FROM fl_inbound_measurement_item
|
FROM fl_inbound_measurement_item
|
||||||
WHERE inbound_detail_id IS NOT NULL AND package_count>0
|
WHERE inbound_detail_id IS NOT NULL AND package_count>0
|
||||||
GROUP BY tenant_id,measurement_id,inbound_detail_id
|
GROUP BY tenant_id,measurement_id,inbound_detail_id
|
||||||
) i ON i.tenant_id=o.tenant_id AND i.measurement_id=m.id
|
) measured
|
||||||
|
JOIN fl_inbound_measurement batch ON batch.id=measured.measurement_id
|
||||||
|
AND batch.tenant_id=measured.tenant_id
|
||||||
|
JOIN fl_inbound_order measured_order ON measured_order.id=batch.inbound_id
|
||||||
|
AND measured_order.tenant_id=batch.tenant_id
|
||||||
|
WHERE COALESCE(measured_order.fulfillment_type,'STANDARD')<>'ONE_CLICK_DROPSHIP'
|
||||||
|
UNION ALL
|
||||||
|
SELECT carton.tenant_id,carton.inbound_id,batch.id,carton.inbound_detail_id,COUNT(*),
|
||||||
|
COALESCE(batch.total_volume_cbm/NULLIF(source.total_package_count,0),0)
|
||||||
|
FROM fl_dropship_pallet_carton carton
|
||||||
|
JOIN fl_dropship_pallet pallet ON pallet.id=carton.pallet_id AND pallet.pallet_status='COMPLETED'
|
||||||
|
JOIN fl_inbound_order source ON source.id=carton.inbound_id AND source.tenant_id=carton.tenant_id
|
||||||
|
AND source.fulfillment_type='ONE_CLICK_DROPSHIP'
|
||||||
|
JOIN fl_inbound_measurement batch ON batch.inbound_id=source.id AND batch.tenant_id=source.tenant_id
|
||||||
|
AND batch.status='APPROVED' AND batch.id=(SELECT MAX(latest.id) FROM fl_inbound_measurement latest
|
||||||
|
WHERE latest.tenant_id=source.tenant_id AND latest.inbound_id=source.id AND latest.status='APPROVED')
|
||||||
|
GROUP BY carton.tenant_id,carton.inbound_id,batch.id,carton.inbound_detail_id,
|
||||||
|
batch.total_volume_cbm,source.total_package_count
|
||||||
|
) i ON i.tenant_id=o.tenant_id AND i.inbound_id=o.id AND i.measurement_id=m.id
|
||||||
WHERE o.warehouse_id=#{warehouseId} AND o.process_status='WAIT_SPLIT'
|
WHERE o.warehouse_id=#{warehouseId} AND o.process_status='WAIT_SPLIT'
|
||||||
AND o.actual_gross_weight_kg IS NOT NULL AND o.actual_net_weight_kg IS NOT NULL
|
AND o.actual_gross_weight_kg IS NOT NULL AND o.actual_net_weight_kg IS NOT NULL
|
||||||
AND (o.inbound_role='MASTER' OR (o.split_purpose='REMAINDER' AND o.split_status='NOT_SPLIT'))
|
AND (o.inbound_role='MASTER' OR (o.split_purpose='REMAINDER' AND o.split_status='NOT_SPLIT'))
|
||||||
@@ -89,13 +110,34 @@ import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate; import org.apache.iba
|
|||||||
AND m.id=(SELECT MAX(m2.id) FROM fl_inbound_measurement m2
|
AND m.id=(SELECT MAX(m2.id) FROM fl_inbound_measurement m2
|
||||||
WHERE m2.tenant_id=o.tenant_id AND m2.inbound_id=o.id AND m2.status='APPROVED')
|
WHERE m2.tenant_id=o.tenant_id AND m2.inbound_id=o.id AND m2.status='APPROVED')
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT tenant_id,measurement_id,inbound_detail_id,SUM(package_count) package_count,
|
SELECT measured.tenant_id,batch.inbound_id,measured.measurement_id,measured.inbound_detail_id,
|
||||||
COALESCE(SUM(COALESCE(total_volume_cbm,COALESCE(unit_volume_cbm,0)*package_count))
|
measured.package_count,measured.unit_volume_cbm
|
||||||
|
FROM (
|
||||||
|
SELECT tenant_id,measurement_id,inbound_detail_id,SUM(package_count) package_count,
|
||||||
|
COALESCE(SUM(COALESCE(total_volume_cbm,COALESCE(unit_volume_cbm,0)*package_count))
|
||||||
/ NULLIF(SUM(package_count),0),0) unit_volume_cbm
|
/ NULLIF(SUM(package_count),0),0) unit_volume_cbm
|
||||||
FROM fl_inbound_measurement_item
|
FROM fl_inbound_measurement_item
|
||||||
WHERE inbound_detail_id IS NOT NULL AND package_count>0
|
WHERE inbound_detail_id IS NOT NULL AND package_count>0
|
||||||
GROUP BY tenant_id,measurement_id,inbound_detail_id
|
GROUP BY tenant_id,measurement_id,inbound_detail_id
|
||||||
) i ON i.tenant_id=o.tenant_id AND i.measurement_id=m.id
|
) measured
|
||||||
|
JOIN fl_inbound_measurement batch ON batch.id=measured.measurement_id
|
||||||
|
AND batch.tenant_id=measured.tenant_id
|
||||||
|
JOIN fl_inbound_order measured_order ON measured_order.id=batch.inbound_id
|
||||||
|
AND measured_order.tenant_id=batch.tenant_id
|
||||||
|
WHERE COALESCE(measured_order.fulfillment_type,'STANDARD')<>'ONE_CLICK_DROPSHIP'
|
||||||
|
UNION ALL
|
||||||
|
SELECT carton.tenant_id,carton.inbound_id,batch.id,carton.inbound_detail_id,COUNT(*),
|
||||||
|
COALESCE(batch.total_volume_cbm/NULLIF(source.total_package_count,0),0)
|
||||||
|
FROM fl_dropship_pallet_carton carton
|
||||||
|
JOIN fl_dropship_pallet pallet ON pallet.id=carton.pallet_id AND pallet.pallet_status='COMPLETED'
|
||||||
|
JOIN fl_inbound_order source ON source.id=carton.inbound_id AND source.tenant_id=carton.tenant_id
|
||||||
|
AND source.fulfillment_type='ONE_CLICK_DROPSHIP'
|
||||||
|
JOIN fl_inbound_measurement batch ON batch.inbound_id=source.id AND batch.tenant_id=source.tenant_id
|
||||||
|
AND batch.status='APPROVED' AND batch.id=(SELECT MAX(latest.id) FROM fl_inbound_measurement latest
|
||||||
|
WHERE latest.tenant_id=source.tenant_id AND latest.inbound_id=source.id AND latest.status='APPROVED')
|
||||||
|
GROUP BY carton.tenant_id,carton.inbound_id,batch.id,carton.inbound_detail_id,
|
||||||
|
batch.total_volume_cbm,source.total_package_count
|
||||||
|
) i ON i.tenant_id=o.tenant_id AND i.inbound_id=o.id AND i.measurement_id=m.id
|
||||||
WHERE o.warehouse_id=#{warehouseId} AND o.id=#{inboundId}
|
WHERE o.warehouse_id=#{warehouseId} AND o.id=#{inboundId}
|
||||||
AND (o.process_status='WAIT_SPLIT' OR (o.process_status='LOADING' AND EXISTS (
|
AND (o.process_status='WAIT_SPLIT' OR (o.process_status='LOADING' AND EXISTS (
|
||||||
SELECT 1 FROM fl_loading_plan_item same_plan_item
|
SELECT 1 FROM fl_loading_plan_item same_plan_item
|
||||||
|
|||||||
+7
@@ -42,6 +42,10 @@ class SmartLoadingCandidateMapperContractTest {
|
|||||||
.contains("WHERE inbound_detail_id IS NOT NULL AND package_count>0")
|
.contains("WHERE inbound_detail_id IS NOT NULL AND package_count>0")
|
||||||
.contains("SUM(package_count) package_count")
|
.contains("SUM(package_count) package_count")
|
||||||
.contains("GROUP BY tenant_id,measurement_id,inbound_detail_id")
|
.contains("GROUP BY tenant_id,measurement_id,inbound_detail_id")
|
||||||
|
.contains("fl_dropship_pallet_carton carton")
|
||||||
|
.contains("pallet.pallet_status='COMPLETED'")
|
||||||
|
.contains("source.fulfillment_type='ONE_CLICK_DROPSHIP'")
|
||||||
|
.contains("i.inbound_id=o.id")
|
||||||
.contains("o.warehouse_id=#{warehouseId}")
|
.contains("o.warehouse_id=#{warehouseId}")
|
||||||
.contains("o.process_status='WAIT_SPLIT'")
|
.contains("o.process_status='WAIT_SPLIT'")
|
||||||
.contains("unitGrossWeightKg")
|
.contains("unitGrossWeightKg")
|
||||||
@@ -82,6 +86,9 @@ class SmartLoadingCandidateMapperContractTest {
|
|||||||
.contains("SELECT SUM(li.package_count)")
|
.contains("SELECT SUM(li.package_count)")
|
||||||
.contains("SUM(package_count) package_count")
|
.contains("SUM(package_count) package_count")
|
||||||
.contains("GROUP BY tenant_id,measurement_id,inbound_detail_id")
|
.contains("GROUP BY tenant_id,measurement_id,inbound_detail_id")
|
||||||
|
.contains("fl_dropship_pallet_carton carton")
|
||||||
|
.contains("source.fulfillment_type='ONE_CLICK_DROPSHIP'")
|
||||||
|
.contains("i.inbound_id=o.id")
|
||||||
.contains("FOR UPDATE");
|
.contains("FOR UPDATE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,8 +212,9 @@ export async function run(context) {
|
|||||||
if (!containerBookingId) return
|
if (!containerBookingId) return
|
||||||
const candidateResponse = await context.request('GET', `${smartLoadingBase}/candidates?bookingId=${encodeURIComponent(containerBookingId)}&warehouseId=${encodeURIComponent(warehouseId)}`)
|
const candidateResponse = await context.request('GET', `${smartLoadingBase}/candidates?bookingId=${encodeURIComponent(containerBookingId)}&warehouseId=${encodeURIComponent(warehouseId)}`)
|
||||||
const candidates = context.apiSuccess(MODULE, '查询本轮入库单柜次装柜候选', candidateResponse, '', OPTIONS)
|
const candidates = context.apiSuccess(MODULE, '查询本轮入库单柜次装柜候选', candidateResponse, '', OPTIONS)
|
||||||
const candidate = Array.isArray(candidates) ? candidates.find((item) => id(item.inboundId) === inboundId
|
const inboundCandidates = Array.isArray(candidates) ? candidates.filter((item) => id(item.inboundId) === inboundId
|
||||||
&& Number(item.availablePackageCount) > 0) : null
|
&& Number(item.availablePackageCount) > 0) : []
|
||||||
|
const candidate = inboundCandidates[0]
|
||||||
context.assert(MODULE, '柜次装柜候选与入库单、客户和仓库一致', candidateResponse,
|
context.assert(MODULE, '柜次装柜候选与入库单、客户和仓库一致', candidateResponse,
|
||||||
Boolean(candidate?.inboundDetailId) && id(candidate?.accountId) === tenantId
|
Boolean(candidate?.inboundDetailId) && id(candidate?.accountId) === tenantId
|
||||||
&& id(candidate?.warehouseId) === warehouseId,
|
&& id(candidate?.warehouseId) === warehouseId,
|
||||||
@@ -226,17 +227,23 @@ export async function run(context) {
|
|||||||
const loadingPlan = context.apiSuccess(MODULE, '读取柜次创建时原子生成的智能装柜计划', loadingPlanResponse, '', OPTIONS)
|
const loadingPlan = context.apiSuccess(MODULE, '读取柜次创建时原子生成的智能装柜计划', loadingPlanResponse, '', OPTIONS)
|
||||||
const loadingPlanId = id(loadingPlan?.id)
|
const loadingPlanId = id(loadingPlan?.id)
|
||||||
if (!loadingPlanId) return
|
if (!loadingPlanId) return
|
||||||
const initialAssignmentCount = source?.fulfillmentType === 'ONE_CLICK_DROPSHIP'
|
const dropshipAssignment = source?.fulfillmentType === 'ONE_CLICK_DROPSHIP'
|
||||||
? Number(candidate.availablePackageCount) : 1
|
const initialAssignmentCount = dropshipAssignment
|
||||||
const assignmentResponse = await request(context, 'POST', `${smartLoadingBase}/plans/${loadingPlanId}/assignments`, {
|
? inboundCandidates.reduce((total, item) => total + Number(item.availablePackageCount || 0), 0) : 1
|
||||||
inboundId, inboundDetailId: id(candidate.inboundDetailId), packageCount: initialAssignmentCount,
|
const assignmentPath = dropshipAssignment
|
||||||
expectedVersion: Number(loadingPlan.version), operationId: `${context.marker}-bridge-assign`.slice(0, 128),
|
? `${smartLoadingBase}/plans/${loadingPlanId}/inbound-assignments`
|
||||||
})
|
: `${smartLoadingBase}/plans/${loadingPlanId}/assignments`
|
||||||
|
const assignmentBody = dropshipAssignment
|
||||||
|
? { inboundId, packageCount: initialAssignmentCount, expectedVersion: Number(loadingPlan.version),
|
||||||
|
operationId: `${context.marker}-bridge-assign-inbound`.slice(0, 128) }
|
||||||
|
: { inboundId, inboundDetailId: id(candidate.inboundDetailId), packageCount: initialAssignmentCount,
|
||||||
|
expectedVersion: Number(loadingPlan.version), operationId: `${context.marker}-bridge-assign`.slice(0, 128) }
|
||||||
|
const assignmentResponse = await request(context, 'POST', assignmentPath, assignmentBody)
|
||||||
const assignedLoadingPlan = context.apiSuccess(MODULE, '将本轮入库货物分配到柜次计划', assignmentResponse, '', OPTIONS)
|
const assignedLoadingPlan = context.apiSuccess(MODULE, '将本轮入库货物分配到柜次计划', assignmentResponse, '', OPTIONS)
|
||||||
context.assert(MODULE, '柜次计划明细和版本真实持久化', assignmentResponse,
|
context.assert(MODULE, '柜次计划明细和版本真实持久化', assignmentResponse,
|
||||||
assignedLoadingPlan?.items?.some?.((item) => id(item.inboundId) === inboundId
|
assignedLoadingPlan?.items?.filter?.((item) => id(item.inboundId) === inboundId)
|
||||||
&& Number(item.packageCount) === initialAssignmentCount)
|
.reduce((total, item) => total + Number(item.packageCount || 0), 0) === initialAssignmentCount
|
||||||
&& Number(assignedLoadingPlan?.version) === 1,
|
&& Number(assignedLoadingPlan?.version) >= 1,
|
||||||
`loadingPlanId=${loadingPlanId}, assignedPackages=${initialAssignmentCount}, version=${assignedLoadingPlan?.version ?? 'missing'}`, OPTIONS)
|
`loadingPlanId=${loadingPlanId}, assignedPackages=${initialAssignmentCount}, version=${assignedLoadingPlan?.version ?? 'missing'}`, OPTIONS)
|
||||||
|
|
||||||
const assignedItem = assignedLoadingPlan?.items?.find?.((item) => id(item.inboundId) === inboundId)
|
const assignedItem = assignedLoadingPlan?.items?.find?.((item) => id(item.inboundId) === inboundId)
|
||||||
@@ -499,12 +506,15 @@ export async function run(context) {
|
|||||||
const canonicalTaskId = id(canonicalTask?.id)
|
const canonicalTaskId = id(canonicalTask?.id)
|
||||||
const taskDetailPath = `${pdaBase}/tasks/${canonicalTaskId}?warehouseId=${encodeURIComponent(warehouseId)}`
|
const taskDetailPath = `${pdaBase}/tasks/${canonicalTaskId}?warehouseId=${encodeURIComponent(warehouseId)}`
|
||||||
const canonicalItem = canonicalTask?.items?.find?.((item) => id(item.inboundId) === inboundId)
|
const canonicalItem = canonicalTask?.items?.find?.((item) => id(item.inboundId) === inboundId)
|
||||||
|
const canonicalPickItems = dropshipAssignment
|
||||||
|
? canonicalTask?.items?.filter?.((item) => id(item.inboundId) === inboundId) || []
|
||||||
|
: canonicalItem ? [canonicalItem] : []
|
||||||
context.assert(MODULE, '柜次 PDA 任务与桥接入库明细一致', canonicalTaskResponse,
|
context.assert(MODULE, '柜次 PDA 任务与桥接入库明细一致', canonicalTaskResponse,
|
||||||
Boolean(canonicalTaskId) && canonicalTaskId === confirmedTaskId && id(canonicalTask?.loadingPlanId) === loadingPlanId
|
Boolean(canonicalTaskId) && canonicalTaskId === confirmedTaskId && id(canonicalTask?.loadingPlanId) === loadingPlanId
|
||||||
&& id(canonicalTask?.containerBookingId) === containerBookingId
|
&& id(canonicalTask?.containerBookingId) === containerBookingId
|
||||||
&& canonicalTask?.workflowStatus === 'UNASSIGNED' && Boolean(canonicalItem?.id),
|
&& canonicalTask?.workflowStatus === 'UNASSIGNED' && canonicalPickItems.length > 0,
|
||||||
`taskId=${canonicalTaskId || 'missing'}, itemId=${id(canonicalItem?.id) || 'missing'}`, OPTIONS)
|
`taskId=${canonicalTaskId || 'missing'}, itemCount=${canonicalPickItems.length}`, OPTIONS)
|
||||||
if (!canonicalTaskId || !canonicalItem?.id) return
|
if (!canonicalTaskId || !canonicalPickItems.length) return
|
||||||
|
|
||||||
const beforeClaimPlanResponse = await context.request('GET', `${smartLoadingBase}/plans/${loadingPlanId}`)
|
const beforeClaimPlanResponse = await context.request('GET', `${smartLoadingBase}/plans/${loadingPlanId}`)
|
||||||
const beforeClaimPlan = context.apiSuccess(MODULE, '领取前读取柜次计划即时版本', beforeClaimPlanResponse, '', OPTIONS)
|
const beforeClaimPlan = context.apiSuccess(MODULE, '领取前读取柜次计划即时版本', beforeClaimPlanResponse, '', OPTIONS)
|
||||||
@@ -570,10 +580,10 @@ export async function run(context) {
|
|||||||
warehouseId, loadingPlanId, containerBookingId,
|
warehouseId, loadingPlanId, containerBookingId,
|
||||||
expectedVersion: Number(claimed?.version),
|
expectedVersion: Number(claimed?.version),
|
||||||
inventorySnapshotToken: inventory.snapshotToken,
|
inventorySnapshotToken: inventory.snapshotToken,
|
||||||
items: [{
|
items: canonicalPickItems.map((item) => ({
|
||||||
itemId: id(canonicalItem.id), action: 'PICK', pickedPackageCount: initialAssignmentCount,
|
itemId: id(item.id), action: 'PICK', pickedPackageCount: Number(item.plannedPackageCount),
|
||||||
releaseMode: 'MANUAL', locationIds: [], version: Number(canonicalItem.version),
|
releaseMode: 'MANUAL', locationIds: [], version: Number(item.version),
|
||||||
}],
|
})),
|
||||||
}
|
}
|
||||||
|
|
||||||
const staleBatch = await request(context, 'POST', batchPath,
|
const staleBatch = await request(context, 'POST', batchPath,
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ test('measurement and loading pass destination identity into the LOADED producer
|
|||||||
assert.match(entry, /skuCodes: Array\.isArray\(fixtureOrder\.skuCodes\) \? fixtureOrder\.skuCodes : \[\]/)
|
assert.match(entry, /skuCodes: Array\.isArray\(fixtureOrder\.skuCodes\) \? fixtureOrder\.skuCodes : \[\]/)
|
||||||
assert.match(entry, /skuLines: Array\.isArray\(fixtureOrder\.skuLines\) \? fixtureOrder\.skuLines : \[\]/)
|
assert.match(entry, /skuLines: Array\.isArray\(fixtureOrder\.skuLines\) \? fixtureOrder\.skuLines : \[\]/)
|
||||||
const pda = read('phase4-overseas-pda.mjs')
|
const pda = read('phase4-overseas-pda.mjs')
|
||||||
assert.match(loading, /pickedPackageCount: initialAssignmentCount/)
|
assert.match(loading, /inbound-assignments/)
|
||||||
|
assert.match(loading, /canonicalPickItems\.map/)
|
||||||
|
assert.match(loading, /pickedPackageCount: Number\(item\.plannedPackageCount\)/)
|
||||||
assert.match(pda, /find\(\(item\) => item\?\.fulfillmentType === 'ONE_CLICK_DROPSHIP'\)/)
|
assert.match(pda, /find\(\(item\) => item\?\.fulfillmentType === 'ONE_CLICK_DROPSHIP'\)/)
|
||||||
assert.match(pda, /resolveCartonFixtures\(context, token, current\.data\.inbounds, producerCartonMarks\)/)
|
assert.match(pda, /resolveCartonFixtures\(context, token, current\.data\.inbounds, producerCartonMarks\)/)
|
||||||
assert.match(pda, /FBA与OVERSEAS箱码resolve均为纯查询且零状态写入/)
|
assert.match(pda, /FBA与OVERSEAS箱码resolve均为纯查询且零状态写入/)
|
||||||
|
|||||||
Reference in New Issue
Block a user