|
|
@@ -2274,90 +2274,138 @@ open fun deleteJoPickOrderJobOrderProductProcessPickOrder(jobOrderId: Long): Mes |
|
|
errorPosition = null |
|
|
errorPosition = null |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
open fun getMaterialPickStatus(): List<MaterialPickStatusItem> { |
|
|
|
|
|
|
|
|
open fun getMaterialPickStatus(date: String?): List<MaterialPickStatusItem> { |
|
|
|
|
|
// Parse date filter if provided |
|
|
|
|
|
val filterDate = date?.let { |
|
|
|
|
|
try { |
|
|
|
|
|
LocalDate.parse(it) |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
null |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get all joPickOrders |
|
|
val joPickOrders = joPickOrderRepository.findAll() |
|
|
val joPickOrders = joPickOrderRepository.findAll() |
|
|
|
|
|
|
|
|
return joPickOrders |
|
|
|
|
|
|
|
|
// Filter by date if provided (filter by jobOrder.planStart date) |
|
|
|
|
|
val filteredJoPickOrders = if (filterDate != null) { |
|
|
|
|
|
joPickOrders.filter { joPickOrder -> |
|
|
|
|
|
val jobOrder = joPickOrder.jobOrderId?.let { |
|
|
|
|
|
jobOrderRepository.findById(it).orElse(null) |
|
|
|
|
|
} |
|
|
|
|
|
jobOrder?.planStart?.toLocalDate() == filterDate |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
joPickOrders |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Group by jobOrderId |
|
|
|
|
|
val groupedByJobOrder = filteredJoPickOrders |
|
|
.filter { joPickOrder -> |
|
|
.filter { joPickOrder -> |
|
|
val jobOrder = joPickOrder.jobOrderId?.let { |
|
|
val jobOrder = joPickOrder.jobOrderId?.let { |
|
|
jobOrderRepository.findById(it).orElse(null) |
|
|
jobOrderRepository.findById(it).orElse(null) |
|
|
} |
|
|
} |
|
|
jobOrder?.status != JobOrderStatus.COMPLETED |
|
|
jobOrder?.status != JobOrderStatus.COMPLETED |
|
|
} |
|
|
} |
|
|
.map { joPickOrder -> |
|
|
|
|
|
// Get related entities for additional data |
|
|
|
|
|
val jobOrder = joPickOrder.jobOrderId?.let { |
|
|
|
|
|
jobOrderRepository.findById(it).orElse(null) |
|
|
|
|
|
} |
|
|
|
|
|
val item = joPickOrder.itemId?.let { |
|
|
|
|
|
itemsRepository.findById(it).orElse(null) |
|
|
|
|
|
} |
|
|
|
|
|
val pickOrder = joPickOrder.pickOrderId?.let { |
|
|
|
|
|
pickOrderRepository.findById(it).orElse(null) |
|
|
|
|
|
|
|
|
.groupBy { it.jobOrderId } |
|
|
|
|
|
|
|
|
|
|
|
// Map each job order group to a single MaterialPickStatusItem |
|
|
|
|
|
return groupedByJobOrder.mapNotNull { (jobOrderId, joPickOrdersForJob) -> |
|
|
|
|
|
if (jobOrderId == null) return@mapNotNull null |
|
|
|
|
|
|
|
|
|
|
|
val jobOrder = jobOrderRepository.findById(jobOrderId).orElse(null) |
|
|
|
|
|
?: return@mapNotNull null |
|
|
|
|
|
|
|
|
|
|
|
// Get BOM item (finished good/semi-finished product), not BOM Material item |
|
|
|
|
|
val bomItem = jobOrder.bom?.item |
|
|
|
|
|
if (bomItem == null) return@mapNotNull null |
|
|
|
|
|
|
|
|
|
|
|
// Get all pick orders for this job order |
|
|
|
|
|
val pickOrderIds = joPickOrdersForJob.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
|
|
|
|
|
|
|
|
// Aggregate data from all pick orders for this job order |
|
|
|
|
|
val allPickOrderLines = pickOrderIds.flatMap { poId -> |
|
|
|
|
|
pickOrderLineRepository.findByPickOrderId(poId) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Get stock out lines for this pick order and item to get start/end times |
|
|
|
|
|
// First get all pick order lines for this pick order |
|
|
|
|
|
val pickOrderLines = pickOrder?.let { po -> |
|
|
|
|
|
pickOrderLineRepository.findByPickOrderId(po.id!!) |
|
|
|
|
|
} ?: emptyList() |
|
|
|
|
|
|
|
|
|
|
|
// Then get stock out lines for each pick order line that matches the item |
|
|
|
|
|
val stockOutLines = pickOrderLines |
|
|
|
|
|
.filter { it.item?.id == joPickOrder.itemId } |
|
|
|
|
|
.flatMap { pol -> |
|
|
|
|
|
stockOutLineRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!) |
|
|
|
|
|
.mapNotNull { solInfo -> |
|
|
|
|
|
// Convert StockOutLineInfo to StockOutLine entity to access startTime/endTime |
|
|
|
|
|
// We need to fetch the actual entity |
|
|
|
|
|
stockOutLineRepository.findById(solInfo.id).orElse(null) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Get all stock out lines for all pick orders of this job order |
|
|
|
|
|
val allStockOutLines = allPickOrderLines.flatMap { pol -> |
|
|
|
|
|
stockOutLineRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!) |
|
|
|
|
|
.mapNotNull { solInfo -> |
|
|
|
|
|
stockOutLineRepository.findById(solInfo.id).orElse(null) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Get earliest startTime and latest endTime from stock out lines |
|
|
|
|
|
val pickStartTime = stockOutLines |
|
|
|
|
|
|
|
|
// ✅ 修复:startTime = 第一个 item 开始提料的时间 |
|
|
|
|
|
// 只考虑已经开始的 items(status 不是 pending),取最早的 startTime |
|
|
|
|
|
val pickStartTime = allStockOutLines |
|
|
|
|
|
.filter { it.status != null && it.status != "pending" } |
|
|
.mapNotNull { it.startTime } |
|
|
.mapNotNull { it.startTime } |
|
|
.minOrNull() |
|
|
.minOrNull() |
|
|
|
|
|
|
|
|
val pickEndTime = stockOutLines |
|
|
|
|
|
.mapNotNull { it.endTime } |
|
|
|
|
|
.maxOrNull() |
|
|
|
|
|
|
|
|
// Count total items to pick (number of distinct pick order lines) |
|
|
|
|
|
val numberOfItemsToPick = allPickOrderLines.size |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修复:检查所有 items 是否都已完成 |
|
|
|
|
|
// 计算已完成的 items 数量(每个 pickOrderLine 至少有一个 completed 的 stockOutLine) |
|
|
|
|
|
val completedItemsCount = allPickOrderLines.count { pol -> |
|
|
|
|
|
val stockOutLinesForPol = allStockOutLines.filter { |
|
|
|
|
|
it.pickOrderLine?.id == pol.id |
|
|
|
|
|
} |
|
|
|
|
|
// 如果这个 pickOrderLine 有至少一个 completed 的 stockOutLine,则认为已完成 |
|
|
|
|
|
stockOutLinesForPol.any { it.status == "completed" } |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Count items to pick from pick order lines |
|
|
|
|
|
val numberOfItemsToPick = pickOrder?.let { po -> |
|
|
|
|
|
pickOrderLineRepository.findByPickOrderId(po.id!!).size |
|
|
|
|
|
} ?: 0 |
|
|
|
|
|
|
|
|
// ✅ 修复:endTime = 最后一个 item 完成提料的时间 |
|
|
|
|
|
// 只有当所有 items 都完成时,才返回 endTime |
|
|
|
|
|
val pickEndTime = if (completedItemsCount == numberOfItemsToPick && numberOfItemsToPick > 0) { |
|
|
|
|
|
// 所有 items 都已完成,取最晚的 endTime |
|
|
|
|
|
allStockOutLines |
|
|
|
|
|
.filter { it.status == "completed" } |
|
|
|
|
|
.mapNotNull { it.endTime } |
|
|
|
|
|
.maxOrNull() |
|
|
|
|
|
} else { |
|
|
|
|
|
// 还有 items 未完成,返回 null |
|
|
|
|
|
null |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Count items with issues from pick execution issues |
|
|
|
|
|
val numberOfItemsWithIssue = joPickOrder.id?.let { joPickOrderId -> |
|
|
|
|
|
pickExecutionIssueRepository.findByPickOrderIdAndDeletedFalse(joPickOrder.pickOrderId ?: 0L) |
|
|
|
|
|
.filter { it.joPickOrderId == joPickOrderId } |
|
|
|
|
|
.size |
|
|
|
|
|
} ?: 0 |
|
|
|
|
|
|
|
|
// Count total items to pick (number of distinct pick order lines) |
|
|
|
|
|
//val numberOfItemsToPick = allPickOrderLines.size |
|
|
|
|
|
|
|
|
// Get job order quantity and UOM - access via bom |
|
|
|
|
|
val jobOrderQty = jobOrder?.reqQty?.toDouble() |
|
|
|
|
|
val uom = jobOrder?.bom?.uom?.code |
|
|
|
|
|
|
|
|
// Count total items with issues from all pick orders |
|
|
|
|
|
val numberOfItemsWithIssue = pickOrderIds.sumOf { poId -> |
|
|
|
|
|
pickExecutionIssueRepository.findByPickOrderIdAndDeletedFalse(poId).size |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Get item name |
|
|
|
|
|
val itemName = item?.name |
|
|
|
|
|
|
|
|
// Get job order quantity and UOM |
|
|
|
|
|
val jobOrderQty = jobOrder.reqQty?.toDouble() |
|
|
|
|
|
val uom = jobOrder.bom?.uom?.code |
|
|
|
|
|
|
|
|
// Determine pick status - prioritize pickOrder.status over matchStatus |
|
|
|
|
|
|
|
|
// Determine pick status - check if all pick orders are completed |
|
|
|
|
|
val pickOrders = pickOrderIds.mapNotNull { poId -> |
|
|
|
|
|
pickOrderRepository.findById(poId).orElse(null) |
|
|
|
|
|
} |
|
|
val pickStatus = when { |
|
|
val pickStatus = when { |
|
|
pickOrder?.status != null -> pickOrder.status!!.value |
|
|
|
|
|
joPickOrder.matchStatus != null -> joPickOrder.matchStatus!!.value |
|
|
|
|
|
else -> null |
|
|
|
|
|
|
|
|
pickOrders.isEmpty() -> null |
|
|
|
|
|
pickOrders.all { it.status == com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.COMPLETED } -> "completed" |
|
|
|
|
|
pickOrders.any { it.status == com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED } -> "released" |
|
|
|
|
|
else -> "pending" |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Use the first pick order code (or combine if multiple) |
|
|
|
|
|
val pickOrderCode = pickOrderIds.firstOrNull()?.let { poId -> |
|
|
|
|
|
pickOrderRepository.findById(poId).orElse(null)?.code |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
MaterialPickStatusItem( |
|
|
MaterialPickStatusItem( |
|
|
id = joPickOrder.id ?: 0L, |
|
|
|
|
|
pickOrderId = joPickOrder.pickOrderId, |
|
|
|
|
|
pickOrderCode = joPickOrder.pickOrderCode, |
|
|
|
|
|
jobOrderId = joPickOrder.jobOrderId, |
|
|
|
|
|
jobOrderCode = joPickOrder.jobOrderCode, |
|
|
|
|
|
itemId = joPickOrder.itemId, |
|
|
|
|
|
itemCode = joPickOrder.itemCode, |
|
|
|
|
|
itemName = itemName, |
|
|
|
|
|
|
|
|
id = jobOrderId, // Use jobOrderId as id |
|
|
|
|
|
pickOrderId = pickOrderIds.firstOrNull(), // First pick order ID |
|
|
|
|
|
pickOrderCode = pickOrderCode, |
|
|
|
|
|
jobOrderId = jobOrderId, |
|
|
|
|
|
jobOrderCode = jobOrder.code, |
|
|
|
|
|
itemId = bomItem.id, // BOM item ID (finished good/semi-finished) |
|
|
|
|
|
itemCode = bomItem.code, // BOM item code |
|
|
|
|
|
itemName = bomItem.name, // BOM item name (finished good/semi-finished) |
|
|
jobOrderQty = jobOrderQty, |
|
|
jobOrderQty = jobOrderQty, |
|
|
uom = uom, |
|
|
uom = uom, |
|
|
pickStartTime = pickStartTime, |
|
|
pickStartTime = pickStartTime, |
|
|
|