|
|
|
@@ -1250,8 +1250,37 @@ open class PickOrderService( |
|
|
|
println("⚠️ WARNING: do_pick_order $doPickOrderId not found, skipping") |
|
|
|
return@forEach |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
println("🔍 Processing do_pick_order ID: ${dpo.id}, ticket: ${dpo.ticketNo}") |
|
|
|
|
|
|
|
// ✅ 新增:检查这个 do_pick_order 下是否还有其他未完成的 pick orders |
|
|
|
val allDoPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(dpo.id!!) |
|
|
|
val allPickOrderIdsInDpo = allDoPickOrderLines.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
|
|
|
|
println("🔍 DEBUG: This do_pick_order has ${allPickOrderIdsInDpo.size} pick orders") |
|
|
|
|
|
|
|
// ✅ 检查每个 pick order 的状态 |
|
|
|
val pickOrderStatuses = allPickOrderIdsInDpo.map { poId -> |
|
|
|
val po = pickOrderRepository.findById(poId).orElse(null) |
|
|
|
poId to po?.status |
|
|
|
} |
|
|
|
|
|
|
|
pickOrderStatuses.forEach { (poId, status) -> |
|
|
|
println("🔍 DEBUG: Pick order $poId status: $status") |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 只有当所有 pick orders 都是 COMPLETED 状态时,才移动到 record 表 |
|
|
|
val allPickOrdersCompleted = pickOrderStatuses.all { (_, status) -> |
|
|
|
status == PickOrderStatus.COMPLETED |
|
|
|
} |
|
|
|
|
|
|
|
if (!allPickOrdersCompleted) { |
|
|
|
println("⏳ Not all pick orders in this do_pick_order are completed, keeping do_pick_order alive") |
|
|
|
println("🔍 DEBUG: Completed pick orders: ${pickOrderStatuses.count { it.second == PickOrderStatus.COMPLETED }}/${pickOrderStatuses.size}") |
|
|
|
return@forEach // ✅ 跳过这个 do_pick_order,不删除它 |
|
|
|
} |
|
|
|
|
|
|
|
println("✅ All pick orders in this do_pick_order are completed, moving to record table") |
|
|
|
|
|
|
|
// 2) 先复制 do_pick_order -> do_pick_order_record |
|
|
|
val dpoRecord = DoPickOrderRecord( |
|
|
|
@@ -3516,261 +3545,307 @@ ORDER BY |
|
|
|
val enrichedResults = filteredResults |
|
|
|
return enrichedResults |
|
|
|
} |
|
|
|
open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, Any?> { |
|
|
|
println("=== Debug: getAllPickOrderLotsWithDetailsHierarchical (NEW STRUCTURE) ===") |
|
|
|
println("userId filter: $userId") |
|
|
|
|
|
|
|
val user = userService.find(userId).orElse(null) |
|
|
|
if (user == null) { |
|
|
|
println("❌ User not found: $userId") |
|
|
|
return emptyMap() |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ Step 1: 获取 do_pick_order 基本信息 |
|
|
|
val doPickOrderSql = """ |
|
|
|
SELECT DISTINCT |
|
|
|
dpo.id as do_pick_order_id, |
|
|
|
dpo.ticket_no, |
|
|
|
dpo.store_id, |
|
|
|
dpo.TruckLanceCode, |
|
|
|
dpo.truck_departure_time, |
|
|
|
dpo.ShopCode, |
|
|
|
dpo.ShopName |
|
|
|
FROM fpsmsdb.do_pick_order dpo |
|
|
|
INNER JOIN fpsmsdb.do_pick_order_line dpol ON dpol.do_pick_order_id = dpo.id AND dpol.deleted = 0 |
|
|
|
INNER JOIN fpsmsdb.pick_order po ON po.id = dpol.pick_order_id |
|
|
|
WHERE po.assignTo = :userId |
|
|
|
AND po.type = 'do' |
|
|
|
AND po.status IN ('assigned', 'released', 'picking') |
|
|
|
AND po.deleted = false |
|
|
|
AND dpo.deleted = false |
|
|
|
LIMIT 1 |
|
|
|
""".trimIndent() |
|
|
|
|
|
|
|
val doPickOrderInfo = jdbcDao.queryForMap(doPickOrderSql, mapOf("userId" to userId)).orElse(null) |
|
|
|
if (doPickOrderInfo == null) { |
|
|
|
println("❌ No do_pick_order found for user $userId") |
|
|
|
return mapOf( |
|
|
|
"fgInfo" to null, |
|
|
|
"pickOrders" to emptyList<Any>() |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
val doPickOrderId = (doPickOrderInfo["do_pick_order_id"] as? Number)?.toLong() |
|
|
|
println("🔍 Found do_pick_order ID: $doPickOrderId") |
|
|
|
|
|
|
|
// ✅ Step 2: 获取该 do_pick_order 下的所有 pick orders |
|
|
|
val pickOrdersSql = """ |
|
|
|
SELECT DISTINCT |
|
|
|
dpol.pick_order_id, |
|
|
|
dpol.pick_order_code, |
|
|
|
dpol.do_order_id, |
|
|
|
dpol.delivery_order_code, |
|
|
|
po.consoCode, |
|
|
|
po.status, |
|
|
|
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as targetDate |
|
|
|
FROM fpsmsdb.do_pick_order_line dpol |
|
|
|
INNER JOIN fpsmsdb.pick_order po ON po.id = dpol.pick_order_id |
|
|
|
WHERE dpol.do_pick_order_id = :doPickOrderId |
|
|
|
AND dpol.deleted = 0 |
|
|
|
AND po.deleted = false |
|
|
|
ORDER BY dpol.pick_order_id |
|
|
|
""".trimIndent() |
|
|
|
// ... existing code ... |
|
|
|
|
|
|
|
open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, Any?> { |
|
|
|
println("=== Debug: getAllPickOrderLotsWithDetailsHierarchical (NEW STRUCTURE) ===") |
|
|
|
println("userId filter: $userId") |
|
|
|
|
|
|
|
val user = userService.find(userId).orElse(null) |
|
|
|
if (user == null) { |
|
|
|
println("❌ User not found: $userId") |
|
|
|
return emptyMap() |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ Step 1: 获取 do_pick_order 基本信息 |
|
|
|
val doPickOrderSql = """ |
|
|
|
SELECT DISTINCT |
|
|
|
dpo.id as do_pick_order_id, |
|
|
|
dpo.ticket_no, |
|
|
|
dpo.store_id, |
|
|
|
dpo.TruckLanceCode, |
|
|
|
dpo.truck_departure_time, |
|
|
|
dpo.ShopCode, |
|
|
|
dpo.ShopName |
|
|
|
FROM fpsmsdb.do_pick_order dpo |
|
|
|
INNER JOIN fpsmsdb.do_pick_order_line dpol ON dpol.do_pick_order_id = dpo.id AND dpol.deleted = 0 |
|
|
|
INNER JOIN fpsmsdb.pick_order po ON po.id = dpol.pick_order_id |
|
|
|
WHERE po.assignTo = :userId |
|
|
|
AND po.type = 'do' |
|
|
|
AND EXISTS ( |
|
|
|
SELECT 1 |
|
|
|
FROM fpsmsdb.do_pick_order_line dpol2 |
|
|
|
INNER JOIN fpsmsdb.pick_order po2 ON po2.id = dpol2.pick_order_id |
|
|
|
WHERE dpol2.do_pick_order_id = dpo.id |
|
|
|
AND dpol2.deleted = 0 |
|
|
|
AND po2.status IN ('assigned', 'released', 'picking') |
|
|
|
AND po2.deleted = false |
|
|
|
) |
|
|
|
AND po.deleted = false |
|
|
|
AND dpo.deleted = false |
|
|
|
LIMIT 1 |
|
|
|
""".trimIndent() |
|
|
|
|
|
|
|
val doPickOrderInfo = jdbcDao.queryForMap(doPickOrderSql, mapOf("userId" to userId)).orElse(null) |
|
|
|
if (doPickOrderInfo == null) { |
|
|
|
println("❌ No do_pick_order found for user $userId") |
|
|
|
return mapOf( |
|
|
|
"fgInfo" to null, |
|
|
|
"pickOrders" to emptyList<Any>() |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
val doPickOrderId = (doPickOrderInfo["do_pick_order_id"] as? Number)?.toLong() |
|
|
|
println("🔍 Found do_pick_order ID: $doPickOrderId") |
|
|
|
|
|
|
|
// ✅ Step 2: 获取该 do_pick_order 下的所有 pick orders |
|
|
|
val pickOrdersSql = """ |
|
|
|
SELECT DISTINCT |
|
|
|
dpol.pick_order_id, |
|
|
|
dpol.pick_order_code, |
|
|
|
dpol.do_order_id, |
|
|
|
dpol.delivery_order_code, |
|
|
|
po.consoCode, |
|
|
|
po.status, |
|
|
|
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as targetDate |
|
|
|
FROM fpsmsdb.do_pick_order_line dpol |
|
|
|
INNER JOIN fpsmsdb.pick_order po ON po.id = dpol.pick_order_id |
|
|
|
WHERE dpol.do_pick_order_id = :doPickOrderId |
|
|
|
AND dpol.deleted = 0 |
|
|
|
AND po.deleted = false |
|
|
|
ORDER BY dpol.pick_order_id |
|
|
|
""".trimIndent() |
|
|
|
|
|
|
|
val pickOrdersInfo = jdbcDao.queryForList(pickOrdersSql, mapOf("doPickOrderId" to doPickOrderId)) |
|
|
|
println("🔍 Found ${pickOrdersInfo.size} pick orders") |
|
|
|
|
|
|
|
// ✅ Step 3: 为每个 pick order 获取 lines 和 lots |
|
|
|
val allPickOrderLines = mutableListOf<Map<String, Any?>>() |
|
|
|
val lineCountsPerPickOrder = mutableListOf<Int>() |
|
|
|
val pickOrderIds = mutableListOf<Long>() |
|
|
|
val pickOrderCodes = mutableListOf<String>() |
|
|
|
val doOrderIds = mutableListOf<Long>() |
|
|
|
val deliveryOrderCodes = mutableListOf<String>() |
|
|
|
|
|
|
|
pickOrdersInfo.forEach { poInfo -> |
|
|
|
val pickOrderId = (poInfo["pick_order_id"] as? Number)?.toLong() |
|
|
|
val pickOrderCode = poInfo["pick_order_code"] as? String |
|
|
|
val doOrderId = (poInfo["do_order_id"] as? Number)?.toLong() |
|
|
|
val deliveryOrderCode = poInfo["delivery_order_code"] as? String |
|
|
|
|
|
|
|
val pickOrdersInfo = jdbcDao.queryForList(pickOrdersSql, mapOf("doPickOrderId" to doPickOrderId)) |
|
|
|
println("🔍 Found ${pickOrdersInfo.size} pick orders") |
|
|
|
// 收集 IDs 和 codes |
|
|
|
pickOrderId?.let { pickOrderIds.add(it) } |
|
|
|
pickOrderCode?.let { pickOrderCodes.add(it) } |
|
|
|
doOrderId?.let { doOrderIds.add(it) } |
|
|
|
deliveryOrderCode?.let { deliveryOrderCodes.add(it) } |
|
|
|
|
|
|
|
// ✅ Step 3: 为每个 pick order 获取 lines 和 lots(包括 null stock 的) |
|
|
|
val pickOrders = pickOrdersInfo.map { poInfo -> |
|
|
|
val pickOrderId = (poInfo["pick_order_id"] as? Number)?.toLong() |
|
|
|
|
|
|
|
// ✅ 查询该 pick order 的所有 lines 和 lots |
|
|
|
val linesSql = """ |
|
|
|
SELECT |
|
|
|
po.id as pickOrderId, |
|
|
|
po.code as pickOrderCode, |
|
|
|
po.consoCode as pickOrderConsoCode, |
|
|
|
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as pickOrderTargetDate, |
|
|
|
po.type as pickOrderType, |
|
|
|
po.status as pickOrderStatus, |
|
|
|
po.assignTo as pickOrderAssignTo, |
|
|
|
|
|
|
|
pol.id as pickOrderLineId, |
|
|
|
pol.qty as pickOrderLineRequiredQty, |
|
|
|
pol.status as pickOrderLineStatus, |
|
|
|
|
|
|
|
i.id as itemId, |
|
|
|
i.code as itemCode, |
|
|
|
i.name as itemName, |
|
|
|
uc.code as uomCode, |
|
|
|
uc.udfudesc as uomDesc, |
|
|
|
uc.udfShortDesc as uomShortDesc, |
|
|
|
|
|
|
|
ill.id as lotId, |
|
|
|
il.lotNo, |
|
|
|
DATE_FORMAT(il.expiryDate, '%Y-%m-%d') as expiryDate, |
|
|
|
w.name as location, |
|
|
|
COALESCE(uc.udfudesc, 'N/A') as stockUnit, |
|
|
|
w.`order` as routerIndex, |
|
|
|
w.code as routerRoute, |
|
|
|
|
|
|
|
CASE |
|
|
|
WHEN sol.status = 'rejected' THEN NULL |
|
|
|
ELSE (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) |
|
|
|
END as availableQty, |
|
|
|
|
|
|
|
COALESCE(spl.qty, 0) as requiredQty, |
|
|
|
COALESCE(sol.qty, 0) as actualPickQty, |
|
|
|
spl.id as suggestedPickLotId, |
|
|
|
ill.status as lotStatus, |
|
|
|
sol.id as stockOutLineId, |
|
|
|
sol.status as stockOutLineStatus, |
|
|
|
COALESCE(sol.qty, 0) as stockOutLineQty, |
|
|
|
COALESCE(ill.inQty, 0) as inQty, |
|
|
|
COALESCE(ill.outQty, 0) as outQty, |
|
|
|
COALESCE(ill.holdQty, 0) as holdQty, |
|
|
|
|
|
|
|
CASE |
|
|
|
WHEN (il.expiryDate IS NOT NULL AND il.expiryDate < CURDATE()) THEN 'expired' |
|
|
|
WHEN sol.status = 'rejected' THEN 'rejected' |
|
|
|
WHEN (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) <= 0 THEN 'insufficient_stock' |
|
|
|
WHEN ill.status = 'unavailable' THEN 'status_unavailable' |
|
|
|
ELSE 'available' |
|
|
|
END as lotAvailability, |
|
|
|
|
|
|
|
CASE |
|
|
|
WHEN sol.status = 'completed' THEN 'completed' |
|
|
|
WHEN sol.status = 'rejected' THEN 'rejected' |
|
|
|
WHEN sol.status = 'created' THEN 'pending' |
|
|
|
ELSE 'pending' |
|
|
|
END as processingStatus |
|
|
|
|
|
|
|
FROM fpsmsdb.pick_order po |
|
|
|
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false |
|
|
|
JOIN fpsmsdb.items i ON i.id = pol.itemId |
|
|
|
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId |
|
|
|
// ✅ 查询该 pick order 的所有 lines 和 lots |
|
|
|
val linesSql = """ |
|
|
|
SELECT |
|
|
|
po.id as pickOrderId, |
|
|
|
po.code as pickOrderCode, |
|
|
|
po.consoCode as pickOrderConsoCode, |
|
|
|
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as pickOrderTargetDate, |
|
|
|
po.type as pickOrderType, |
|
|
|
po.status as pickOrderStatus, |
|
|
|
po.assignTo as pickOrderAssignTo, |
|
|
|
|
|
|
|
-- ✅ 关键修改:使用 LEFT JOIN 来包含没有 lot 的 pick order lines |
|
|
|
LEFT JOIN ( |
|
|
|
SELECT spl.pickOrderLineId, spl.suggestedLotLineId AS lotLineId |
|
|
|
FROM fpsmsdb.suggested_pick_lot spl |
|
|
|
UNION |
|
|
|
SELECT sol.pickOrderLineId, sol.inventoryLotLineId |
|
|
|
FROM fpsmsdb.stock_out_line sol |
|
|
|
WHERE sol.deleted = false |
|
|
|
) ll ON ll.pickOrderLineId = pol.id |
|
|
|
pol.id as pickOrderLineId, |
|
|
|
pol.qty as pickOrderLineRequiredQty, |
|
|
|
pol.status as pickOrderLineStatus, |
|
|
|
|
|
|
|
LEFT JOIN fpsmsdb.suggested_pick_lot spl |
|
|
|
ON spl.pickOrderLineId = pol.id AND spl.suggestedLotLineId = ll.lotLineId |
|
|
|
LEFT JOIN fpsmsdb.stock_out_line sol |
|
|
|
ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ll.lotLineId AND sol.deleted = false |
|
|
|
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = ll.lotLineId AND ill.deleted = false |
|
|
|
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false |
|
|
|
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId |
|
|
|
i.id as itemId, |
|
|
|
i.code as itemCode, |
|
|
|
i.name as itemName, |
|
|
|
uc.code as uomCode, |
|
|
|
uc.udfudesc as uomDesc, |
|
|
|
uc.udfShortDesc as uomShortDesc, |
|
|
|
|
|
|
|
WHERE po.id = :pickOrderId |
|
|
|
AND po.deleted = false |
|
|
|
ORDER BY |
|
|
|
COALESCE(w.`order`, 999999) ASC, |
|
|
|
pol.id ASC, |
|
|
|
il.lotNo ASC |
|
|
|
""".trimIndent() |
|
|
|
ill.id as lotId, |
|
|
|
il.lotNo, |
|
|
|
DATE_FORMAT(il.expiryDate, '%Y-%m-%d') as expiryDate, |
|
|
|
w.name as location, |
|
|
|
COALESCE(uc.udfudesc, 'N/A') as stockUnit, |
|
|
|
w.`order` as routerIndex, |
|
|
|
w.code as routerRoute, |
|
|
|
|
|
|
|
CASE |
|
|
|
WHEN sol.status = 'rejected' THEN NULL |
|
|
|
ELSE (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) |
|
|
|
END as availableQty, |
|
|
|
|
|
|
|
COALESCE(spl.qty, 0) as requiredQty, |
|
|
|
COALESCE(sol.qty, 0) as actualPickQty, |
|
|
|
spl.id as suggestedPickLotId, |
|
|
|
ill.status as lotStatus, |
|
|
|
sol.id as stockOutLineId, |
|
|
|
sol.status as stockOutLineStatus, |
|
|
|
COALESCE(sol.qty, 0) as stockOutLineQty, |
|
|
|
COALESCE(ill.inQty, 0) as inQty, |
|
|
|
COALESCE(ill.outQty, 0) as outQty, |
|
|
|
COALESCE(ill.holdQty, 0) as holdQty, |
|
|
|
|
|
|
|
CASE |
|
|
|
WHEN (il.expiryDate IS NOT NULL AND il.expiryDate < CURDATE()) THEN 'expired' |
|
|
|
WHEN sol.status = 'rejected' THEN 'rejected' |
|
|
|
WHEN (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) <= 0 THEN 'insufficient_stock' |
|
|
|
WHEN ill.status = 'unavailable' THEN 'status_unavailable' |
|
|
|
ELSE 'available' |
|
|
|
END as lotAvailability, |
|
|
|
|
|
|
|
CASE |
|
|
|
WHEN sol.status = 'completed' THEN 'completed' |
|
|
|
WHEN sol.status = 'rejected' THEN 'rejected' |
|
|
|
WHEN sol.status = 'created' THEN 'pending' |
|
|
|
ELSE 'pending' |
|
|
|
END as processingStatus |
|
|
|
|
|
|
|
FROM fpsmsdb.pick_order po |
|
|
|
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false |
|
|
|
JOIN fpsmsdb.items i ON i.id = pol.itemId |
|
|
|
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId |
|
|
|
|
|
|
|
val linesResults = jdbcDao.queryForList(linesSql, mapOf("pickOrderId" to pickOrderId)) |
|
|
|
println("🔍 Pick order $pickOrderId has ${linesResults.size} line-lot records") |
|
|
|
LEFT JOIN ( |
|
|
|
SELECT spl.pickOrderLineId, spl.suggestedLotLineId AS lotLineId |
|
|
|
FROM fpsmsdb.suggested_pick_lot spl |
|
|
|
UNION |
|
|
|
SELECT sol.pickOrderLineId, sol.inventoryLotLineId |
|
|
|
FROM fpsmsdb.stock_out_line sol |
|
|
|
WHERE sol.deleted = false |
|
|
|
) ll ON ll.pickOrderLineId = pol.id |
|
|
|
|
|
|
|
// ✅ 按 pickOrderLineId 分组 |
|
|
|
val lineGroups = linesResults.groupBy { (it["pickOrderLineId"] as? Number)?.toLong() } |
|
|
|
LEFT JOIN fpsmsdb.suggested_pick_lot spl |
|
|
|
ON spl.pickOrderLineId = pol.id AND spl.suggestedLotLineId = ll.lotLineId |
|
|
|
LEFT JOIN fpsmsdb.stock_out_line sol |
|
|
|
ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ll.lotLineId AND sol.deleted = false |
|
|
|
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = ll.lotLineId AND ill.deleted = false |
|
|
|
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false |
|
|
|
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId |
|
|
|
|
|
|
|
val pickOrderLines = lineGroups.map { (lineId, lineRows) -> |
|
|
|
val firstLineRow = lineRows.firstOrNull() |
|
|
|
|
|
|
|
if (firstLineRow == null) { |
|
|
|
null |
|
|
|
} else { |
|
|
|
// ✅ 构建 lots 列表(如果没有 lot,返回空数组) |
|
|
|
val lots = if (lineRows.any { it["lotId"] != null }) { |
|
|
|
lineRows.filter { it["lotId"] != null }.map { lotRow -> |
|
|
|
mapOf( |
|
|
|
"id" to lotRow["lotId"], |
|
|
|
"lotNo" to lotRow["lotNo"], |
|
|
|
"expiryDate" to lotRow["expiryDate"], |
|
|
|
"location" to lotRow["location"], |
|
|
|
"stockUnit" to lotRow["stockUnit"], |
|
|
|
"availableQty" to lotRow["availableQty"], |
|
|
|
"requiredQty" to lotRow["requiredQty"], |
|
|
|
"actualPickQty" to lotRow["actualPickQty"], |
|
|
|
"inQty" to lotRow["inQty"], |
|
|
|
"outQty" to lotRow["outQty"], |
|
|
|
"holdQty" to lotRow["holdQty"], |
|
|
|
"lotStatus" to lotRow["lotStatus"], |
|
|
|
"lotAvailability" to lotRow["lotAvailability"], |
|
|
|
"processingStatus" to lotRow["processingStatus"], |
|
|
|
"suggestedPickLotId" to lotRow["suggestedPickLotId"], |
|
|
|
"stockOutLineId" to lotRow["stockOutLineId"], |
|
|
|
"stockOutLineStatus" to lotRow["stockOutLineStatus"], |
|
|
|
"stockOutLineQty" to lotRow["stockOutLineQty"], |
|
|
|
"router" to mapOf( |
|
|
|
"id" to null, |
|
|
|
"index" to lotRow["routerIndex"], |
|
|
|
"route" to lotRow["routerRoute"], |
|
|
|
"area" to lotRow["routerRoute"], |
|
|
|
"itemCode" to lotRow["itemId"], |
|
|
|
"itemName" to lotRow["itemName"], |
|
|
|
"uomId" to lotRow["uomCode"], |
|
|
|
"noofCarton" to lotRow["requiredQty"] |
|
|
|
) |
|
|
|
WHERE po.id = :pickOrderId |
|
|
|
AND po.deleted = false |
|
|
|
ORDER BY |
|
|
|
COALESCE(w.`order`, 999999) ASC, |
|
|
|
pol.id ASC, |
|
|
|
il.lotNo ASC |
|
|
|
""".trimIndent() |
|
|
|
|
|
|
|
val linesResults = jdbcDao.queryForList(linesSql, mapOf("pickOrderId" to pickOrderId)) |
|
|
|
println("🔍 Pick order $pickOrderId has ${linesResults.size} line-lot records") |
|
|
|
|
|
|
|
// ✅ 按 pickOrderLineId 分组 |
|
|
|
val lineGroups = linesResults.groupBy { (it["pickOrderLineId"] as? Number)?.toLong() } |
|
|
|
|
|
|
|
val pickOrderLines = lineGroups.map { (lineId, lineRows) -> |
|
|
|
val firstLineRow = lineRows.firstOrNull() |
|
|
|
|
|
|
|
if (firstLineRow == null) { |
|
|
|
null |
|
|
|
} else { |
|
|
|
// ✅ 构建 lots 列表 |
|
|
|
val lots = if (lineRows.any { it["lotId"] != null }) { |
|
|
|
lineRows.filter { it["lotId"] != null }.map { lotRow -> |
|
|
|
mapOf( |
|
|
|
"id" to lotRow["lotId"], |
|
|
|
"lotNo" to lotRow["lotNo"], |
|
|
|
"expiryDate" to lotRow["expiryDate"], |
|
|
|
"location" to lotRow["location"], |
|
|
|
"stockUnit" to lotRow["stockUnit"], |
|
|
|
"availableQty" to lotRow["availableQty"], |
|
|
|
"requiredQty" to lotRow["requiredQty"], |
|
|
|
"actualPickQty" to lotRow["actualPickQty"], |
|
|
|
"inQty" to lotRow["inQty"], |
|
|
|
"outQty" to lotRow["outQty"], |
|
|
|
"holdQty" to lotRow["holdQty"], |
|
|
|
"lotStatus" to lotRow["lotStatus"], |
|
|
|
"lotAvailability" to lotRow["lotAvailability"], |
|
|
|
"processingStatus" to lotRow["processingStatus"], |
|
|
|
"suggestedPickLotId" to lotRow["suggestedPickLotId"], |
|
|
|
"stockOutLineId" to lotRow["stockOutLineId"], |
|
|
|
"stockOutLineStatus" to lotRow["stockOutLineStatus"], |
|
|
|
"stockOutLineQty" to lotRow["stockOutLineQty"], |
|
|
|
"router" to mapOf( |
|
|
|
"id" to null, |
|
|
|
"index" to lotRow["routerIndex"], |
|
|
|
"route" to lotRow["routerRoute"], |
|
|
|
"area" to lotRow["routerRoute"], |
|
|
|
"itemCode" to lotRow["itemId"], |
|
|
|
"itemName" to lotRow["itemName"], |
|
|
|
"uomId" to lotRow["uomCode"], |
|
|
|
"noofCarton" to lotRow["requiredQty"] |
|
|
|
) |
|
|
|
} |
|
|
|
} else { |
|
|
|
emptyList() // ✅ 返回空数组而不是 null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
mapOf( |
|
|
|
"id" to lineId, |
|
|
|
"requiredQty" to firstLineRow["pickOrderLineRequiredQty"], |
|
|
|
"status" to firstLineRow["pickOrderLineStatus"], |
|
|
|
"item" to mapOf( |
|
|
|
"id" to firstLineRow["itemId"], |
|
|
|
"code" to firstLineRow["itemCode"], |
|
|
|
"name" to firstLineRow["itemName"], |
|
|
|
"uomCode" to firstLineRow["uomCode"], |
|
|
|
"uomDesc" to firstLineRow["uomDesc"], |
|
|
|
"uomShortDesc" to firstLineRow["uomShortDesc"], |
|
|
|
), |
|
|
|
"lots" to lots // ✅ 即使是空数组也返回 |
|
|
|
) |
|
|
|
} else { |
|
|
|
emptyList() |
|
|
|
} |
|
|
|
}.filterNotNull() |
|
|
|
|
|
|
|
mapOf( |
|
|
|
"pickOrderId" to pickOrderId, |
|
|
|
"pickOrderCode" to poInfo["pick_order_code"], |
|
|
|
"doOrderId" to poInfo["do_order_id"], |
|
|
|
"deliveryOrderCode" to poInfo["delivery_order_code"], |
|
|
|
"consoCode" to poInfo["consoCode"], |
|
|
|
"status" to poInfo["status"], |
|
|
|
"targetDate" to poInfo["targetDate"], |
|
|
|
"pickOrderLines" to pickOrderLines |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 构建 FG 信息 |
|
|
|
val fgInfo = mapOf( |
|
|
|
"doPickOrderId" to doPickOrderId, |
|
|
|
"ticketNo" to doPickOrderInfo["ticket_no"], |
|
|
|
"storeId" to doPickOrderInfo["store_id"], |
|
|
|
"shopCode" to doPickOrderInfo["ShopCode"], |
|
|
|
"shopName" to doPickOrderInfo["ShopName"], |
|
|
|
"truckLanceCode" to doPickOrderInfo["TruckLanceCode"], |
|
|
|
"departureTime" to doPickOrderInfo["truck_departure_time"] |
|
|
|
) |
|
|
|
|
|
|
|
mapOf( |
|
|
|
"id" to lineId, |
|
|
|
"requiredQty" to firstLineRow["pickOrderLineRequiredQty"], |
|
|
|
"status" to firstLineRow["pickOrderLineStatus"], |
|
|
|
"item" to mapOf( |
|
|
|
"id" to firstLineRow["itemId"], |
|
|
|
"code" to firstLineRow["itemCode"], |
|
|
|
"name" to firstLineRow["itemName"], |
|
|
|
"uomCode" to firstLineRow["uomCode"], |
|
|
|
"uomDesc" to firstLineRow["uomDesc"], |
|
|
|
"uomShortDesc" to firstLineRow["uomShortDesc"], |
|
|
|
), |
|
|
|
"lots" to lots |
|
|
|
) |
|
|
|
} |
|
|
|
}.filterNotNull() |
|
|
|
|
|
|
|
return mapOf( |
|
|
|
"fgInfo" to fgInfo, |
|
|
|
"pickOrders" to pickOrders |
|
|
|
// 记录该 pick order 的行数 |
|
|
|
lineCountsPerPickOrder.add(pickOrderLines.size) |
|
|
|
allPickOrderLines.addAll(pickOrderLines) |
|
|
|
} |
|
|
|
// 合并到总列表 |
|
|
|
allPickOrderLines.sortWith(compareBy( |
|
|
|
{ line -> |
|
|
|
val lots = line["lots"] as? List<Map<String, Any?>> |
|
|
|
val firstLot = lots?.firstOrNull() |
|
|
|
val router = firstLot?.get("router") as? Map<String, Any?> |
|
|
|
(router?.get("index") as? Number)?.toInt() ?: 999999 |
|
|
|
} |
|
|
|
)) |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 构建 FG 信息 |
|
|
|
val fgInfo = mapOf( |
|
|
|
"doPickOrderId" to doPickOrderId, |
|
|
|
"ticketNo" to doPickOrderInfo["ticket_no"], |
|
|
|
"storeId" to doPickOrderInfo["store_id"], |
|
|
|
"shopCode" to doPickOrderInfo["ShopCode"], |
|
|
|
"shopName" to doPickOrderInfo["ShopName"], |
|
|
|
"truckLanceCode" to doPickOrderInfo["TruckLanceCode"], |
|
|
|
"departureTime" to doPickOrderInfo["truck_departure_time"] |
|
|
|
) |
|
|
|
|
|
|
|
// ✅ 构建合并后的 pick order 对象 |
|
|
|
val mergedPickOrder = if (pickOrdersInfo.isNotEmpty()) { |
|
|
|
val firstPickOrderInfo = pickOrdersInfo.first() |
|
|
|
mapOf( |
|
|
|
"pickOrderIds" to pickOrderIds, |
|
|
|
"pickOrderCodes" to pickOrderCodes, |
|
|
|
"doOrderIds" to doOrderIds, |
|
|
|
"deliveryOrderCodes" to deliveryOrderCodes, |
|
|
|
"lineCountsPerPickOrder" to lineCountsPerPickOrder, |
|
|
|
"consoCode" to firstPickOrderInfo["consoCode"], |
|
|
|
"status" to firstPickOrderInfo["status"], |
|
|
|
"targetDate" to firstPickOrderInfo["targetDate"], |
|
|
|
"pickOrderLines" to allPickOrderLines |
|
|
|
) |
|
|
|
} else { |
|
|
|
null |
|
|
|
} |
|
|
|
|
|
|
|
return mapOf( |
|
|
|
"fgInfo" to fgInfo, |
|
|
|
"pickOrders" to listOfNotNull(mergedPickOrder) |
|
|
|
) |
|
|
|
} |
|
|
|
// Fix the type issues in the getPickOrdersByDateAndStore method |
|
|
|
open fun getPickOrdersByDateAndStore(storeId: String): Map<String, Any?> { |
|
|
|
println("=== Debug: getPickOrdersByDateAndStore ===") |
|
|
|
|