From bdcb3cab3a429d4fe9233b0ab10657023e912a71 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Sun, 1 Mar 2026 21:56:20 +0800 Subject: [PATCH] updated bag --- .../fpsms/modules/bag/service/bagService.kt | 10 +- .../pickOrder/service/PickOrderService.kt | 733 +----------------- .../stock/service/SuggestedPickLotService.kt | 12 +- 3 files changed, 16 insertions(+), 739 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/bag/service/bagService.kt b/src/main/java/com/ffii/fpsms/modules/bag/service/bagService.kt index 29b7ea8..af4b56d 100644 --- a/src/main/java/com/ffii/fpsms/modules/bag/service/bagService.kt +++ b/src/main/java/com/ffii/fpsms/modules/bag/service/bagService.kt @@ -97,20 +97,22 @@ fun createJoBagConsumption(request: CreateJoBagConsumptionRequest): MessageRespo entity = null, ) } + val balanceQty = bagLotLine.balanceQty ?: 0 val startQty = bagLotLine.startQty ?: 0 - if (startQty < request.consumedQty + request.scrapQty) { + + if (balanceQty < request.consumedQty + request.scrapQty) { return MessageResponse( id = null, code = null, name = null, type = "error", - message = "Insufficient balance. Available: $startQty, Requested: ${request.consumedQty + request.scrapQty}", + message = "Insufficient balance. Available: $balanceQty, Requested: ${request.consumedQty + request.scrapQty}", errorPosition = null, entity = null, ) } - val newBalanceQty = startQty - request.consumedQty - request.scrapQty + val newBalanceQty = balanceQty - request.consumedQty - request.scrapQty if (bagLotLine.firstUseDate == null) { bagLotLine.firstUseDate = LocalDate.now() @@ -134,7 +136,7 @@ fun createJoBagConsumption(request: CreateJoBagConsumptionRequest): MessageRespo this.jobId = request.jobId this.stockOutLineId = bagLotLine.stockOutLineId this.jobOrderCode=jobOrder?.code?:"" - this.startQty = startQty + this.startQty = balanceQty this.consumedQty = request.consumedQty this.scrapQty = request.scrapQty this.endQty = newBalanceQty diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt index ee3cf67..1d999d9 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt @@ -1533,16 +1533,7 @@ open class PickOrderService( return pickOrderGroupRepository.findByPickOrderIdAndDeletedIsFalse(pickOrderId) } - fun getAllGroupsFromDatabase(): List> { - val sql = """ - SELECT id, name, target_date, pick_order_id - FROM pick_order_group - WHERE deleted = false - ORDER BY name ASC - """.trimIndent() - return jdbcDao.queryForList(sql, emptyMap()) - } open fun allPickOrdersGroup(): List { return pickOrderGroupRepository.findAllByDeletedIsFalse() @@ -2455,279 +2446,7 @@ open class PickOrderService( ) } } -/* - @Transactional(rollbackFor = [java.lang.Exception::class]) - open fun autoAssignAndReleasePickOrderByStore(userId: Long, storeId: String): MessageResponse { - try { - println("=== DEBUG: autoAssignAndReleasePickOrderByStore ===") - println("userId: $userId, storeId: $storeId") - - val zero = BigDecimal.ZERO - val releasedBy = SecurityUtils.getUser().getOrNull() - val user = userService.find(userId).orElse(null) - if (user == null) { - return MessageResponse( - id = null, name = "User not found", code = "ERROR", type = "pickorder", - message = "User with ID $userId not found", errorPosition = null - ) - } - - val sql = """ - SELECT DISTINCT dpo.pick_order_id AS pickOrderId - FROM do_pick_order dpo - WHERE dpo.deleted = false - AND dpo.ticket_status = 'pending' - AND dpo.store_id = :storeId - AND dpo.pick_order_id IS NOT NULL - """.trimIndent() - val idRows = jdbcDao.queryForList(sql, mapOf("storeId" to storeId)) - val pickOrderIdsByStore = idRows.mapNotNull { row -> - when (val id = row["pickOrderId"]) { - is Number -> id.toLong() - is String -> id.toLongOrNull() - else -> null - } - }.toSet() - println("Candidate pickOrderIds by store $storeId: $pickOrderIdsByStore") - - if (pickOrderIdsByStore.isEmpty()) { - return MessageResponse( - id = null, name = "No pick orders", code = "NO_ORDERS", type = "pickorder", - message = "No pending pick orders found for store $storeId", errorPosition = null - ) - } - - // Check if user already has pick orders in progress - val userExistingOrders = pickOrderRepository.findAllByAssignToAndStatusIn( - user, - listOf(PickOrderStatus.PENDING, PickOrderStatus.RELEASED) - ).filter { it.deliveryOrder != null } - - if (userExistingOrders.isNotEmpty()) { - println(" DEBUG: User $userId already has ${userExistingOrders.size} pick orders in progress:") - userExistingOrders.forEach { po -> - println(" DEBUG: Existing order ${po.id}: code=${po.code}, status=${po.status}") - } - return MessageResponse( - id = null, - name = "User already has pick orders", - code = "USER_BUSY", - type = "pickorder", - message = "User $userId already has ${userExistingOrders.size} pick orders in progress. Cannot assign new orders.", - errorPosition = null, - entity = mapOf( - "existingOrders" to userExistingOrders.map { - mapOf( - "id" to it.id, - "code" to it.code, - "status" to it.status?.value - ) - } - ) - ) - } - - val availablePickOrders = pickOrderRepository - .findAll() - .filter { it.id != null && pickOrderIdsByStore.contains(it.id!!) } - .filter { it.deliveryOrder != null } - .filter { it.assignTo == null } - .filter { it.status == PickOrderStatus.PENDING || it.status == PickOrderStatus.RELEASED } - .sortedBy { it.targetDate } - .take(1) - - if (availablePickOrders.isEmpty()) { - return MessageResponse( - id = null, name = "No available pick orders", code = "NO_ORDERS", type = "pickorder", - message = "No unassigned pick orders available for store $storeId", errorPosition = null - ) - } - - val selected = availablePickOrders.first() - val currUser = SecurityUtils.getUser().orElseThrow() - - // If still PENDING, perform full release flow - if (selected.status == PickOrderStatus.PENDING) { - val newConsoCode = assignConsoCode() - - val stockOut = StockOut().apply { - this.type = "job" - this.consoPickOrderCode = newConsoCode - this.status = StockOutStatus.PENDING.status - this.handler = currUser.id - } - val savedStockOut = stockOutRepository.saveAndFlush(stockOut) - - selected.apply { - this.releasedBy = releasedBy - status = PickOrderStatus.RELEASED - this.consoCode = newConsoCode - } - - val suggestions = suggestedPickLotService.suggestionForPickOrders( - SuggestedPickLotForPoRequest(pickOrders = listOf(selected)) - ) - val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList) - pickOrderRepository.saveAndFlush(selected) - - val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn( - saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id } - ) - saveSuggestedPickLots.forEach { lot -> - val lotLineId = lot.suggestedLotLine?.id - if (lotLineId != null) { - val idx = inventoryLotLines.indexOf(lot.suggestedLotLine) - if (idx >= 0) { - val currentHold = inventoryLotLines[idx].holdQty ?: zero - val addHold = lot.qty ?: zero - inventoryLotLines[idx].holdQty = currentHold.plus(addHold) - } - } - } - inventoryLotLineRepository.saveAll(inventoryLotLines) - - var precreated = 0 - saveSuggestedPickLots.forEach { lot -> - val polId = lot.pickOrderLine?.id - val illId = lot.suggestedLotLine?.id - if (polId != null && illId != null) { - val existing = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( - polId, - illId - ) - if (existing.isEmpty()) { - val pol = pickOrderLineRepository.findById(polId).orElse(null) - val ill = inventoryLotLineRepository.findById(illId).orElse(null) - if (pol != null && ill != null) { - val line = com.ffii.fpsms.modules.stock.entity.StockOutLine().apply { - this.stockOut = savedStockOut - this.pickOrderLine = pol - this.inventoryLotLine = ill - this.item = pol.item - this.status = - com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus.PENDING.status - this.qty = 0.0 - } - stockOutLIneRepository.save(line) - precreated++ - } - } - } - } - println("Precreated $precreated stock out lines for store $storeId") - } - - // Assign user (both pending->released and already released) - selected.assignTo = user - pickOrderRepository.saveAndFlush(selected) - - // 更新 DoPickOrderRecord,填充新字段 - val deliveryOrder = selected.deliveryOrder - if (deliveryOrder != null) { - val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now() - val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")) - - println(" DEBUG: Target date: $targetDate, Date prefix: $datePrefix") - - // Find truck by shop ID with earliest departure time - val truck = deliveryOrder.shop?.id?.let { shopId -> - println(" DEBUG: Looking for truck with shop ID: $shopId") - val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) - println(" DEBUG: Found ${trucks.size} trucks for shop $shopId") - val selectedTruck = trucks.minByOrNull { it.departureTime ?: LocalTime.MAX } - println(" DEBUG: Selected truck: ID=${selectedTruck?.id}, DepartureTime=${selectedTruck?.departureTime}") - selectedTruck - } - - // 根据 truck 的 Store_id 字段确定 storeId - val determinedStoreId = when (truck?.storeId) { - 4 -> "4/F" - 2 -> "2/F" - else -> "2/F" - } - - // Get next ticket number for this date and store - val nextTicketNumber = doPickOrderService.getNextTicketNumber(datePrefix, determinedStoreId) - println(" DEBUG: Next ticket number: $nextTicketNumber") - - println(" DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines") - - // UPDATE existing do_pick_order_record entries instead of creating new ones - val existingRecords = doPickOrderRecordRepository.findByPickOrderId(selected.id!!) - println(" DEBUG: Found ${existingRecords.size} existing DoPickOrderRecord entries for pick order ${selected.id}") - - if (existingRecords.isNotEmpty()) { - // Update existing records - existingRecords.forEach { record -> - record.handledBy = user.id - record.ticketStatus = DoPickOrderStatus.released - record.ticketReleaseTime = LocalDateTime.now() // 设置 release time - // 填充新字段 - record.truckLanceCode = truck?.truckLanceCode - record.shopCode = deliveryOrder.shop?.code - record.shopName = deliveryOrder.shop?.name - record.requiredDeliveryDate = targetDate - println(" DEBUG: Updating existing DoPickOrderRecord ID: ${record.id} - handledBy: ${user.id}, status: released") - } - doPickOrderRecordRepository.saveAll(existingRecords) - println(" Updated ${existingRecords.size} existing DoPickOrderRecord entries") - } else { - // Only create new records if none exist (fallback) - println("⚠️ No existing DoPickOrderRecord entries found, creating new ones") - deliveryOrder.deliveryOrderLines.forEach { line -> - println(" DEBUG: Processing line - Store ID: $determinedStoreId") - - val doPickOrderRecord = DoPickOrderRecord( - id=dopickorder.id, - storeId = determinedStoreId, - ticketNo = nextTicketNumber, - ticketStatus = DoPickOrderStatus.released, - truckId = truck?.id, - pickOrderId = selected.id, - truckDepartureTime = truck?.departureTime, - shopId = deliveryOrder.shop?.id, - handledBy = user.id, - // 填充新增字段 - truckLanceCode = truck?.truckLanceCode, - shopCode = deliveryOrder.shop?.code, - shopName = deliveryOrder.shop?.name, - requiredDeliveryDate = targetDate - ) - - println(" DEBUG: Creating new DoPickOrderRecord - Store: $determinedStoreId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") - val savedDoPickOrderRecord = doPickOrderRecordRepository.save(doPickOrderRecord) - println(" DEBUG: Saved new DoPickOrderRecord - ID: ${savedDoPickOrderRecord.id}") - } - } - } - - doPickOrderService.updateHandledByForPickOrder(selected.id!!, user.id!!) - doPickOrderService.updateRecordHandledByForPickOrder(selected.id!!, user.id!!) // 添加这行 - println(" Updated DoPickOrder and DoPickOrderRecord handledBy to user $userId for pick order ${selected.id}") - - return MessageResponse( - id = null, - name = "Pick order assigned", - code = "SUCCESS", - type = "pickorder", - message = "Assigned to user $userId for store $storeId", - errorPosition = null, - entity = mapOf( - "pickOrderIds" to listOf(selected.id!!), - "storeId" to storeId, - "status" to selected.status?.value - ) - ) - } catch (e: Exception) { - e.printStackTrace() - return MessageResponse( - id = null, name = "Failed to auto-assign by store", code = "ERROR", type = "pickorder", - message = "Failed to auto-assign by store: ${e.message}", errorPosition = null - ) - } - } -*/ open fun getAllPickOrderLotsWithDetailsWithAutoAssign(userId: Long): List> { println("=== Debug: getAllPickOrderLotsWithDetailsWithAutoAssign ===") println("today: ${LocalDate.now()}") @@ -4791,227 +4510,7 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto } } -open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map { - println("=== Debug: getLotDetailsByDoPickOrderRecordId (Hierarchical Format) ===") - println("doPickOrderRecordId: $doPickOrderRecordId") - // 获取 FG 信息(从 record 表) - val doPickOrderRecordSql = """ - SELECT - dpor.id as do_pick_order_record_id, - dpor.ticket_no, - dpor.store_id, - dpor.TruckLanceCode, - dpor.truck_departure_time, - dpor.ShopCode, - dpor.ShopName - FROM fpsmsdb.do_pick_order_record dpor - WHERE dpor.id = :doPickOrderRecordId - AND dpor.deleted = false - """.trimIndent() - - val doPickOrderRecordInfo = jdbcDao.queryForMap(doPickOrderRecordSql, mapOf("doPickOrderRecordId" to doPickOrderRecordId)).orElse(null) - - if (doPickOrderRecordInfo == null) { - println("❌ No do_pick_order_record found with ID: $doPickOrderRecordId") - return mapOf( - "fgInfo" to null, - "pickOrders" to emptyList() - ) - } - - // 构建 FG 信息 - val fgInfo = mapOf( - "doPickOrderId" to doPickOrderRecordId, - "ticketNo" to doPickOrderRecordInfo["ticket_no"], - "storeId" to doPickOrderRecordInfo["store_id"], - "shopCode" to doPickOrderRecordInfo["ShopCode"], - "shopName" to doPickOrderRecordInfo["ShopName"], - "truckLanceCode" to doPickOrderRecordInfo["TruckLanceCode"], - "departureTime" to doPickOrderRecordInfo["truck_departure_time"] - ) - - // Step 2: 获取该 do_pick_order_record 下的所有 pick orders - val pickOrdersSql = """ - SELECT DISTINCT - dpolr.pick_order_id, - dpolr.pick_order_code, - dpolr.do_order_id, - dpolr.delivery_order_code, - po.consoCode, - po.status, - DATE_FORMAT(po.targetDate, '%Y-%m-%d') as targetDate - FROM fpsmsdb.do_pick_order_line_record dpolr - INNER JOIN fpsmsdb.pick_order po ON po.id = dpolr.pick_order_id - WHERE dpolr.do_pick_order_id = :doPickOrderRecordId - AND dpolr.deleted = 0 - AND po.deleted = false - ORDER BY dpolr.pick_order_id - """.trimIndent() - - val pickOrdersInfo = jdbcDao.queryForList(pickOrdersSql, mapOf("doPickOrderRecordId" to doPickOrderRecordId)) - println(" Found ${pickOrdersInfo.size} pick orders in do_pick_order_record") - - // Step 3: 为每个 pick order 获取 lines 和 lots - val pickOrders = pickOrdersInfo.map { poInfo -> - val pickOrderId = (poInfo["pick_order_id"] as? Number)?.toLong() - - // 查询该 pick order 的所有 lines 和 lots(复用之前的 SQL) - 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, - - 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' - 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 - LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId - LEFT JOIN fpsmsdb.stock_out_line sol - ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = spl.suggestedLotLineId AND sol.deleted = false - LEFT JOIN fpsmsdb.inventory_lot_line ill ON spl.suggestedLotLineId = ill.id 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 - - 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)) - - // 按 pickOrderLineId 分组构建层级结构(与 all-lots-hierarchical 相同) - val lineGroups = linesResults.groupBy { (it["pickOrderLineId"] as? Number)?.toLong() } - - val pickOrderLines = lineGroups.map { (lineId, lineRows) -> - val firstLineRow = lineRows.firstOrNull() - - if (firstLineRow == null) { - null - } else { - 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"] - ) - ) - } - } else { - emptyList() - } - - 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() - - 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 - ) - } - - return mapOf( - "fgInfo" to fgInfo, - "pickOrders" to pickOrders - ) -} private fun numToBigDecimal(n: Number?): BigDecimal { return when (n) { null -> BigDecimal.ZERO @@ -5019,162 +4518,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map BigDecimal.valueOf(n.toDouble()) } } - open fun getLotDetailsByDoPickOrderRecordId2(doPickOrderRecordId: Long): Map { - // 1) 头信息(来自 record 表) - val dpor = doPickOrderRecordRepository.findById(doPickOrderRecordId).orElse(null) - ?: return mapOf("fgInfo" to null, "pickOrders" to emptyList()) - - val fgInfo = mapOf( - "doPickOrderId" to dpor.id, - "ticketNo" to dpor.ticketNo, - "storeId" to dpor.storeId, - "shopCode" to dpor.shopCode, - "shopName" to dpor.shopName, - "truckLanceCode" to dpor.truckLanceCode, - "departureTime" to dpor.truckDepartureTime - ) - - // 2) 取该 record 下所有 pick orders(通过行记录聚合) - val lineRecords = doPickOrderLineRecordRepository.findByDoPickOrderIdAndDeletedFalse(dpor.id!!) - val pickOrderIds = lineRecords.mapNotNull { it.pickOrderId }.distinct() - if (pickOrderIds.isEmpty()) { - return mapOf("fgInfo" to fgInfo, "pickOrders" to emptyList()) - } - val pickOrders = pickOrderRepository.findAllById(pickOrderIds) - - // 3) 逐个 pick order 组装行与批次/出库行 - val pickOrderDtos = pickOrders.map { po -> - val lines = po.pickOrderLines - - val lineDtos = lines.map { pol -> - val item = pol.item - val uom = pol.uom - - // 建议批次 - val lots = pol.suggestedPickLots.mapNotNull { spl -> - val ill = spl.suggestedLotLine ?: return@mapNotNull null - val il = ill.inventoryLot - val w = ill.warehouse - val today = LocalDate.now() - val isExpired = il?.expiryDate?.let { exp -> exp.isBefore(today) } == true - - mapOf( - "id" to ill.id, - "lotNo" to il?.lotNo, - "expiryDate" to il?.expiryDate, - "location" to w?.code, - "stockUnit" to (ill.stockUom?.uom?.udfudesc ?: uom?.udfudesc ?: "N/A"), - "availableQty" to ((ill.inQty ?: BigDecimal.ZERO) - .minus(ill.outQty ?: BigDecimal.ZERO) - .minus(ill.holdQty ?: BigDecimal.ZERO)), - "requiredQty" to spl.qty, - "actualPickQty" to null, // 稍后回填 - "inQty" to ill.inQty, - "outQty" to ill.outQty, - "holdQty" to ill.holdQty, - "lotStatus" to ill.status?.value, - "lotAvailability" to when { - isExpired -> "expired" - ill.status?.value == "unavailable" -> "status_unavailable" - ((ill.inQty ?: BigDecimal.ZERO) - .minus(ill.outQty ?: BigDecimal.ZERO) - .minus(ill.holdQty ?: BigDecimal.ZERO)) <= BigDecimal.ZERO -> "insufficient_stock" - else -> "available" - }, - "processingStatus" to "pending", - "suggestedPickLotId" to spl.id, - "stockOutLineId" to null, - "stockOutLineStatus" to null, - "stockOutLineQty" to null, - "router" to mapOf( - "id" to null, - "index" to w?.order, - "route" to w?.code, - "area" to w?.code - ) - ) - } - - // 出库行(投影):支持 inventoryLotLineId 为空 - val stockOutLines = pol.id?.let { - // 使用你已有的方法;若该方法返回投影类型(如 StockOutLineInfo),则以下基于字段名访问 - stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(it) - } ?: emptyList() - - // 如果是投影,通常字段:id, qty(Double?), status(String?), inventoryLotLineId(Long?) - // 计算每个 ILL 的已拣数量(BigDecimal) - val actualQtyByIllId: Map = stockOutLines - .mapNotNull { sol -> - val illId = sol.inventoryLotLineId ?: return@mapNotNull null - val qtyBD = numToBigDecimal(sol.qty as Number?) - illId to qtyBD - } - .groupBy({ it.first }, { it.second }) - .mapValues { (_, list) -> list.fold(BigDecimal.ZERO) { acc, v -> acc + v } } - - val lotsWithActual = lots.map { lot -> - val illId = lot["id"] as? Long - val actual = illId?.let { actualQtyByIllId[it] } - lot.toMutableMap().apply { if (actual != null) put("actualPickQty", actual) } - } - - // 构建 stockouts,若有 lotLineId 则加载 ILL 以补充 lotNo/location/availableQty - val stockouts = stockOutLines.map { sol -> - val illId = sol.inventoryLotLineId - val ill = illId?.let { inventoryLotLineRepository.findById(it).orElse(null) } - val il = ill?.inventoryLot - val w = ill?.warehouse - val available = if (ill == null) null else - (ill.inQty ?: BigDecimal.ZERO) - .minus(ill.outQty ?: BigDecimal.ZERO) - .minus(ill.holdQty ?: BigDecimal.ZERO) - - mapOf( - "id" to sol.id, - "status" to sol.status, - "qty" to numToBigDecimal(sol.qty as Number?), - "lotId" to ill?.id, - "lotNo" to (il?.lotNo ?: ""), - "location" to (w?.code ?: ""), - "availableQty" to available, - "noLot" to (ill == null) - ) - } - - mapOf( - "id" to pol.id, - "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 lotsWithActual, - "stockouts" to stockouts - ) - } - - mapOf( - "pickOrderId" to po.id, - "pickOrderCode" to po.code, - "doOrderId" to po.deliveryOrder?.id, - "deliveryOrderCode" to po.deliveryOrder?.code, - "consoCode" to po.consoCode, - "status" to po.status?.value, - "targetDate" to po.targetDate?.toLocalDate(), - "pickOrderLines" to lineDtos - ) - } - - return mapOf( - "fgInfo" to fgInfo, - "pickOrders" to pickOrderDtos - ) - } + open fun getLotDetailsByDoPickOrderRecordId3(doPickOrderRecordId: Long): LotDetailsByDoPickOrderRecordResponse { println("=== Debug: getLotDetailsByDoPickOrderRecordId3 (Repository-based) ===") println("doPickOrderRecordId: $doPickOrderRecordId") @@ -5463,81 +4807,6 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map> { - try { - println(" Starting getFgPickOrdersFromRecordByPickOrderId with pickOrderId: $pickOrderId") - - val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) - if (pickOrder == null) { - println("❌ Pick order not found with ID: $pickOrderId") - return emptyList() - } - - val deliveryOrder = pickOrder.deliveryOrder - val shop = deliveryOrder?.shop - val supplier = deliveryOrder?.supplier - - // 从 do_pick_order_record 和 do_pick_order_line_record 表查询 - val ticketNo = try { - val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) - doPickOrderRecords.firstOrNull()?.ticketNo ?: "" - } catch (e: Exception) { - println("⚠️ Error getting ticket number from record: ${e.message}") - "" - } - - val dpoStoreId = try { - val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) - doPickOrderRecords.firstOrNull()?.storeId ?: "" - } catch (e: Exception) { - "" - } - - val truckLanceCode = try { - val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) - doPickOrderRecords.firstOrNull()?.truckLanceCode ?: "" - } catch (e: Exception) { - "" - } - - val departureTime = try { - val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) - doPickOrderRecords.firstOrNull()?.truckDepartureTime?.toString() ?: "" - } catch (e: Exception) { - "" - } - - val result = mapOf( - "pickOrderId" to (pickOrder.id ?: 0L), - "pickOrderCode" to (pickOrder.code ?: ""), - "pickOrderConsoCode" to (pickOrder.consoCode ?: ""), - "pickOrderTargetDate" to (pickOrder.targetDate?.toString() ?: ""), - "pickOrderStatus" to (pickOrder.status?.value ?: ""), - "deliveryOrderId" to (deliveryOrder?.id ?: 0L), - "deliveryNo" to (deliveryOrder?.code ?: ""), - "deliveryDate" to (deliveryOrder?.orderDate?.toString() ?: ""), - "shopId" to (shop?.id ?: 0L), - "shopCode" to (shop?.code ?: ""), - "shopName" to (shop?.name ?: ""), - "shopAddress" to buildShopAddress(shop), - "shopPoNo" to (supplier?.code ?: ""), - "numberOfCartons" to (pickOrder.pickOrderLines.size), - "truckLanceCode" to truckLanceCode, // 从 record 表 - "DepartureTime" to departureTime, // 从 record 表 - "ticketNo" to ticketNo, // 从 record 表 - "storeId" to dpoStoreId, // 从 record 表 - "qrCodeData" to (pickOrder.id ?: 0L) - ) - println(" FG Pick Orders from record: 1") - return listOf(result) - - } catch (e: Exception) { - println("❌ Error in getFgPickOrdersFromRecordByPickOrderId: ${e.message}") - e.printStackTrace() - return emptyList() - } -} } diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt index 1f41bba..c8a084b 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt @@ -148,7 +148,9 @@ open class SuggestedPickLotService( println("calculateRemainingQtyForInfo(lotLine) ${calculateRemainingQtyForInfo(lotLine)}") val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() } val warehouseStoreId=inventoryLotLine?.warehouse?.store_id - if ( warehouseStoreId == "3F") { + // 3F filter only applies to delivery order release; no limit for job order, AssignAndRelease, etc. + val isDeliveryOrderPick = line.pickOrder?.type?.value == "do" + if (warehouseStoreId == "3F" && isDeliveryOrderPick) { return@forEachIndexed } // 修复:计算可用数量,转换为销售单位 @@ -1223,10 +1225,11 @@ private fun generateOptimalSuggestionsForAllPickOrders( remainingQty > zero }.toMutableList() + val isDeliveryOrderContext = pickOrderLines.any { it.pickOrder?.type?.value == "do" } lotEntities.forEach { lot -> if (remainingPickOrderLines.isEmpty()) return@forEach val warehouseStoreId=lot.warehouse?.store_id - if ( warehouseStoreId == "3F") { + if (warehouseStoreId == "3F" && isDeliveryOrderContext) { return@forEach } val totalQty = lot.inQty ?: zero @@ -1281,6 +1284,7 @@ private fun generateOptimalSuggestionsForAllPickOrders( return suggestions } + private fun updateInventoryTableAfterResuggest(pickOrder: PickOrder) { try { // Get all item IDs from the pick order @@ -1503,8 +1507,10 @@ private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): L val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) } ?: return@forEach + val warehouseStoreId=lot.warehouse?.store_id - if ( warehouseStoreId == "3F") { + // 3F filter only for delivery order + if (warehouseStoreId == "3F" && pickOrder.type?.value == "do") { return@forEach } val totalQty = lot.inQty ?: zero