| @@ -3340,125 +3340,142 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A | |||||
| val pickOrderIdsStr = pickOrderIds.joinToString(",") | val pickOrderIdsStr = pickOrderIds.joinToString(",") | ||||
| val sql = """ | val sql = """ | ||||
| SELECT | |||||
| -- Pick Order Information | |||||
| 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, | |||||
| -- Pick Order Line Information | |||||
| pol.id as pickOrderLineId, | |||||
| pol.qty as pickOrderLineRequiredQty, | |||||
| pol.status as pickOrderLineStatus, | |||||
| -- Item Information | |||||
| i.id as itemId, | |||||
| i.code as itemCode, | |||||
| i.name as itemName, | |||||
| uc.code as uomCode, | |||||
| uc.udfudesc as uomDesc, | |||||
| uc.udfShortDesc as uomShortDesc, | |||||
| -- Lot Information | |||||
| 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, | |||||
| -- Router Information | |||||
| r.id as routerId, | |||||
| r.index as routerIndex, | |||||
| r.route as routerRoute, | |||||
| r.area as routerArea, | |||||
| -- ✅ FIXED: Set quantities to NULL for rejected lots | |||||
| CASE | |||||
| WHEN sol.status = 'rejected' THEN NULL | |||||
| ELSE (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) | |||||
| END as availableQty, | |||||
| -- Required quantity for this lot | |||||
| COALESCE(spl.qty, 0) as requiredQty, | |||||
| -- Actual picked quantity | |||||
| COALESCE(sol.qty, 0) as actualPickQty, | |||||
| -- Suggested pick lot information | |||||
| spl.id as suggestedPickLotId, | |||||
| ill.status as lotStatus, | |||||
| -- Stock out line information | |||||
| sol.id as stockOutLineId, | |||||
| sol.status as stockOutLineStatus, | |||||
| COALESCE(sol.qty, 0) as stockOutLineQty, | |||||
| -- Additional detailed fields | |||||
| COALESCE(ill.inQty, 0) as inQty, | |||||
| COALESCE(ill.outQty, 0) as outQty, | |||||
| COALESCE(ill.holdQty, 0) as holdQty, | |||||
| COALESCE(spl.suggestedLotLineId, ill.id) as debugSuggestedLotLineId, | |||||
| ill.inventoryLotId as debugInventoryLotId, | |||||
| -- Calculate total picked quantity by ALL pick orders for this lot | |||||
| COALESCE(( | |||||
| SELECT SUM(sol_all.qty) | |||||
| FROM fpsmsdb.stock_out_line sol_all | |||||
| WHERE sol_all.inventoryLotLineId = ill.id | |||||
| AND sol_all.deleted = false | |||||
| AND sol_all.status IN ('pending', 'checked', 'partially_completed', 'completed') | |||||
| ), 0) as totalPickedByAllPickOrders, | |||||
| -- Calculate remaining available quantity correctly | |||||
| (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) as remainingAfterAllPickOrders, | |||||
| -- Lot availability status | |||||
| 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, | |||||
| -- Processing status | |||||
| 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 | |||||
| JOIN fpsmsdb.items i ON i.id = pol.itemId | |||||
| LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId | |||||
| LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId | |||||
| LEFT JOIN fpsmsdb.inventory_lot_line ill ON spl.suggestedLotLineId = ill.id | |||||
| LEFT JOIN fpsmsdb.router r ON r.inventoryLotId = ill.id AND r.deleted = false | |||||
| LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId | |||||
| LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId | |||||
| LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ill.id AND sol.deleted = false | |||||
| WHERE po.deleted = false | |||||
| AND po.id IN ($pickOrderIdsStr) | |||||
| AND pol.deleted = false | |||||
| AND po.status IN ('PENDING', 'RELEASED', 'COMPLETED') | |||||
| AND po.assignTo = :userId | |||||
| AND ill.deleted = false | |||||
| AND il.deleted = false | |||||
| AND (spl.pickOrderLineId IS NOT NULL OR sol.pickOrderLineId IS NOT NULL) | |||||
| ORDER BY | |||||
| CASE WHEN sol.status = 'rejected' THEN 0 ELSE 1 END, | |||||
| COALESCE(r.index, 0) ASC, | |||||
| po.code ASC, | |||||
| i.code ASC, | |||||
| il.expiryDate ASC, | |||||
| il.lotNo ASC | |||||
| """.trimIndent() | |||||
| SELECT | |||||
| -- Pick Order Information | |||||
| 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, | |||||
| -- Pick Order Line Information | |||||
| pol.id as pickOrderLineId, | |||||
| pol.qty as pickOrderLineRequiredQty, | |||||
| pol.status as pickOrderLineStatus, | |||||
| -- Item Information | |||||
| i.id as itemId, | |||||
| i.code as itemCode, | |||||
| i.name as itemName, | |||||
| uc.code as uomCode, | |||||
| uc.udfudesc as uomDesc, | |||||
| uc.udfShortDesc as uomShortDesc, | |||||
| -- Lot Information | |||||
| 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, | |||||
| -- Router Information | |||||
| r.id as routerId, | |||||
| r.index as routerIndex, | |||||
| r.route as routerRoute, | |||||
| r.area as routerArea, | |||||
| -- ✅ FIXED: Set quantities to NULL for rejected lots | |||||
| CASE | |||||
| WHEN sol.status = 'rejected' THEN NULL | |||||
| ELSE (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) | |||||
| END as availableQty, | |||||
| -- Required quantity for this lot | |||||
| COALESCE(spl.qty, 0) as requiredQty, | |||||
| -- Actual picked quantity | |||||
| COALESCE(sol.qty, 0) as actualPickQty, | |||||
| -- Suggested pick lot information | |||||
| spl.id as suggestedPickLotId, | |||||
| ill.status as lotStatus, | |||||
| -- Stock out line information | |||||
| sol.id as stockOutLineId, | |||||
| sol.status as stockOutLineStatus, | |||||
| COALESCE(sol.qty, 0) as stockOutLineQty, | |||||
| -- Additional detailed fields | |||||
| COALESCE(ill.inQty, 0) as inQty, | |||||
| COALESCE(ill.outQty, 0) as outQty, | |||||
| COALESCE(ill.holdQty, 0) as holdQty, | |||||
| COALESCE(spl.suggestedLotLineId, sol.inventoryLotLineId) as debugSuggestedLotLineId, | |||||
| ill.inventoryLotId as debugInventoryLotId, | |||||
| -- Calculate total picked quantity by ALL pick orders for this lot | |||||
| COALESCE(( | |||||
| SELECT SUM(sol_all.qty) | |||||
| FROM fpsmsdb.stock_out_line sol_all | |||||
| WHERE sol_all.inventoryLotLineId = ill.id | |||||
| AND sol_all.deleted = false | |||||
| AND sol_all.status IN ('pending', 'checked', 'partially_completed', 'completed') | |||||
| ), 0) as totalPickedByAllPickOrders, | |||||
| -- Calculate remaining available quantity correctly | |||||
| (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) as remainingAfterAllPickOrders, | |||||
| -- Lot availability status | |||||
| 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, | |||||
| -- Processing status | |||||
| 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 | |||||
| JOIN fpsmsdb.items i ON i.id = pol.itemId | |||||
| LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId | |||||
| -- Base lot links: all lot lines referenced by either suggestions or stock out lines for this POL | |||||
| LEFT JOIN ( | |||||
| SELECT spl.pickOrderLineId AS 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 | |||||
| -- Re-bind spl/sol strictly on the same lot line | |||||
| 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 | |||||
| LEFT JOIN fpsmsdb.router r ON r.inventoryLotId = ill.id AND r.deleted = false | |||||
| LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId | |||||
| LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId | |||||
| WHERE po.deleted = false | |||||
| AND po.id IN ($pickOrderIdsStr) | |||||
| AND pol.deleted = false | |||||
| AND po.status IN ('PENDING', 'RELEASED', 'COMPLETED') | |||||
| AND po.assignTo = :userId | |||||
| AND ill.deleted = false | |||||
| AND il.deleted = false | |||||
| AND ll.lotLineId IS NOT NULL | |||||
| AND (spl.pickOrderLineId IS NOT NULL OR sol.pickOrderLineId IS NOT NULL) | |||||
| ORDER BY | |||||
| CASE WHEN sol.status = 'rejected' THEN 0 ELSE 1 END, | |||||
| COALESCE(r.index, 0) ASC, | |||||
| po.code ASC, | |||||
| i.code ASC, | |||||
| il.expiryDate ASC, | |||||
| il.lotNo ASC | |||||
| """.trimIndent() | |||||
| println("�� Executing SQL for hierarchical structure: $sql") | println("�� Executing SQL for hierarchical structure: $sql") | ||||
| println("�� With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr") | println("�� With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr") | ||||