|
|
|
@@ -21,6 +21,7 @@ import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus |
|
|
|
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderGroup |
|
|
|
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderGroupRepository |
|
|
|
import com.ffii.fpsms.modules.pickOrder.web.models.* |
|
|
|
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine |
|
|
|
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository |
|
|
|
import com.ffii.fpsms.modules.stock.entity.StockOut |
|
|
|
import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository |
|
|
|
@@ -3357,286 +3358,7 @@ ORDER BY |
|
|
|
val enrichedResults = filteredResults |
|
|
|
return enrichedResults |
|
|
|
} |
|
|
|
// 修改后的逻辑 |
|
|
|
/* |
|
|
|
open fun getAllPickOrderLotsWithDetailsHierarchicalold(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:直接按 handledBy 查当前用户的活动 do_pick_order(一个 ticket) |
|
|
|
val activeTicketStatuses = listOf("released", "picking") // 如果你用的是 DoPickOrderStatus 枚举,也可以改成 List<DoPickOrderStatus> |
|
|
|
val doPickOrder = doPickOrderRepository |
|
|
|
.findFirstByHandledByAndDeletedFalseAndTicketStatusIn(user.id!!, activeTicketStatuses) |
|
|
|
|
|
|
|
if (doPickOrder == null) { |
|
|
|
println("❌ No active do_pick_order found for handledBy user $userId") |
|
|
|
return mapOf( |
|
|
|
"fgInfo" to null, |
|
|
|
"pickOrders" to emptyList<Any>() |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
val doPickOrderId = doPickOrder.id!! |
|
|
|
println(" Using do_pick_order ID (by handledBy): $doPickOrderId") |
|
|
|
|
|
|
|
// Step 2:用这个 do_pick_orderId 查对应的 do_pick_order_line / pick_order |
|
|
|
val allDoPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrderId) |
|
|
|
val allPickOrderIdsForThisTicket = allDoPickOrderLines.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
|
|
|
|
println(" Found ${allPickOrderIdsForThisTicket.size} pick orders in this do_pick_order (including completed)") |
|
|
|
|
|
|
|
// Step 3:加载这些 pick orders(包括 COMPLETED) |
|
|
|
val pickOrders = pickOrderRepository.findAllById(allPickOrderIdsForThisTicket) |
|
|
|
.filter { |
|
|
|
it.deleted == false && |
|
|
|
it.assignTo?.id == userId && |
|
|
|
it.type?.value == "do" |
|
|
|
} |
|
|
|
|
|
|
|
println(" Loaded ${pickOrders.size} pick orders (including completed)") |
|
|
|
|
|
|
|
// Step 4:原来你从 3413 行开始的收集所有 line / lots 的逻辑,全部保留 |
|
|
|
val allPickOrderLineIds = pickOrders.flatMap { it.pickOrderLines }.mapNotNull { it.id } |
|
|
|
|
|
|
|
val allSuggestions = suggestPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIds) |
|
|
|
val allStockOutLines = allPickOrderLineIds.flatMap { lineId -> |
|
|
|
stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(lineId) |
|
|
|
} |
|
|
|
|
|
|
|
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 = po.pickOrderLines |
|
|
|
.filter { !it.deleted } |
|
|
|
.map { pol -> |
|
|
|
val lineId = pol.id |
|
|
|
val item = pol.item |
|
|
|
val uom = pol.uom |
|
|
|
|
|
|
|
// 获取该 line 的 suggestions 和 stock out lines |
|
|
|
val suggestions = lineId?.let { suggestionsByLineId[it] } ?: emptyList() |
|
|
|
val stockOutLines = lineId?.let { stockOutLinesByLineId[it] } ?: emptyList() |
|
|
|
|
|
|
|
// 构建 lots(合并相同 lot 的多个 suggestions) |
|
|
|
val lotMap = mutableMapOf<Long?, LotDetailResponse>() |
|
|
|
|
|
|
|
suggestions.forEach { spl -> |
|
|
|
val ill = spl.suggestedLotLine |
|
|
|
if (ill != null && ill.id != null) { |
|
|
|
val illId = ill.id!! |
|
|
|
val illEntity = inventoryLotLinesMap[illId] ?: ill |
|
|
|
val il = illEntity.inventoryLot |
|
|
|
val w = illEntity.warehouse |
|
|
|
val isExpired = il?.expiryDate?.let { exp -> exp.isBefore(today) } == true |
|
|
|
|
|
|
|
val availableQty = (illEntity.inQty ?: zero) |
|
|
|
.minus(illEntity.outQty ?: zero) |
|
|
|
.minus(illEntity.holdQty ?: zero) |
|
|
|
|
|
|
|
// 查找对应的 stock out line |
|
|
|
val stockOutLine = stockOutLines.find { sol -> |
|
|
|
sol.inventoryLotLineId == illId |
|
|
|
} |
|
|
|
|
|
|
|
// 计算 actualPickQty |
|
|
|
val actualPickQty = stockOutLine?.qty?.let { numToBigDecimal(it as? Number) } |
|
|
|
|
|
|
|
if (lotMap.containsKey(illId)) { |
|
|
|
// 合并 requiredQty |
|
|
|
val existing = lotMap[illId]!! |
|
|
|
val newRequiredQty = (existing.requiredQty ?: zero).plus(spl.qty ?: zero) |
|
|
|
lotMap[illId] = existing.copy(requiredQty = newRequiredQty) |
|
|
|
} else { |
|
|
|
lotMap[illId] = LotDetailResponse( |
|
|
|
id = illId, |
|
|
|
lotNo = il?.lotNo, |
|
|
|
expiryDate = il?.expiryDate, |
|
|
|
location = w?.code, |
|
|
|
stockUnit = illEntity.stockUom?.uom?.udfudesc ?: uom?.udfudesc ?: "N/A", |
|
|
|
availableQty = availableQty, |
|
|
|
requiredQty = spl.qty, |
|
|
|
actualPickQty = actualPickQty, |
|
|
|
inQty = illEntity.inQty, |
|
|
|
outQty = illEntity.outQty, |
|
|
|
holdQty = illEntity.holdQty, |
|
|
|
lotStatus = illEntity.status?.value, |
|
|
|
lotAvailability = when { |
|
|
|
isExpired -> "expired" |
|
|
|
stockOutLine?.status == "rejected" -> "rejected" |
|
|
|
availableQty <= zero -> "insufficient_stock" |
|
|
|
illEntity.status?.value == "unavailable" -> "status_unavailable" |
|
|
|
else -> "available" |
|
|
|
}, |
|
|
|
processingStatus = when { |
|
|
|
stockOutLine?.status == "completed" -> "completed" |
|
|
|
stockOutLine?.status == "rejected" -> "rejected" |
|
|
|
else -> "pending" |
|
|
|
}, |
|
|
|
suggestedPickLotId = spl.id, |
|
|
|
stockOutLineId = stockOutLine?.id, |
|
|
|
stockOutLineStatus = stockOutLine?.status, |
|
|
|
stockOutLineQty = stockOutLine?.qty?.let { numToBigDecimal(it as? Number) }, |
|
|
|
router = RouterInfoResponse( |
|
|
|
id = null, |
|
|
|
index = w?.order.toString(), |
|
|
|
route = w?.code, |
|
|
|
area = w?.code |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
val lots = lotMap.values.toList() |
|
|
|
|
|
|
|
// 构建 stockouts(包括没有 lot 的) |
|
|
|
val stockouts = stockOutLines.map { sol -> |
|
|
|
val illId = sol.inventoryLotLineId |
|
|
|
val ill = illId?.let { inventoryLotLinesMap[it] } |
|
|
|
val il = ill?.inventoryLot |
|
|
|
val w = ill?.warehouse |
|
|
|
val available = if (ill == null) null else |
|
|
|
(ill.inQty ?: zero) |
|
|
|
.minus(ill.outQty ?: zero) |
|
|
|
.minus(ill.holdQty ?: zero) |
|
|
|
|
|
|
|
StockOutDetailResponse( |
|
|
|
id = sol.id, |
|
|
|
status = sol.status, |
|
|
|
qty = sol.qty?.let { numToBigDecimal(it as? Number) }, |
|
|
|
lotId = ill?.id, |
|
|
|
lotNo = il?.lotNo ?: "", |
|
|
|
location = w?.code ?: "", |
|
|
|
availableQty = available, |
|
|
|
noLot = (ill == null) |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
PickOrderLineDetailResponse( |
|
|
|
id = lineId, |
|
|
|
requiredQty = pol.qty, |
|
|
|
status = pol.status?.value, |
|
|
|
item = ItemInfoResponse( |
|
|
|
id = item?.id, |
|
|
|
code = item?.code, |
|
|
|
name = item?.name, |
|
|
|
uomCode = uom?.code, |
|
|
|
uomDesc = uom?.udfudesc, |
|
|
|
uomShortDesc = uom?.udfShortDesc |
|
|
|
), |
|
|
|
lots = lots, |
|
|
|
stockouts = stockouts |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
lineCountsPerPickOrder.add(lineDtos.size) |
|
|
|
allPickOrderLines.addAll(lineDtos) |
|
|
|
} |
|
|
|
|
|
|
|
// 排序、fgInfo、mergedPickOrder 这些也全部沿用你当前代码,只要用上面定义好的 doPickOrder/doPickOrderId 即可: |
|
|
|
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") |
|
|
|
val floorSortValue = when (indexValue) { |
|
|
|
is String -> { |
|
|
|
val parts = indexValue.split("-") |
|
|
|
if (parts.isNotEmpty()) { |
|
|
|
val floorPart = parts[0].uppercase() |
|
|
|
when (floorPart) { |
|
|
|
"1F" -> 1 |
|
|
|
"2F", "4F" -> 2 |
|
|
|
else -> 3 |
|
|
|
} |
|
|
|
} else 3 |
|
|
|
} |
|
|
|
else -> 3 |
|
|
|
} |
|
|
|
floorSortValue |
|
|
|
}, |
|
|
|
{ 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 -> { |
|
|
|
val parts = indexValue.split("-") |
|
|
|
if (parts.size > 1) { |
|
|
|
parts.last().toIntOrNull() ?: 999999 |
|
|
|
} else { |
|
|
|
indexValue.toIntOrNull() ?: 999999 |
|
|
|
} |
|
|
|
} |
|
|
|
else -> 999999 |
|
|
|
} |
|
|
|
} |
|
|
|
)) |
|
|
|
|
|
|
|
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() |
|
|
|
) |
|
|
|
|
|
|
|
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 getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, Any?> { |
|
|
|
println("=== Debug: getAllPickOrderLotsWithDetailsHierarchical (NEW STRUCTURE) ===") |
|
|
|
println("userId filter: $userId") |
|
|
|
@@ -4159,112 +3881,105 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Transactional(rollbackFor = [java.lang.Exception::class]) |
|
|
|
open fun confirmLotSubstitution(req: LotSubstitutionConfirmRequest): MessageResponse { |
|
|
|
val zero = BigDecimal.ZERO |
|
|
|
|
|
|
|
// Validate pick order line |
|
|
|
val pol = req.pickOrderLineId.let { pickOrderLineRepository.findById(it).orElse(null) } |
|
|
|
val pol = pickOrderLineRepository.findById(req.pickOrderLineId).orElse(null) |
|
|
|
?: return MessageResponse( |
|
|
|
id = null, name = "Pick order line not found", code = "ERROR", type = "pickorder", |
|
|
|
message = "Pick order line ${req.pickOrderLineId} not found", errorPosition = null |
|
|
|
) |
|
|
|
|
|
|
|
val polItemId = pol.item?.id |
|
|
|
if (polItemId == null) { |
|
|
|
return MessageResponse( |
|
|
|
?: return MessageResponse( |
|
|
|
id = null, name = "Item not found", code = "ERROR", type = "pickorder", |
|
|
|
message = "Pick order line item is null", errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 根据 lotNo 和 itemId 查找新的 InventoryLotLine |
|
|
|
val newIll = when { |
|
|
|
// 优先使用 stockInLineId(更可靠) |
|
|
|
req.newStockInLineId != null && req.newStockInLineId > 0 -> { |
|
|
|
// 通过 stockInLineId 查找 InventoryLot |
|
|
|
val inventoryLot = inventoryLotRepository.findByStockInLineIdAndDeletedFalse(req.newStockInLineId) |
|
|
|
?: return MessageResponse( |
|
|
|
id = null, name = "Inventory lot not found", code = "ERROR", type = "pickorder", |
|
|
|
message = "Inventory lot with stockInLineId ${req.newStockInLineId} not found", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
|
|
|
|
// 通过 InventoryLot 和 itemId 查找 InventoryLotLine |
|
|
|
val lotLines = inventoryLotLineRepository.findAllByInventoryLotId(inventoryLot.id!!) |
|
|
|
.filter { it.inventoryLot?.item?.id == polItemId && !it.deleted } |
|
|
|
|
|
|
|
if (lotLines.isEmpty()) { |
|
|
|
return MessageResponse( |
|
|
|
id = null, name = "Lot line not found", code = "ERROR", type = "pickorder", |
|
|
|
message = "Inventory lot line with stockInLineId ${req.newStockInLineId} and itemId ${polItemId} not found", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// 如果有多个,取第一个(通常应该只有一个) |
|
|
|
lotLines.first() |
|
|
|
} |
|
|
|
|
|
|
|
// 兼容旧方式:使用 lotNo |
|
|
|
req.newInventoryLotNo != null && req.newInventoryLotNo.isNotBlank() -> { |
|
|
|
inventoryLotLineRepository.findByLotNoAndItemId(req.newInventoryLotNo, polItemId) |
|
|
|
?: return MessageResponse( |
|
|
|
id = null, name = "New lot line not found", code = "ERROR", type = "pickorder", |
|
|
|
message = "Inventory lot line with lotNo '${req.newInventoryLotNo}' and itemId ${polItemId} not found", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
else -> { |
|
|
|
return MessageResponse( |
|
|
|
id = null, name = "Invalid request", code = "ERROR", type = "pickorder", |
|
|
|
message = "Either newStockInLineId or newInventoryLotNo must be provided", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
// Find new InventoryLotLine (from stockInLineId first, fallback lotNo) |
|
|
|
val newIll = resolveNewInventoryLotLine(req, polItemId) |
|
|
|
?: return MessageResponse( |
|
|
|
id = null, name = "New lot line not found", code = "ERROR", type = "pickorder", |
|
|
|
message = "Cannot resolve new inventory lot line", errorPosition = null |
|
|
|
) |
|
|
|
|
|
|
|
// Item consistency check (应该已经通过上面的查询保证了,但再次确认) |
|
|
|
// Item consistency check |
|
|
|
val newItemId = newIll.inventoryLot?.item?.id |
|
|
|
if (newItemId == null || polItemId != newItemId) { |
|
|
|
if (newItemId == null || newItemId != polItemId) { |
|
|
|
return MessageResponse( |
|
|
|
id = null, name = "Item mismatch", code = "ERROR", type = "pickorder", |
|
|
|
message = "New lot line item does not match pick order line item", errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
val newIllId = newIll.id ?: return MessageResponse( |
|
|
|
id = null, name = "Invalid lot line", code = "ERROR", type = "pickorder", |
|
|
|
message = "New inventory lot line has no ID", errorPosition = null |
|
|
|
) |
|
|
|
val newLotNo = newIll.inventoryLot?.lotNo ?: req.newInventoryLotNo ?: "unknown" |
|
|
|
// 1) Update suggested pick lot (if provided): move holdQty from old ILL to new ILL and re-point the suggestion |
|
|
|
if (req.originalSuggestedPickLotId != null && req.originalSuggestedPickLotId > 0) { |
|
|
|
// ✅ 使用 repository 而不是 SQL |
|
|
|
val originalSpl = suggestPickLotRepository.findById(req.originalSuggestedPickLotId).orElse(null) |
|
|
|
|
|
|
|
if (originalSpl != null) { |
|
|
|
val oldIll = originalSpl.suggestedLotLine |
|
|
|
val qty = originalSpl.qty ?: zero |
|
|
|
// Resolve SuggestedPickLot: |
|
|
|
// - If originalSuggestedPickLotId provided: use it |
|
|
|
// - Else (1:1 assumption): find by pickOrderLineId (optionally also by stockOutLineId if you add repository method) |
|
|
|
val spl = if (req.originalSuggestedPickLotId != null && req.originalSuggestedPickLotId > 0) { |
|
|
|
suggestPickLotRepository.findById(req.originalSuggestedPickLotId).orElse(null) |
|
|
|
} else { |
|
|
|
// 1:1 assumption fallback (you need a repository method; replace with your actual one) |
|
|
|
// e.g. suggestPickLotRepository.findFirstByPickOrderLineIdAndDeletedFalseOrderByIdDesc(req.pickOrderLineId) |
|
|
|
suggestPickLotRepository.findFirstByPickOrderLineId(req.pickOrderLineId) |
|
|
|
} |
|
|
|
|
|
|
|
if (oldIll != null && oldIll.id != newIllId) { |
|
|
|
// Decrease hold on old, increase on new |
|
|
|
oldIll.holdQty = (oldIll.holdQty ?: zero).minus(qty).max(zero) |
|
|
|
inventoryLotLineRepository.save(oldIll) |
|
|
|
|
|
|
|
newIll.holdQty = (newIll.holdQty ?: zero).plus(qty) |
|
|
|
inventoryLotLineRepository.save(newIll) |
|
|
|
} |
|
|
|
if (spl == null) { |
|
|
|
return MessageResponse( |
|
|
|
id = null, name = "Suggested pick lot not found", code = "ERROR", type = "pickorder", |
|
|
|
message = "SuggestedPickLot not found for pickOrderLineId=${req.pickOrderLineId}", errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ 使用 repository 更新 suggestion |
|
|
|
originalSpl.suggestedLotLine = newIll |
|
|
|
suggestPickLotRepository.save(originalSpl) |
|
|
|
} |
|
|
|
val qtyToHold = spl.qty ?: zero |
|
|
|
if (qtyToHold.compareTo(zero) <= 0) { |
|
|
|
return MessageResponse( |
|
|
|
id = null, name = "Invalid qty", code = "ERROR", type = "pickorder", |
|
|
|
message = "SuggestedPickLot qty is invalid: $qtyToHold", errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// Availability check on newIll BEFORE updates |
|
|
|
val inQty = newIll.inQty ?: zero |
|
|
|
val outQty = newIll.outQty ?: zero |
|
|
|
val holdQty = newIll.holdQty ?: zero |
|
|
|
val issueQty = newIll.issueQty ?: zero |
|
|
|
val available = inQty.subtract(outQty).subtract(holdQty).subtract(issueQty) |
|
|
|
|
|
|
|
if (available.compareTo(qtyToHold) < 0) { |
|
|
|
return MessageResponse( |
|
|
|
id = null, name = "Insufficient lot qty", code = "REJECT", type = "pickorder", |
|
|
|
message = "Reject switch lot: available=$available < required=$qtyToHold", errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// 2) Update stock out line (if provided): re-point to new ILL; keep qty and status unchanged |
|
|
|
val oldIll = spl.suggestedLotLine |
|
|
|
|
|
|
|
// If oldIll exists and different: move hold old -> new |
|
|
|
if (oldIll != null && oldIll.id != null && oldIll.id != newIll.id) { |
|
|
|
val oldHold = oldIll.holdQty ?: zero |
|
|
|
val newOldHold = oldHold.subtract(qtyToHold) |
|
|
|
oldIll.holdQty = if (newOldHold.compareTo(zero) < 0) zero else newOldHold |
|
|
|
inventoryLotLineRepository.save(oldIll) |
|
|
|
|
|
|
|
val newHold = (newIll.holdQty ?: zero).add(qtyToHold) |
|
|
|
newIll.holdQty = newHold |
|
|
|
inventoryLotLineRepository.save(newIll) |
|
|
|
} |
|
|
|
|
|
|
|
// If first bind (oldIll == null): just hold on new |
|
|
|
if (oldIll == null) { |
|
|
|
val newHold = (newIll.holdQty ?: zero).add(qtyToHold) |
|
|
|
newIll.holdQty = newHold |
|
|
|
inventoryLotLineRepository.save(newIll) |
|
|
|
} |
|
|
|
|
|
|
|
// Point suggestion to new lot line |
|
|
|
spl.suggestedLotLine = newIll |
|
|
|
suggestPickLotRepository.save(spl) |
|
|
|
|
|
|
|
// Update stock out line if provided |
|
|
|
if (req.stockOutLineId != null && req.stockOutLineId > 0) { |
|
|
|
val sol = stockOutLIneRepository.findById(req.stockOutLineId).orElse(null) |
|
|
|
if (sol != null) { |
|
|
|
@@ -4274,15 +3989,39 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
val newLotNo = newIll.inventoryLot?.lotNo ?: req.newInventoryLotNo |
|
|
|
return MessageResponse( |
|
|
|
id = null, |
|
|
|
name = "Lot substitution confirmed", |
|
|
|
code = "SUCCESS", |
|
|
|
type = "pickorder", |
|
|
|
message = "Updated suggestion and stock out line to new lot line with lotNo '${newLotNo}'", |
|
|
|
errorPosition = null |
|
|
|
message = "Updated suggestion and stock out line to new lot line with lotNo '$newLotNo'", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
private fun resolveNewInventoryLotLine( |
|
|
|
req: LotSubstitutionConfirmRequest, |
|
|
|
itemId: Long |
|
|
|
): InventoryLotLine? { |
|
|
|
// Prefer stockInLineId |
|
|
|
if (req.newStockInLineId != null && req.newStockInLineId > 0) { |
|
|
|
val inventoryLot = inventoryLotRepository.findByStockInLineIdAndDeletedFalse(req.newStockInLineId) |
|
|
|
?: return null |
|
|
|
|
|
|
|
val lotLines = inventoryLotLineRepository.findAllByInventoryLotId(inventoryLot.id!!) |
|
|
|
.filter { it.inventoryLot?.item?.id == itemId && !it.deleted } |
|
|
|
|
|
|
|
return lotLines.firstOrNull() |
|
|
|
} |
|
|
|
|
|
|
|
// Fallback lotNo (req.newInventoryLotNo is non-null String in your model) |
|
|
|
if (req.newInventoryLotNo.isNotBlank()) { |
|
|
|
return inventoryLotLineRepository.findByLotNoAndItemId(req.newInventoryLotNo, itemId) |
|
|
|
} |
|
|
|
|
|
|
|
return null |
|
|
|
} |
|
|
|
|
|
|
|
open fun getCompletedDoPickOrders( |
|
|
|
userId: Long, |
|
|
|
|