|
|
|
@@ -65,6 +65,10 @@ import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRecordReposito |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRecord |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLine |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus |
|
|
|
|
|
|
|
import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository // ✅ 添加这行 |
|
|
|
import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo // ✅ 添加这行 |
|
|
|
import com.ffii.fpsms.modules.stock.entity.SuggestedPickLot |
|
|
|
@Service |
|
|
|
open class PickOrderService( |
|
|
|
private val jdbcDao: JdbcDao, |
|
|
|
@@ -76,6 +80,7 @@ open class PickOrderService( |
|
|
|
private val inventoryLotLineRepository: InventoryLotLineRepository, |
|
|
|
private val pickOrderGroupRepository: PickOrderGroupRepository, |
|
|
|
private val inventoryLotLineService: InventoryLotLineService, |
|
|
|
private val suggestPickLotRepository: SuggestPickLotRepository, |
|
|
|
private val inventoryService: InventoryService, |
|
|
|
private val stockOutRepository: StockOutRepository, |
|
|
|
private val itemsRepository: ItemsRepository, |
|
|
|
@@ -3549,8 +3554,389 @@ ORDER BY |
|
|
|
return enrichedResults |
|
|
|
} |
|
|
|
// ... existing code ... |
|
|
|
|
|
|
|
open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, Any?> { |
|
|
|
open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, Any?> { |
|
|
|
println("=== Debug: getAllPickOrderLotsWithDetailsHierarchical (Repository-based) ===") |
|
|
|
println("userId filter: $userId") |
|
|
|
|
|
|
|
val user = userService.find(userId).orElse(null) |
|
|
|
if (user == null) { |
|
|
|
println("❌ User not found: $userId") |
|
|
|
return emptyMap() |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ Step 1: 先找到分配给该用户的所有 pick orders |
|
|
|
val userPickOrders = pickOrderRepository.findAll() |
|
|
|
.filter { |
|
|
|
it.deleted == false && |
|
|
|
it.assignTo?.id == userId && |
|
|
|
it.type?.value == "do" && |
|
|
|
(it.status == PickOrderStatus.RELEASED || it.status == PickOrderStatus.PENDING) |
|
|
|
} |
|
|
|
|
|
|
|
if (userPickOrders.isEmpty()) { |
|
|
|
println("❌ No pick orders found for user $userId") |
|
|
|
return mapOf( |
|
|
|
"fgInfo" to null, |
|
|
|
"pickOrders" to emptyList<Any>() |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
println("🔍 Found ${userPickOrders.size} pick orders assigned to user $userId") |
|
|
|
val pickOrderIds = userPickOrders.mapNotNull { it.id } |
|
|
|
|
|
|
|
// ✅ Step 2: 通过 do_pick_order_line 找到相关的 do_pick_order |
|
|
|
val doPickOrderLineRecords = pickOrderIds.flatMap { poId -> |
|
|
|
doPickOrderLineRepository.findByPickOrderIdAndDeletedFalse(poId) |
|
|
|
} |
|
|
|
|
|
|
|
val doPickOrderIds = doPickOrderLineRecords.mapNotNull { it.doPickOrderId }.distinct() |
|
|
|
|
|
|
|
if (doPickOrderIds.isEmpty()) { |
|
|
|
println("❌ No do_pick_order found for pick orders: $pickOrderIds") |
|
|
|
return mapOf( |
|
|
|
"fgInfo" to null, |
|
|
|
"pickOrders" to emptyList<Any>() |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
println("🔍 Found ${doPickOrderIds.size} do_pick_order records") |
|
|
|
|
|
|
|
// ✅ Step 3: 获取第一个 do_pick_order 的详细信息(用于构建 fgInfo) |
|
|
|
val doPickOrder = doPickOrderRepository.findById(doPickOrderIds.first()).orElse(null) |
|
|
|
if (doPickOrder == null) { |
|
|
|
println("❌ do_pick_order not found: ${doPickOrderIds.first()}") |
|
|
|
return mapOf( |
|
|
|
"fgInfo" to null, |
|
|
|
"pickOrders" to emptyList<Any>() |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
val doPickOrderId = doPickOrder.id!! |
|
|
|
println("🔍 Using do_pick_order ID: $doPickOrderId") |
|
|
|
|
|
|
|
// ✅ Step 4: 使用 Repository 加载 pick orders 及其关联数据 |
|
|
|
val pickOrders = pickOrderRepository.findAllById(pickOrderIds) |
|
|
|
.filter { it.deleted == false } |
|
|
|
|
|
|
|
println("🔍 Loaded ${pickOrders.size} pick orders") |
|
|
|
|
|
|
|
// ✅ 收集所有需要的数据 |
|
|
|
// ✅ 收集所有需要的数据 |
|
|
|
val allPickOrderLineIds = pickOrders.flatMap { it.pickOrderLines }.mapNotNull { it.id } |
|
|
|
|
|
|
|
// ✅ 使用 Repository 批量加载所有 suggestions 和 stock out lines |
|
|
|
// ✅ 使用 Repository 批量加载所有 suggestions 和 stock out lines |
|
|
|
val allSuggestions = suggestPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIds) // ✅ 修复:使用 suggestPickLotRepository |
|
|
|
val allStockOutLines = allPickOrderLineIds.flatMap { lineId -> |
|
|
|
stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(lineId) |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 按 pickOrderLineId 分组 |
|
|
|
val suggestionsByLineId = allSuggestions.groupBy { spl: SuggestedPickLot -> // ✅ 修复:明确类型 |
|
|
|
spl.pickOrderLine?.id |
|
|
|
} |
|
|
|
val stockOutLinesByLineId = allStockOutLines.groupBy { sol: StockOutLineInfo -> // ✅ 修复:明确类型 |
|
|
|
sol.pickOrderLineId |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 构建层级数据结构 |
|
|
|
val allPickOrderLines = mutableListOf<Map<String, Any?>>() |
|
|
|
val lineCountsPerPickOrder = mutableListOf<Int>() |
|
|
|
val pickOrderIdsList = mutableListOf<Long>() |
|
|
|
val pickOrderCodesList = mutableListOf<String>() |
|
|
|
val doOrderIdsList = mutableListOf<Long>() |
|
|
|
val deliveryOrderCodesList = mutableListOf<String>() |
|
|
|
|
|
|
|
pickOrders.forEach { po -> |
|
|
|
pickOrderIdsList.add(po.id!!) |
|
|
|
pickOrderCodesList.add(po.code ?: "") |
|
|
|
|
|
|
|
val doOrderId = po.deliveryOrder?.id |
|
|
|
if (doOrderId != null) doOrderIdsList.add(doOrderId) |
|
|
|
deliveryOrderCodesList.add(po.deliveryOrder?.code ?: "") |
|
|
|
|
|
|
|
val lines = po.pickOrderLines.filter { !it.deleted } |
|
|
|
|
|
|
|
val lineDtos = lines.map { pol -> |
|
|
|
val lineId = pol.id!! |
|
|
|
val item = pol.item |
|
|
|
val uom = pol.uom |
|
|
|
val zero = BigDecimal.ZERO |
|
|
|
val today = LocalDate.now() |
|
|
|
|
|
|
|
// ✅ 使用 Repository 获取该 line 的 suggestions 和 stock out lines |
|
|
|
val suggestions = suggestionsByLineId[lineId] ?: emptyList<SuggestedPickLot>() // ✅ 修复:明确类型 |
|
|
|
val stockOutLines = stockOutLinesByLineId[lineId] ?: emptyList<StockOutLineInfo>() // ✅ 修复:明确类型 |
|
|
|
// ✅ 合并 suggestions 和 stock out lines,按 (lotId, stockOutLineId) 去重 |
|
|
|
// ✅ 合并 suggestions 和 stock out lines,按 lotId 去重(合并相同 lot 的多个条目) |
|
|
|
val lotMap = mutableMapOf<Long?, MutableMap<String, Any?>>() |
|
|
|
|
|
|
|
// ✅ 第一步:处理 suggestions,构建基础数据 |
|
|
|
suggestions.forEach { spl: SuggestedPickLot -> |
|
|
|
val ill = spl.suggestedLotLine |
|
|
|
if (ill != null) { |
|
|
|
val lotId = ill.id |
|
|
|
val il = ill.inventoryLot |
|
|
|
val w = ill.warehouse |
|
|
|
val isExpired = il?.expiryDate?.let { exp: LocalDate -> exp.isBefore(today) } == true |
|
|
|
val availableQty = (ill.inQty ?: zero) |
|
|
|
.minus(ill.outQty ?: zero) |
|
|
|
.minus(ill.holdQty ?: zero) |
|
|
|
|
|
|
|
// ✅ 查找对应的 stock out line(通过 lotId 查找,而不是通过 suggestion.stockOutLine) |
|
|
|
val stockOutLine = stockOutLines.find { sol: StockOutLineInfo -> |
|
|
|
sol.inventoryLotLineId == lotId |
|
|
|
} |
|
|
|
|
|
|
|
if (lotMap.containsKey(lotId)) { |
|
|
|
// ✅ 如果已存在,合并 requiredQty(累加) |
|
|
|
val existing = lotMap[lotId]!! |
|
|
|
val existingRequiredQty = (existing["requiredQty"] as? BigDecimal) ?: zero |
|
|
|
val newRequiredQty = spl.qty ?: zero |
|
|
|
existing["requiredQty"] = existingRequiredQty.plus(newRequiredQty) |
|
|
|
|
|
|
|
// ✅ 如果当前有 stockOutLine 但 existing 没有,更新 stockOutLine 信息 |
|
|
|
if (stockOutLine != null && existing["stockOutLineId"] == null) { |
|
|
|
existing["stockOutLineId"] = stockOutLine.id |
|
|
|
existing["stockOutLineStatus"] = stockOutLine.status |
|
|
|
existing["stockOutLineQty"] = numToBigDecimal(stockOutLine.qty as? Number) |
|
|
|
existing["actualPickQty"] = numToBigDecimal(stockOutLine.qty as? Number) |
|
|
|
existing["processingStatus"] = when { |
|
|
|
stockOutLine.status == "completed" -> "completed" |
|
|
|
stockOutLine.status == "rejected" -> "rejected" |
|
|
|
else -> "pending" |
|
|
|
} |
|
|
|
existing["lotAvailability"] = when { |
|
|
|
isExpired -> "expired" |
|
|
|
stockOutLine.status == "rejected" -> "rejected" |
|
|
|
availableQty <= zero -> "insufficient_stock" |
|
|
|
ill.status?.value == "unavailable" -> "status_unavailable" |
|
|
|
else -> "available" |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// ✅ 首次遇到,创建新条目 |
|
|
|
lotMap[lotId] = mutableMapOf( |
|
|
|
"id" to lotId, |
|
|
|
"lotNo" to il?.lotNo, |
|
|
|
"expiryDate" to il?.expiryDate?.toString(), |
|
|
|
"location" to w?.name, |
|
|
|
"stockUnit" to (ill.stockUom?.uom?.udfudesc ?: uom?.udfudesc ?: "N/A"), |
|
|
|
"availableQty" to availableQty, |
|
|
|
"requiredQty" to (spl.qty ?: zero), |
|
|
|
"actualPickQty" to numToBigDecimal(stockOutLine?.qty as? Number), |
|
|
|
"inQty" to ill.inQty, |
|
|
|
"outQty" to ill.outQty, |
|
|
|
"holdQty" to ill.holdQty, |
|
|
|
"lotStatus" to ill.status?.value, |
|
|
|
"lotAvailability" to when { |
|
|
|
isExpired -> "expired" |
|
|
|
stockOutLine?.status == "rejected" -> "rejected" |
|
|
|
availableQty <= zero -> "insufficient_stock" |
|
|
|
ill.status?.value == "unavailable" -> "status_unavailable" |
|
|
|
else -> "available" |
|
|
|
}, |
|
|
|
"processingStatus" to when { |
|
|
|
stockOutLine?.status == "completed" -> "completed" |
|
|
|
stockOutLine?.status == "rejected" -> "rejected" |
|
|
|
else -> "pending" |
|
|
|
}, |
|
|
|
"suggestedPickLotId" to spl.id, |
|
|
|
"stockOutLineId" to stockOutLine?.id, |
|
|
|
"stockOutLineStatus" to stockOutLine?.status, |
|
|
|
"stockOutLineQty" to numToBigDecimal(stockOutLine?.qty as? Number), |
|
|
|
"router" to mapOf( |
|
|
|
"id" to null, |
|
|
|
"index" to w?.order, |
|
|
|
"route" to w?.code, |
|
|
|
"area" to w?.code, |
|
|
|
"itemCode" to item?.id, |
|
|
|
"itemName" to item?.name, |
|
|
|
"uomId" to uom?.code, |
|
|
|
"noofCarton" to (spl.qty ?: zero) |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 第二步:处理 stock out lines(包括没有 suggestion 的,或更新已存在的) |
|
|
|
stockOutLines.forEach { sol: StockOutLineInfo -> |
|
|
|
val inventoryLotLineId = sol.inventoryLotLineId |
|
|
|
val stockOutLineId = sol.id |
|
|
|
|
|
|
|
if (inventoryLotLineId != null) { |
|
|
|
// ✅ 有 lot 的 stock out line |
|
|
|
if (lotMap.containsKey(inventoryLotLineId)) { |
|
|
|
// ✅ 如果 lot 已存在,更新 stockOutLine 信息(如果还没有) |
|
|
|
val existing = lotMap[inventoryLotLineId]!! |
|
|
|
if (existing["stockOutLineId"] == null) { |
|
|
|
existing["stockOutLineId"] = stockOutLineId |
|
|
|
existing["stockOutLineStatus"] = sol.status |
|
|
|
existing["stockOutLineQty"] = numToBigDecimal(sol.qty as? Number) |
|
|
|
existing["actualPickQty"] = numToBigDecimal(sol.qty as? Number) |
|
|
|
existing["processingStatus"] = when { |
|
|
|
sol.status == "completed" -> "completed" |
|
|
|
sol.status == "rejected" -> "rejected" |
|
|
|
else -> "pending" |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 更新 lotAvailability(如果 stockOutLine 是 rejected) |
|
|
|
if (sol.status == "rejected") { |
|
|
|
existing["lotAvailability"] = "rejected" |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// ✅ lot 不存在,创建新条目(没有 suggestion 的情况) |
|
|
|
val ill = inventoryLotLineRepository.findById(inventoryLotLineId).orElse(null) |
|
|
|
if (ill != null) { |
|
|
|
val il = ill.inventoryLot |
|
|
|
val w = ill.warehouse |
|
|
|
val isExpired = il?.expiryDate?.let { exp: LocalDate -> exp.isBefore(today) } == true |
|
|
|
val availableQty = (ill.inQty ?: zero) |
|
|
|
.minus(ill.outQty ?: zero) |
|
|
|
.minus(ill.holdQty ?: zero) |
|
|
|
|
|
|
|
// ✅ 查找对应的 suggestion(如果有) |
|
|
|
val suggestion = suggestions.find { spl: SuggestedPickLot -> |
|
|
|
spl.suggestedLotLine?.id == inventoryLotLineId |
|
|
|
} |
|
|
|
|
|
|
|
lotMap[inventoryLotLineId] = mutableMapOf( |
|
|
|
"id" to inventoryLotLineId, |
|
|
|
"lotNo" to il?.lotNo, |
|
|
|
"expiryDate" to il?.expiryDate?.toString(), |
|
|
|
"location" to w?.name, |
|
|
|
"stockUnit" to (ill.stockUom?.uom?.udfudesc ?: uom?.udfudesc ?: "N/A"), |
|
|
|
"availableQty" to availableQty, |
|
|
|
"requiredQty" to (suggestion?.qty ?: zero), |
|
|
|
"actualPickQty" to numToBigDecimal(sol.qty as? Number), |
|
|
|
"inQty" to ill.inQty, |
|
|
|
"outQty" to ill.outQty, |
|
|
|
"holdQty" to ill.holdQty, |
|
|
|
"lotStatus" to ill.status?.value, |
|
|
|
"lotAvailability" to when { |
|
|
|
isExpired -> "expired" |
|
|
|
sol.status == "rejected" -> "rejected" |
|
|
|
availableQty <= zero -> "insufficient_stock" |
|
|
|
ill.status?.value == "unavailable" -> "status_unavailable" |
|
|
|
else -> "available" |
|
|
|
}, |
|
|
|
"processingStatus" to when { |
|
|
|
sol.status == "completed" -> "completed" |
|
|
|
sol.status == "rejected" -> "rejected" |
|
|
|
else -> "pending" |
|
|
|
}, |
|
|
|
"suggestedPickLotId" to suggestion?.id, |
|
|
|
"stockOutLineId" to stockOutLineId, |
|
|
|
"stockOutLineStatus" to sol.status, |
|
|
|
"stockOutLineQty" to numToBigDecimal(sol.qty as? Number), |
|
|
|
"router" to mapOf( |
|
|
|
"id" to null, |
|
|
|
"index" to w?.order, |
|
|
|
"route" to w?.code, |
|
|
|
"area" to w?.code, |
|
|
|
"itemCode" to item?.id, |
|
|
|
"itemName" to item?.name, |
|
|
|
"uomId" to uom?.code, |
|
|
|
"noofCarton" to (suggestion?.qty ?: zero) |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
val lots = lotMap.values.toList() |
|
|
|
|
|
|
|
// ✅ 处理没有 lot 的 stock out lines(noLot = true) |
|
|
|
val stockouts = stockOutLines |
|
|
|
.filter { sol: StockOutLineInfo -> sol.inventoryLotLineId == null } |
|
|
|
.map { sol: StockOutLineInfo -> |
|
|
|
mapOf( |
|
|
|
"id" to sol.id, |
|
|
|
"status" to sol.status, |
|
|
|
"qty" to numToBigDecimal(sol.qty as? Number), |
|
|
|
"lotId" to null, |
|
|
|
"lotNo" to "", |
|
|
|
"location" to "", |
|
|
|
"availableQty" to null, |
|
|
|
"noLot" to true |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
mapOf( |
|
|
|
"id" to lineId, |
|
|
|
"requiredQty" to pol.qty, |
|
|
|
"status" to pol.status?.value, |
|
|
|
"item" to mapOf( |
|
|
|
"id" to item?.id, |
|
|
|
"code" to item?.code, |
|
|
|
"name" to item?.name, |
|
|
|
"uomCode" to uom?.code, |
|
|
|
"uomDesc" to uom?.udfudesc, |
|
|
|
"uomShortDesc" to uom?.udfShortDesc |
|
|
|
), |
|
|
|
"lots" to lots, |
|
|
|
"stockouts" to stockouts |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
lineCountsPerPickOrder.add(lineDtos.size) |
|
|
|
allPickOrderLines.addAll(lineDtos) |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 按 router index 排序 |
|
|
|
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?> |
|
|
|
val indexValue = router?.get("index") |
|
|
|
// ✅ 修复:支持字符串和数字两种格式 |
|
|
|
when (indexValue) { |
|
|
|
is Number -> indexValue.toInt() |
|
|
|
is String -> indexValue.toIntOrNull() ?: 999999 |
|
|
|
else -> 999999 |
|
|
|
} |
|
|
|
} |
|
|
|
)) |
|
|
|
|
|
|
|
// ✅ 构建 FG 信息 |
|
|
|
val fgInfo = mapOf( |
|
|
|
"doPickOrderId" to doPickOrderId, |
|
|
|
"ticketNo" to doPickOrder.ticketNo, |
|
|
|
"storeId" to doPickOrder.storeId, |
|
|
|
"shopCode" to doPickOrder.shopCode, |
|
|
|
"shopName" to doPickOrder.shopName, |
|
|
|
"truckLanceCode" to doPickOrder.truckLanceCode, |
|
|
|
"departureTime" to doPickOrder.truckDepartureTime?.toString() |
|
|
|
) |
|
|
|
|
|
|
|
// ✅ 构建合并后的 pick order 对象 |
|
|
|
val mergedPickOrder = if (pickOrders.isNotEmpty()) { |
|
|
|
val firstPickOrder = pickOrders.first() |
|
|
|
mapOf( |
|
|
|
"pickOrderIds" to pickOrderIdsList, |
|
|
|
"pickOrderCodes" to pickOrderCodesList, |
|
|
|
"doOrderIds" to doOrderIdsList, |
|
|
|
"deliveryOrderCodes" to deliveryOrderCodesList, |
|
|
|
"lineCountsPerPickOrder" to lineCountsPerPickOrder, |
|
|
|
"consoCodes" to pickOrders.mapNotNull { it.consoCode }.distinct(), |
|
|
|
"status" to doPickOrder.ticketStatus?.value, |
|
|
|
"targetDate" to firstPickOrder.targetDate?.toLocalDate()?.toString(), |
|
|
|
"pickOrderLines" to allPickOrderLines |
|
|
|
) |
|
|
|
} else { |
|
|
|
null |
|
|
|
} |
|
|
|
|
|
|
|
return mapOf( |
|
|
|
"fgInfo" to fgInfo, |
|
|
|
"pickOrders" to listOfNotNull(mergedPickOrder) |
|
|
|
) |
|
|
|
} |
|
|
|
open fun getAllPickOrderLotsWithDetailsHierarchicalold(userId: Long): Map<String, Any?> { |
|
|
|
println("=== Debug: getAllPickOrderLotsWithDetailsHierarchical (NEW STRUCTURE) ===") |
|
|
|
println("userId filter: $userId") |
|
|
|
|
|
|
|
|