| @@ -46,7 +46,7 @@ class DoPickOrder { | |||||
| @Column(name = "ticket_release_time") | @Column(name = "ticket_release_time") | ||||
| var ticketReleaseTime: LocalDateTime? = null | var ticketReleaseTime: LocalDateTime? = null | ||||
| // ✅ 新增字段声明 | |||||
| // 新增字段声明 | |||||
| @Column(name = "ticketCompleteDateTime") | @Column(name = "ticketCompleteDateTime") | ||||
| var ticketCompleteDateTime: LocalDateTime? = null | var ticketCompleteDateTime: LocalDateTime? = null | ||||
| @@ -23,13 +23,13 @@ open class DoPickOrderLineRecord: BaseEntity<Long>() { | |||||
| @Column(name = "do_pick_order_id", length = 100) | @Column(name = "do_pick_order_id", length = 100) | ||||
| open var doPickOrderId: Long? = null | open var doPickOrderId: Long? = null | ||||
| @Column(name = "pick_order_id") // ✅ 正确:普通列 | |||||
| @Column(name = "pick_order_id") // 正确:普通列 | |||||
| open var pickOrderId: Long? = null | open var pickOrderId: Long? = null | ||||
| @Column(name = "do_order_id") // ✅ 正确:普通列 | |||||
| @Column(name = "do_order_id") // 正确:普通列 | |||||
| open var doOrderId: Long? = null | open var doOrderId: Long? = null | ||||
| @Column(name = "pick_order_code") // ✅ 正确:普通列 | |||||
| @Column(name = "pick_order_code") // 正确:普通列 | |||||
| open var pickOrderCode: String? = null | open var pickOrderCode: String? = null | ||||
| @Column(name = "delivery_order_code") | @Column(name = "delivery_order_code") | ||||
| @@ -54,7 +54,7 @@ class DoPickOrderRecord { | |||||
| @Column(name = "handled_by") | @Column(name = "handled_by") | ||||
| var handledBy: Long? = null | var handledBy: Long? = null | ||||
| // ✅ 新增字段声明 | |||||
| // 新增字段声明 | |||||
| @Column(name = "ticketCompleteDateTime") | @Column(name = "ticketCompleteDateTime") | ||||
| var ticketCompleteDateTime: LocalDateTime? = null | var ticketCompleteDateTime: LocalDateTime? = null | ||||
| @@ -10,9 +10,11 @@ import org.springframework.data.jpa.repository.Query | |||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| import java.io.Serializable | import java.io.Serializable | ||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
| import java.time.LocalDate | |||||
| @Repository | @Repository | ||||
| interface DoPickOrderRecordRepository : JpaRepository<DoPickOrderRecord, Long> { | interface DoPickOrderRecordRepository : JpaRepository<DoPickOrderRecord, Long> { | ||||
| fun findByPickOrderId(pickOrderId: Long): List<DoPickOrderRecord> | fun findByPickOrderId(pickOrderId: Long): List<DoPickOrderRecord> | ||||
| fun findByTicketNoStartingWith(ticketPrefix: String): List<DoPickOrderRecord> | fun findByTicketNoStartingWith(ticketPrefix: String): List<DoPickOrderRecord> | ||||
| fun findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn(storeId: String, requiredDeliveryDate: LocalDate, ticketStatus: List<DoPickOrderStatus>): List<DoPickOrderRecord> | |||||
| } | } | ||||
| @@ -71,11 +71,11 @@ import com.ffii.fpsms.modules.stock.service.InventoryLotService | |||||
| import net.sf.jasperreports.engine.JasperPrintManager | import net.sf.jasperreports.engine.JasperPrintManager | ||||
| import net.sf.jasperreports.engine.JRPrintPage | import net.sf.jasperreports.engine.JRPrintPage | ||||
| import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository | import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository | ||||
| import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService // ✅ 添加这行 | |||||
| import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService // 添加这行 | |||||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.* | import com.ffii.fpsms.modules.deliveryOrder.web.models.* | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // ✅ 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository // ✅ 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // ✅ 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository // 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus | import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus | ||||
| @Service | @Service | ||||
| open class DeliveryOrderService( | open class DeliveryOrderService( | ||||
| @@ -310,7 +310,7 @@ open class DeliveryOrderService( | |||||
| return null | return null | ||||
| } | } | ||||
| // ✅ 新增方法2:获取 warehouse 的 code 字段(用于显示路由) | |||||
| // 新增方法2:获取 warehouse 的 code 字段(用于显示路由) | |||||
| open fun getWarehouseCodeByItemId(itemId: Long): String? { | open fun getWarehouseCodeByItemId(itemId: Long): String? { | ||||
| val inventoryLots = inventoryLotService.findByItemId(itemId) | val inventoryLots = inventoryLotService.findByItemId(itemId) | ||||
| if (inventoryLots.isNotEmpty()) { | if (inventoryLots.isNotEmpty()) { | ||||
| @@ -451,7 +451,7 @@ open class DeliveryOrderService( | |||||
| pickOrderRepository.saveAndFlush(pickOrderEntity) | pickOrderRepository.saveAndFlush(pickOrderEntity) | ||||
| println("�� DEBUG: Assigned consoCode $consoCode to pick order ${createdPickOrder.id}") | println("�� DEBUG: Assigned consoCode $consoCode to pick order ${createdPickOrder.id}") | ||||
| // ✅ Debug: Check pick order lines | |||||
| // Debug: Check pick order lines | |||||
| println("�� DEBUG: Pick order has ${pickOrderEntity.pickOrderLines?.size ?: 0} pick order lines") | println("�� DEBUG: Pick order has ${pickOrderEntity.pickOrderLines?.size ?: 0} pick order lines") | ||||
| pickOrderEntity.pickOrderLines?.forEach { line -> | pickOrderEntity.pickOrderLines?.forEach { line -> | ||||
| println("🔍 DEBUG: Pick order line - Item ID: ${line.item?.id}, Qty: ${line.qty}") | println("🔍 DEBUG: Pick order line - Item ID: ${line.item?.id}, Qty: ${line.qty}") | ||||
| @@ -470,7 +470,7 @@ open class DeliveryOrderService( | |||||
| println("⚠️ WARNING: $insufficientCount items have insufficient stock (issues auto-created)") | println("⚠️ WARNING: $insufficientCount items have insufficient stock (issues auto-created)") | ||||
| } | } | ||||
| // ✅ Hold inventory quantities | |||||
| // Hold inventory quantities | |||||
| val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn( | val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn( | ||||
| saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id } | saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id } | ||||
| ) | ) | ||||
| @@ -486,7 +486,7 @@ open class DeliveryOrderService( | |||||
| } | } | ||||
| inventoryLotLineRepository.saveAll(inventoryLotLines) | inventoryLotLineRepository.saveAll(inventoryLotLines) | ||||
| // ✅ Create stock out record and pre-create stock out lines | |||||
| // Create stock out record and pre-create stock out lines | |||||
| val stockOut = StockOut().apply { | val stockOut = StockOut().apply { | ||||
| this.type = "do" | this.type = "do" | ||||
| this.consoPickOrderCode = consoCode | this.consoPickOrderCode = consoCode | ||||
| @@ -495,7 +495,7 @@ open class DeliveryOrderService( | |||||
| } | } | ||||
| val savedStockOut = stockOutRepository.saveAndFlush(stockOut) | val savedStockOut = stockOutRepository.saveAndFlush(stockOut) | ||||
| // ✅ Pre-create stock out lines for suggested lots | |||||
| // Pre-create stock out lines for suggested lots | |||||
| saveSuggestedPickLots.forEach { lot -> | saveSuggestedPickLots.forEach { lot -> | ||||
| val polId = lot.pickOrderLine?.id | val polId = lot.pickOrderLine?.id | ||||
| val illId = lot.suggestedLotLine?.id | val illId = lot.suggestedLotLine?.id | ||||
| @@ -522,10 +522,10 @@ open class DeliveryOrderService( | |||||
| } | } | ||||
| } | } | ||||
| // ✅ CREATE do_pick_order_record entries | |||||
| // CREATE do_pick_order_record entries | |||||
| // 第 471-555 行附近 - 修复创建逻辑 | // 第 471-555 行附近 - 修复创建逻辑 | ||||
| // ✅ CREATE do_pick_order_record entries | |||||
| // CREATE do_pick_order_record entries | |||||
| val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now() | val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now() | ||||
| val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")) | val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")) | ||||
| @@ -535,9 +535,9 @@ open class DeliveryOrderService( | |||||
| val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | ||||
| println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") | println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") | ||||
| // ✅ 移除提前返回,总是分析 items 分布 | |||||
| // 移除提前返回,总是分析 items 分布 | |||||
| // 分析 DO order lines 中的 items 分布 | // 分析 DO order lines 中的 items 分布 | ||||
| // ✅ 分析 items 来确定 storeId | |||||
| // 分析 items 来确定 storeId | |||||
| val itemIds = deliveryOrder.deliveryOrderLines.mapNotNull { it.item?.id }.distinct() | val itemIds = deliveryOrder.deliveryOrderLines.mapNotNull { it.item?.id }.distinct() | ||||
| val inventoryQuery = """ | val inventoryQuery = """ | ||||
| SELECT | SELECT | ||||
| @@ -560,7 +560,7 @@ open class DeliveryOrderService( | |||||
| println("🔍 DEBUG: Floor item count distribution: $floorItemCount") | println("🔍 DEBUG: Floor item count distribution: $floorItemCount") | ||||
| println("🔍 DEBUG: Total items: ${itemIds.size}, Items on 4F: ${floorItemCount["4F"] ?: 0}") | println("🔍 DEBUG: Total items: ${itemIds.size}, Items on 4F: ${floorItemCount["4F"] ?: 0}") | ||||
| // ✅ 新逻辑:只有所有 items 都在 4F,才算 4F,否则算 2F | |||||
| // 新逻辑:只有所有 items 都在 4F,才算 4F,否则算 2F | |||||
| val preferredFloor = if ((floorItemCount["4F"] ?: 0) == itemIds.size && (floorItemCount["2F"] ?: 0) == 0) { | val preferredFloor = if ((floorItemCount["4F"] ?: 0) == itemIds.size && (floorItemCount["2F"] ?: 0) == 0) { | ||||
| "4F" // 所有 items 都在 4F | "4F" // 所有 items 都在 4F | ||||
| } else { | } else { | ||||
| @@ -569,7 +569,7 @@ open class DeliveryOrderService( | |||||
| println("🔍 DEBUG: Preferred floor: $preferredFloor (All items on 4F: ${preferredFloor == "4F"})") | println("🔍 DEBUG: Preferred floor: $preferredFloor (All items on 4F: ${preferredFloor == "4F"})") | ||||
| // ✅ 查找 truck | |||||
| // 查找 truck | |||||
| val truck = deliveryOrder.shop?.id?.let { shopId -> | val truck = deliveryOrder.shop?.id?.let { shopId -> | ||||
| println("🔍 DEBUG: Looking for truck with shop ID: $shopId") | println("🔍 DEBUG: Looking for truck with shop ID: $shopId") | ||||
| val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | ||||
| @@ -593,7 +593,7 @@ open class DeliveryOrderService( | |||||
| selectedTruck | selectedTruck | ||||
| } | } | ||||
| // ✅ 检查 truck 和 preferredFloor 是否匹配 | |||||
| // 检查 truck 和 preferredFloor 是否匹配 | |||||
| val truckStoreId = truck?.storeId | val truckStoreId = truck?.storeId | ||||
| val expectedStoreId = when (preferredFloor) { | val expectedStoreId = when (preferredFloor) { | ||||
| "2F" -> 2 | "2F" -> 2 | ||||
| @@ -608,9 +608,9 @@ open class DeliveryOrderService( | |||||
| throw IllegalStateException(errorMsg) // 或返回错误响应 | throw IllegalStateException(errorMsg) // 或返回错误响应 | ||||
| } | } | ||||
| println("✅ DEBUG: Truck matches preferred floor - Truck Store: $truckStoreId, Preferred: $preferredFloor") | |||||
| println(" DEBUG: Truck matches preferred floor - Truck Store: $truckStoreId, Preferred: $preferredFloor") | |||||
| // ✅ storeId 基于 items 的 preferredFloor | |||||
| // storeId 基于 items 的 preferredFloor | |||||
| val storeId = "$preferredFloor/F" | val storeId = "$preferredFloor/F" | ||||
| val loadingSequence = truck.loadingSequence ?: 999 | val loadingSequence = truck.loadingSequence ?: 999 | ||||
| @@ -922,12 +922,12 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo | |||||
| val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.id) | val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.id) | ||||
| ?: throw NoSuchElementException("Delivery Order not found") | ?: throw NoSuchElementException("Delivery Order not found") | ||||
| // ✅ 检查状态,跳过已完成或已发布的DO | |||||
| // 检查状态,跳过已完成或已发布的DO | |||||
| if (deliveryOrder.status == DeliveryOrderStatus.COMPLETED || deliveryOrder.status == DeliveryOrderStatus.RECEIVING) { | if (deliveryOrder.status == DeliveryOrderStatus.COMPLETED || deliveryOrder.status == DeliveryOrderStatus.RECEIVING) { | ||||
| throw IllegalStateException("Delivery Order ${deliveryOrder.id} is already ${deliveryOrder.status?.value}, skipping release") | throw IllegalStateException("Delivery Order ${deliveryOrder.id} is already ${deliveryOrder.status?.value}, skipping release") | ||||
| } | } | ||||
| // ✅ 更新状态为released (使用RECEIVING表示已发布) | |||||
| // 更新状态为released (使用RECEIVING表示已发布) | |||||
| deliveryOrder.apply { | deliveryOrder.apply { | ||||
| status = DeliveryOrderStatus.RECEIVING // 使用RECEIVING表示已发布状态 | status = DeliveryOrderStatus.RECEIVING // 使用RECEIVING表示已发布状态 | ||||
| } | } | ||||
| @@ -1004,7 +1004,7 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo | |||||
| this.pickOrderLine = pickOrderLine | this.pickOrderLine = pickOrderLine | ||||
| this.inventoryLotLine = inventoryLotLine // 可能为 null | this.inventoryLotLine = inventoryLotLine // 可能为 null | ||||
| this.item = pickOrderLine.item | this.item = pickOrderLine.item | ||||
| // ✅ 修复:根据是否有 inventoryLotLine 设置状态 | |||||
| // 修复:根据是否有 inventoryLotLine 设置状态 | |||||
| this.status = if (inventoryLotLine == null) { | this.status = if (inventoryLotLine == null) { | ||||
| StockOutLineStatus.PARTIALLY_COMPLETE.status // 没有库存批次时使用 PARTIALLY_COMPLETE | StockOutLineStatus.PARTIALLY_COMPLETE.status // 没有库存批次时使用 PARTIALLY_COMPLETE | ||||
| } else { | } else { | ||||
| @@ -1046,7 +1046,7 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo | |||||
| "2F" | "2F" | ||||
| } | } | ||||
| // ✅ 查找匹配 preferred floor 的 truck | |||||
| // 查找匹配 preferred floor 的 truck | |||||
| val truck = deliveryOrder.shop?.id?.let { shopId -> | val truck = deliveryOrder.shop?.id?.let { shopId -> | ||||
| val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | ||||
| val preferredStoreId = when (preferredFloor) { | val preferredStoreId = when (preferredFloor) { | ||||
| @@ -1065,14 +1065,14 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 如果没有匹配的 truck,抛出异常跳过 | |||||
| // 如果没有匹配的 truck,抛出异常跳过 | |||||
| if (truck == null) { | if (truck == null) { | ||||
| val errorMsg = "No matching truck found for preferredFloor ($preferredFloor). Skipping DO ${deliveryOrder.id}." | val errorMsg = "No matching truck found for preferredFloor ($preferredFloor). Skipping DO ${deliveryOrder.id}." | ||||
| println("⚠️ $errorMsg") | println("⚠️ $errorMsg") | ||||
| throw IllegalStateException(errorMsg) | throw IllegalStateException(errorMsg) | ||||
| } | } | ||||
| println("✅ DEBUG: Matched truck - ID=${truck.id}, Store=${truck.storeId}, Floor=$preferredFloor") | |||||
| println(" DEBUG: Matched truck - ID=${truck.id}, Store=${truck.storeId}, Floor=$preferredFloor") | |||||
| return ReleaseDoResult( | return ReleaseDoResult( | ||||
| deliveryOrderId = deliveryOrder.id!!, | deliveryOrderId = deliveryOrder.id!!, | ||||
| @@ -1087,7 +1087,7 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo | |||||
| truckId = truck.id, | truckId = truck.id, | ||||
| truckDepartureTime = truck.departureTime, | truckDepartureTime = truck.departureTime, | ||||
| truckLanceCode = truck.truckLanceCode, | truckLanceCode = truck.truckLanceCode, | ||||
| loadingSequence = truck.loadingSequence // ✅ 直接使用 truck 的值 | |||||
| loadingSequence = truck.loadingSequence // 直接使用 truck 的值 | |||||
| ) | ) | ||||
| } | } | ||||
| @@ -20,7 +20,7 @@ class DoPickOrderAssignmentService( | |||||
| private val doPickOrderRecordRepository: DoPickOrderRecordRepository, | private val doPickOrderRecordRepository: DoPickOrderRecordRepository, | ||||
| private val pickOrderRepository: PickOrderRepository, | private val pickOrderRepository: PickOrderRepository, | ||||
| private val userRepository: UserRepository, | private val userRepository: UserRepository, | ||||
| private val jdbcDao: JdbcDao // ✅ 添加 JdbcDao | |||||
| private val jdbcDao: JdbcDao // 添加 JdbcDao | |||||
| ) { | ) { | ||||
| fun assignByLane(request: AssignByLaneRequest): MessageResponse { | fun assignByLane(request: AssignByLaneRequest): MessageResponse { | ||||
| @@ -30,7 +30,7 @@ class DoPickOrderAssignmentService( | |||||
| message = "User not found", errorPosition = null, entity = null | message = "User not found", errorPosition = null, entity = null | ||||
| ) | ) | ||||
| // ✅ 转换 storeId 格式 | |||||
| // 转换 storeId 格式 | |||||
| val actualStoreId = when (request.storeId) { | val actualStoreId = when (request.storeId) { | ||||
| "2/F" -> "2/F" | "2/F" -> "2/F" | ||||
| "4/F" -> "4/F" | "4/F" -> "4/F" | ||||
| @@ -38,11 +38,11 @@ class DoPickOrderAssignmentService( | |||||
| } | } | ||||
| println("🔍 DEBUG: assignByLane - Converting storeId from '${request.storeId}' to '$actualStoreId'") | println("🔍 DEBUG: assignByLane - Converting storeId from '${request.storeId}' to '$actualStoreId'") | ||||
| // ✅ 获取日期(如果提供) | |||||
| // 获取日期(如果提供) | |||||
| val requiredDate = request.requiredDate | val requiredDate = request.requiredDate | ||||
| println("🔍 DEBUG: assignByLane - Requested date: $requiredDate") | println("🔍 DEBUG: assignByLane - Requested date: $requiredDate") | ||||
| // ✅ 根据是否有日期参数选择不同的查询方法 | |||||
| // 根据是否有日期参数选择不同的查询方法 | |||||
| val allCandidates = if (requiredDate != null) { | val allCandidates = if (requiredDate != null) { | ||||
| println("🔍 DEBUG: Filtering by date: $requiredDate") | println("🔍 DEBUG: Filtering by date: $requiredDate") | ||||
| doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | ||||
| @@ -62,7 +62,7 @@ class DoPickOrderAssignmentService( | |||||
| println("🔍 DEBUG: Found ${allCandidates.size} candidate do_pick_orders for lane ${request.truckLanceCode}") | println("🔍 DEBUG: Found ${allCandidates.size} candidate do_pick_orders for lane ${request.truckLanceCode}") | ||||
| // ✅ 过滤掉所有 pick orders 都是 issue 的记录 | |||||
| // 过滤掉所有 pick orders 都是 issue 的记录 | |||||
| val filteredCandidates = allCandidates.filter { doPickOrder -> | val filteredCandidates = allCandidates.filter { doPickOrder -> | ||||
| val hasNonIssueLines = checkDoPickOrderHasNonIssueLines(doPickOrder.id!!) | val hasNonIssueLines = checkDoPickOrderHasNonIssueLines(doPickOrder.id!!) | ||||
| if (!hasNonIssueLines) { | if (!hasNonIssueLines) { | ||||
| @@ -83,14 +83,14 @@ class DoPickOrderAssignmentService( | |||||
| val firstOrder = filteredCandidates.first() | val firstOrder = filteredCandidates.first() | ||||
| // ✅ 更新 do_pick_order | |||||
| // 更新 do_pick_order | |||||
| firstOrder.handledBy = request.userId | firstOrder.handledBy = request.userId | ||||
| firstOrder.handlerName = user.name | firstOrder.handlerName = user.name | ||||
| firstOrder.ticketStatus = DoPickOrderStatus.released | firstOrder.ticketStatus = DoPickOrderStatus.released | ||||
| firstOrder.ticketReleaseTime = LocalDateTime.now() | firstOrder.ticketReleaseTime = LocalDateTime.now() | ||||
| doPickOrderRepository.save(firstOrder) | doPickOrderRepository.save(firstOrder) | ||||
| // ✅ 获取这个 do_pick_order 下的所有 pick orders 并分配给用户 | |||||
| // 获取这个 do_pick_order 下的所有 pick orders 并分配给用户 | |||||
| val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(firstOrder.id!!) | val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(firstOrder.id!!) | ||||
| println("🔍 DEBUG: Found ${doPickOrderLines.size} pick orders in do_pick_order ${firstOrder.id}") | println("🔍 DEBUG: Found ${doPickOrderLines.size} pick orders in do_pick_order ${firstOrder.id}") | ||||
| @@ -108,7 +108,7 @@ class DoPickOrderAssignmentService( | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 同步更新 do_pick_order_record(如果有的话) | |||||
| // 同步更新 do_pick_order_record(如果有的话) | |||||
| doPickOrderLines.forEach { line -> | doPickOrderLines.forEach { line -> | ||||
| if (line.pickOrderId != null) { | if (line.pickOrderId != null) { | ||||
| val records = doPickOrderRecordRepository.findByPickOrderId(line.pickOrderId!!) | val records = doPickOrderRecordRepository.findByPickOrderId(line.pickOrderId!!) | ||||
| @@ -141,7 +141,7 @@ class DoPickOrderAssignmentService( | |||||
| ) | ) | ||||
| } | } | ||||
| // ✅ 添加过滤方法(和 DoPickOrderQueryService 中相同的逻辑) | |||||
| // 添加过滤方法(和 DoPickOrderQueryService 中相同的逻辑) | |||||
| private fun checkDoPickOrderHasNonIssueLines(doPickOrderId: Long): Boolean { | private fun checkDoPickOrderHasNonIssueLines(doPickOrderId: Long): Boolean { | ||||
| return try { | return try { | ||||
| val totalLinesSql = """ | val totalLinesSql = """ | ||||
| @@ -25,14 +25,14 @@ open class DoPickOrderCompletionService( | |||||
| } | } | ||||
| val savedDoPickOrders = doPickOrderRepository.saveAll(doPickOrders) | val savedDoPickOrders = doPickOrderRepository.saveAll(doPickOrders) | ||||
| // ✅ 同步更新相关的delivery_order状态 | |||||
| // 同步更新相关的delivery_order状态 | |||||
| savedDoPickOrders.forEach { doPickOrder -> | savedDoPickOrders.forEach { doPickOrder -> | ||||
| if (doPickOrder.doOrderId != null) { | if (doPickOrder.doOrderId != null) { | ||||
| val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doPickOrder.doOrderId!!) | val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doPickOrder.doOrderId!!) | ||||
| if (deliveryOrder != null && deliveryOrder.status != DeliveryOrderStatus.COMPLETED) { | if (deliveryOrder != null && deliveryOrder.status != DeliveryOrderStatus.COMPLETED) { | ||||
| deliveryOrder.status = DeliveryOrderStatus.COMPLETED | deliveryOrder.status = DeliveryOrderStatus.COMPLETED | ||||
| deliveryOrderRepository.save(deliveryOrder) | deliveryOrderRepository.save(deliveryOrder) | ||||
| println("✅ Updated delivery order ${doPickOrder.doOrderId} status to completed") | |||||
| println(" Updated delivery order ${doPickOrder.doOrderId} status to completed") | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -46,7 +46,7 @@ open class DoPickOrderCompletionService( | |||||
| var deletedCount = 0 | var deletedCount = 0 | ||||
| doPickOrders.forEach { doPickOrder -> | doPickOrders.forEach { doPickOrder -> | ||||
| // ✅ 第一步:复制 do_pick_order 到 do_pick_order_record | |||||
| // 第一步:复制 do_pick_order 到 do_pick_order_record | |||||
| val doPickOrderRecord = DoPickOrderRecord( | val doPickOrderRecord = DoPickOrderRecord( | ||||
| storeId = doPickOrder.storeId ?: "", | storeId = doPickOrder.storeId ?: "", | ||||
| @@ -70,9 +70,9 @@ open class DoPickOrderCompletionService( | |||||
| requiredDeliveryDate = doPickOrder.requiredDeliveryDate | requiredDeliveryDate = doPickOrder.requiredDeliveryDate | ||||
| ) | ) | ||||
| val savedRecord = doPickOrderRecordRepository.save(doPickOrderRecord) | val savedRecord = doPickOrderRecordRepository.save(doPickOrderRecord) | ||||
| println("✅ Copied do_pick_order ${doPickOrder.id} to do_pick_order_record") | |||||
| println(" Copied do_pick_order ${doPickOrder.id} to do_pick_order_record") | |||||
| // ✅ 第二步:复制 do_pick_order_line 到 do_pick_order_line_record | |||||
| // 第二步:复制 do_pick_order_line 到 do_pick_order_line_record | |||||
| val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrder.id!!) | val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrder.id!!) | ||||
| doPickOrderLines.forEach { line -> | doPickOrderLines.forEach { line -> | ||||
| val doPickOrderLineRecord = DoPickOrderLineRecord() | val doPickOrderLineRecord = DoPickOrderLineRecord() | ||||
| @@ -83,19 +83,19 @@ open class DoPickOrderCompletionService( | |||||
| doPickOrderLineRecord.deliveryOrderCode = line.deliveryOrderCode | doPickOrderLineRecord.deliveryOrderCode = line.deliveryOrderCode | ||||
| doPickOrderLineRecord.status = line.status | doPickOrderLineRecord.status = line.status | ||||
| doPickOrderLineRecordRepository.save(doPickOrderLineRecord) | doPickOrderLineRecordRepository.save(doPickOrderLineRecord) | ||||
| println("✅ Copied do_pick_order_line ${line.id} to do_pick_order_line_record") | |||||
| println(" Copied do_pick_order_line ${line.id} to do_pick_order_line_record") | |||||
| } | } | ||||
| // ✅ 第三步:删除原始的 do_pick_order_line 记录 | |||||
| // 第三步:删除原始的 do_pick_order_line 记录 | |||||
| if (doPickOrderLines.isNotEmpty()) { | if (doPickOrderLines.isNotEmpty()) { | ||||
| doPickOrderLineRepository.deleteAll(doPickOrderLines) | doPickOrderLineRepository.deleteAll(doPickOrderLines) | ||||
| println("✅ Deleted ${doPickOrderLines.size} do_pick_order_line records") | |||||
| println(" Deleted ${doPickOrderLines.size} do_pick_order_line records") | |||||
| } | } | ||||
| // ✅ 第四步:删除原始的 do_pick_order 记录 | |||||
| // 第四步:删除原始的 do_pick_order 记录 | |||||
| doPickOrderRepository.delete(doPickOrder) | doPickOrderRepository.delete(doPickOrder) | ||||
| deletedCount++ | deletedCount++ | ||||
| println("✅ Deleted do_pick_order ${doPickOrder.id}") | |||||
| println(" Deleted do_pick_order ${doPickOrder.id}") | |||||
| } | } | ||||
| return deletedCount | return deletedCount | ||||
| @@ -33,7 +33,7 @@ import java.time.LocalDateTime | |||||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.AssignByStoreRequest | import com.ffii.fpsms.modules.deliveryOrder.web.models.AssignByStoreRequest | ||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecord | import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecord | ||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository | import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository | ||||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.* // ✅ 导入 | |||||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.* // 导入 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | ||||
| import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus | import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository | import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository | ||||
| @@ -52,7 +52,7 @@ open class DoPickOrderService( | |||||
| private val doPickOrderRecordRepository: DoPickOrderRecordRepository, | private val doPickOrderRecordRepository: DoPickOrderRecordRepository, | ||||
| private val userRepository: UserRepository, | private val userRepository: UserRepository, | ||||
| private val pickOrderRepository: PickOrderRepository, | private val pickOrderRepository: PickOrderRepository, | ||||
| private val jdbcDao: JdbcDao, // ✅ 添加这行 | |||||
| private val jdbcDao: JdbcDao, // 添加这行 | |||||
| private val truckRepository: TruckRepository, | private val truckRepository: TruckRepository, | ||||
| private val doPickOrderLineRepository: DoPickOrderLineRepository, | private val doPickOrderLineRepository: DoPickOrderLineRepository, | ||||
| @Lazy private val deliveryOrderRepository: DeliveryOrderRepository, | @Lazy private val deliveryOrderRepository: DeliveryOrderRepository, | ||||
| @@ -73,7 +73,7 @@ open class DoPickOrderService( | |||||
| } else { | } else { | ||||
| datePrefix | datePrefix | ||||
| } | } | ||||
| // ✅ 修改搜索模式为新格式 | |||||
| // 修改搜索模式为新格式 | |||||
| val searchPattern = "TI-${shortDatePrefix}-${sanitizedStoreId}-" // T-20250915-4F- | val searchPattern = "TI-${shortDatePrefix}-${sanitizedStoreId}-" // T-20250915-4F- | ||||
| val todayTickets = doPickOrderRepository.findByTicketNoStartingWith(searchPattern) | val todayTickets = doPickOrderRepository.findByTicketNoStartingWith(searchPattern) | ||||
| println("🔍 DEBUG: Found ${todayTickets.size} existing tickets with prefix $searchPattern") | println("🔍 DEBUG: Found ${todayTickets.size} existing tickets with prefix $searchPattern") | ||||
| @@ -81,7 +81,7 @@ open class DoPickOrderService( | |||||
| println("🔍 DEBUG: Existing ticket: ${ticket.ticketNo}, Status: ${ticket.ticketStatus}") | println("🔍 DEBUG: Existing ticket: ${ticket.ticketNo}, Status: ${ticket.ticketStatus}") | ||||
| } | } | ||||
| val nextNumber = (todayTickets.size + 1).toString().padStart(3, '0') | val nextNumber = (todayTickets.size + 1).toString().padStart(3, '0') | ||||
| // ✅ 修改生成格式 | |||||
| // 修改生成格式 | |||||
| val ticketNumber = "TI-${datePrefix}-${sanitizedStoreId}-${nextNumber}" // T-20250915-4F-001 | val ticketNumber = "TI-${datePrefix}-${sanitizedStoreId}-${nextNumber}" // T-20250915-4F-001 | ||||
| println("🔍 DEBUG: Generated ticket number: $ticketNumber") | println("🔍 DEBUG: Generated ticket number: $ticketNumber") | ||||
| return ticketNumber | return ticketNumber | ||||
| @@ -129,10 +129,10 @@ open class DoPickOrderService( | |||||
| ) | ) | ||||
| } | } | ||||
| // ✅ Updated method to set ticketReleaseTime when assigning order to user | |||||
| // Updated method to set ticketReleaseTime when assigning order to user | |||||
| open fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrder> { | open fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrder> { | ||||
| val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | ||||
| val user = userRepository.findById(userId).orElse(null) // ✅ 改用 orElse(null) | |||||
| val user = userRepository.findById(userId).orElse(null) // 改用 orElse(null) | |||||
| val handlerName = user?.name ?: "Unknown" | val handlerName = user?.name ?: "Unknown" | ||||
| doPickOrders.forEach { | doPickOrders.forEach { | ||||
| it.handledBy = userId | it.handledBy = userId | ||||
| @@ -151,14 +151,14 @@ open class DoPickOrderService( | |||||
| } | } | ||||
| val savedDoPickOrders = doPickOrderRepository.saveAll(doPickOrders) | val savedDoPickOrders = doPickOrderRepository.saveAll(doPickOrders) | ||||
| // ✅ 同步更新相关的delivery_order状态 | |||||
| // 同步更新相关的delivery_order状态 | |||||
| savedDoPickOrders.forEach { doPickOrder -> | savedDoPickOrders.forEach { doPickOrder -> | ||||
| if (doPickOrder.doOrderId != null) { | if (doPickOrder.doOrderId != null) { | ||||
| val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doPickOrder.doOrderId!!) | val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doPickOrder.doOrderId!!) | ||||
| if (deliveryOrder != null && deliveryOrder.status != DeliveryOrderStatus.COMPLETED) { | if (deliveryOrder != null && deliveryOrder.status != DeliveryOrderStatus.COMPLETED) { | ||||
| deliveryOrder.status = DeliveryOrderStatus.COMPLETED | deliveryOrder.status = DeliveryOrderStatus.COMPLETED | ||||
| deliveryOrderRepository.save(deliveryOrder) | deliveryOrderRepository.save(deliveryOrder) | ||||
| println("✅ Updated delivery order ${doPickOrder.doOrderId} status to completed") | |||||
| println(" Updated delivery order ${doPickOrder.doOrderId} status to completed") | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -166,15 +166,15 @@ open class DoPickOrderService( | |||||
| return savedDoPickOrders | return savedDoPickOrders | ||||
| } | } | ||||
| // ✅ New method to remove do_pick_order records when auto-assigning by store | |||||
| // ✅ 修改方法:先复制记录到record表,再删除原记录 | |||||
| // New method to remove do_pick_order records when auto-assigning by store | |||||
| // 修改方法:先复制记录到record表,再删除原记录 | |||||
| @Transactional | @Transactional | ||||
| open fun removeDoPickOrdersForPickOrder(pickOrderId: Long): Int { | open fun removeDoPickOrdersForPickOrder(pickOrderId: Long): Int { | ||||
| val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | ||||
| var deletedCount = 0 | var deletedCount = 0 | ||||
| doPickOrders.forEach { doPickOrder -> | doPickOrders.forEach { doPickOrder -> | ||||
| // ✅ 第一步:复制 do_pick_order 到 do_pick_order_record | |||||
| // 第一步:复制 do_pick_order 到 do_pick_order_record | |||||
| val doPickOrderRecord = DoPickOrderRecord( | val doPickOrderRecord = DoPickOrderRecord( | ||||
| recordId = doPickOrder.id, | recordId = doPickOrder.id, | ||||
| storeId = doPickOrder.storeId?: "", | storeId = doPickOrder.storeId?: "", | ||||
| @@ -198,34 +198,34 @@ open class DoPickOrderService( | |||||
| requiredDeliveryDate = doPickOrder.requiredDeliveryDate | requiredDeliveryDate = doPickOrder.requiredDeliveryDate | ||||
| ) | ) | ||||
| val savedRecord = doPickOrderRecordRepository.save(doPickOrderRecord) | val savedRecord = doPickOrderRecordRepository.save(doPickOrderRecord) | ||||
| println("✅ Copied do_pick_order ${doPickOrder.id} to do_pick_order_record") | |||||
| println(" Copied do_pick_order ${doPickOrder.id} to do_pick_order_record") | |||||
| // ✅ 第二步:复制 do_pick_order_line 到 do_pick_order_line_record | |||||
| // ✅ 第二步:复制 do_pick_order_line 到 do_pick_order_line_record | |||||
| // 第二步:复制 do_pick_order_line 到 do_pick_order_line_record | |||||
| // 第二步:复制 do_pick_order_line 到 do_pick_order_line_record | |||||
| val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrder.id!!) | val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrder.id!!) | ||||
| doPickOrderLines.forEach { line -> | doPickOrderLines.forEach { line -> | ||||
| val doPickOrderLineRecord = DoPickOrderLineRecord() | val doPickOrderLineRecord = DoPickOrderLineRecord() | ||||
| doPickOrderLineRecord.recordId = line.id // ✅ 使用默认构造函数 | |||||
| doPickOrderLineRecord.doPickOrderId = savedRecord.id // ✅ 设置属性 | |||||
| doPickOrderLineRecord.recordId = line.id // 使用默认构造函数 | |||||
| doPickOrderLineRecord.doPickOrderId = savedRecord.id // 设置属性 | |||||
| doPickOrderLineRecord.pickOrderId = line.pickOrderId | doPickOrderLineRecord.pickOrderId = line.pickOrderId | ||||
| doPickOrderLineRecord.doOrderId = line.doOrderId | doPickOrderLineRecord.doOrderId = line.doOrderId | ||||
| doPickOrderLineRecord.pickOrderCode = line.pickOrderCode | doPickOrderLineRecord.pickOrderCode = line.pickOrderCode | ||||
| doPickOrderLineRecord.deliveryOrderCode = line.deliveryOrderCode | doPickOrderLineRecord.deliveryOrderCode = line.deliveryOrderCode | ||||
| doPickOrderLineRecord.status = line.status | doPickOrderLineRecord.status = line.status | ||||
| doPickOrderLineRecordRepository.save(doPickOrderLineRecord) | doPickOrderLineRecordRepository.save(doPickOrderLineRecord) | ||||
| println("✅ Copied do_pick_order_line ${line.id} to do_pick_order_line_record") | |||||
| println(" Copied do_pick_order_line ${line.id} to do_pick_order_line_record") | |||||
| } | } | ||||
| // ✅ 第三步:删除原始的 do_pick_order_line 记录 | |||||
| // 第三步:删除原始的 do_pick_order_line 记录 | |||||
| if (doPickOrderLines.isNotEmpty()) { | if (doPickOrderLines.isNotEmpty()) { | ||||
| doPickOrderLineRepository.deleteAll(doPickOrderLines) | doPickOrderLineRepository.deleteAll(doPickOrderLines) | ||||
| println("✅ Deleted ${doPickOrderLines.size} do_pick_order_line records") | |||||
| println(" Deleted ${doPickOrderLines.size} do_pick_order_line records") | |||||
| } | } | ||||
| // ✅ 第四步:删除原始的 do_pick_order 记录 | |||||
| // 第四步:删除原始的 do_pick_order 记录 | |||||
| doPickOrderRepository.delete(doPickOrder) | doPickOrderRepository.delete(doPickOrder) | ||||
| deletedCount++ | deletedCount++ | ||||
| println("✅ Deleted do_pick_order ${doPickOrder.id}") | |||||
| println(" Deleted do_pick_order ${doPickOrder.id}") | |||||
| } | } | ||||
| return deletedCount | return deletedCount | ||||
| @@ -235,10 +235,10 @@ open class DoPickOrderService( | |||||
| return doPickOrderRecordRepository.save(record) | return doPickOrderRecordRepository.save(record) | ||||
| } | } | ||||
| // ✅ Add method to update DoPickOrderRecord status | |||||
| // Add method to update DoPickOrderRecord status | |||||
| open fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrderRecord> { | open fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrderRecord> { | ||||
| val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) | val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) | ||||
| val user = userRepository.findById(userId).orElse(null) // ✅ 改用 orElse(null) | |||||
| val user = userRepository.findById(userId).orElse(null) // 改用 orElse(null) | |||||
| val handlerName = user?.name ?: "Unknown" | val handlerName = user?.name ?: "Unknown" | ||||
| doPickOrderRecords.forEach { | doPickOrderRecords.forEach { | ||||
| it.handledBy = userId | it.handledBy = userId | ||||
| @@ -249,12 +249,12 @@ open class DoPickOrderService( | |||||
| return doPickOrderRecordRepository.saveAll(doPickOrderRecords) | return doPickOrderRecordRepository.saveAll(doPickOrderRecords) | ||||
| } | } | ||||
| // ✅ Add method to complete DoPickOrderRecord | |||||
| // Add method to complete DoPickOrderRecord | |||||
| open fun completeDoPickOrderRecordsForPickOrder(pickOrderId: Long): List<DoPickOrderRecord> { | open fun completeDoPickOrderRecordsForPickOrder(pickOrderId: Long): List<DoPickOrderRecord> { | ||||
| val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) | val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) | ||||
| doPickOrderRecords.forEach { | doPickOrderRecords.forEach { | ||||
| it.ticketStatus = DoPickOrderStatus.completed | it.ticketStatus = DoPickOrderStatus.completed | ||||
| it.ticketCompleteDateTime = LocalDateTime.now() // ✅ 设置完成时间 | |||||
| it.ticketCompleteDateTime = LocalDateTime.now() // 设置完成时间 | |||||
| } | } | ||||
| return doPickOrderRecordRepository.saveAll(doPickOrderRecords) | return doPickOrderRecordRepository.saveAll(doPickOrderRecords) | ||||
| } | } | ||||
| @@ -271,29 +271,32 @@ open class DoPickOrderService( | |||||
| } | } | ||||
| return doPickOrderRepository.saveAll(doPickOrders) | return doPickOrderRepository.saveAll(doPickOrders) | ||||
| } | } | ||||
| open fun getSummaryByStore(storeId: String, requiredDate: LocalDate?): StoreLaneSummary { | |||||
| fun getSummaryByStore(storeId: String, requiredDate: LocalDate?): StoreLaneSummary { | |||||
| val targetDate = requiredDate ?: LocalDate.now() | val targetDate = requiredDate ?: LocalDate.now() | ||||
| println("🔍 DEBUG: Getting summary for store=$storeId, date=$targetDate") | println("🔍 DEBUG: Getting summary for store=$storeId, date=$targetDate") | ||||
| // ✅ 修复格式转换:保持原始格式 | |||||
| val actualStoreId = when (storeId) { | val actualStoreId = when (storeId) { | ||||
| "2/F" -> "2/F" // ✅ 保持原格式 | |||||
| "4/F" -> "4/F" // ✅ 保持原格式 | |||||
| "2/F" -> "2/F" | |||||
| "4/F" -> "4/F" | |||||
| else -> storeId | else -> storeId | ||||
| } | } | ||||
| println("🔍 DEBUG: Using storeId: '$actualStoreId'") | |||||
| // ✅ 直接查询 do_pick_order 表 | |||||
| val allRecords = doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | val allRecords = doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | ||||
| actualStoreId, | actualStoreId, | ||||
| targetDate, | targetDate, | ||||
| listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) | listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) | ||||
| ) | ) | ||||
| // 添加 finishedRecords 查询 | |||||
| val finishedRecords = doPickOrderRecordRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | |||||
| actualStoreId, | |||||
| targetDate, | |||||
| listOf(DoPickOrderStatus.completed) | |||||
| ) | |||||
| println("🔍 DEBUG: Found ${allRecords.size} records for date $targetDate") | println("🔍 DEBUG: Found ${allRecords.size} records for date $targetDate") | ||||
| println("🔍 DEBUG: Found ${finishedRecords.size} finished records for date $targetDate") | |||||
| // ✅ 过滤掉所有 do_pick_order_line 都是 "issue" 状态的 shop | |||||
| /* | |||||
| val filteredRecords = allRecords.filter { doPickOrder -> | val filteredRecords = allRecords.filter { doPickOrder -> | ||||
| val hasNonIssueLines = checkDoPickOrderHasNonIssueLines(doPickOrder.id!!) | val hasNonIssueLines = checkDoPickOrderHasNonIssueLines(doPickOrder.id!!) | ||||
| if (!hasNonIssueLines) { | if (!hasNonIssueLines) { | ||||
| @@ -301,19 +304,27 @@ open class DoPickOrderService( | |||||
| } | } | ||||
| hasNonIssueLines | hasNonIssueLines | ||||
| } | } | ||||
| */ | |||||
| // println("🔍 DEBUG: After filtering, ${filteredRecords.size} records remain") | |||||
| // ✅ 使用过滤后的记录 | |||||
| val grouped = allRecords.groupBy { it.truckDepartureTime to it.truckLanceCode } | |||||
| .mapValues { (_, list) -> | |||||
| println("🔍 DEBUG: After filtering, ${filteredRecords.size} records remain") | |||||
| val grouped = filteredRecords.groupBy { it.truckDepartureTime to it.truckLanceCode } | |||||
| .mapValues { (key, list) -> | |||||
| val (truckDepartureTime, truckLanceCode) = key | |||||
| // 计算匹配的 finishedRecords 数量 | |||||
| val matchingFinishedCount = finishedRecords.count { record -> | |||||
| (record.truckDepartureTime == truckDepartureTime) && | |||||
| (record.truckLanceCode == truckLanceCode) | |||||
| } | |||||
| println("🔍 DEBUG: Group key - truckDepartureTime: $truckDepartureTime, truckLanceCode: $truckLanceCode") | |||||
| println("🔍 DEBUG: Found ${list.size} active records in this group") | |||||
| println("🔍 DEBUG: Found $matchingFinishedCount finished records matching this group") | |||||
| LaneBtn( | LaneBtn( | ||||
| truckLanceCode = list.first().truckLanceCode ?: "", | truckLanceCode = list.first().truckLanceCode ?: "", | ||||
| unassigned = list.count { it.handledBy == null }, | unassigned = list.count { it.handledBy == null }, | ||||
| total = list.size | |||||
| total = list.size + matchingFinishedCount // 添加 finishedRecords 计数 | |||||
| ) | ) | ||||
| } | } | ||||
| val timeGroups = grouped.entries | val timeGroups = grouped.entries | ||||
| .groupBy { it.key.first } | .groupBy { it.key.first } | ||||
| .mapValues { (_, entries) -> | .mapValues { (_, entries) -> | ||||
| @@ -330,7 +341,7 @@ open class DoPickOrderService( | |||||
| lanes = lanes | lanes = lanes | ||||
| ) | ) | ||||
| } | } | ||||
| return StoreLaneSummary(storeId = storeId, rows = timeGroups) | return StoreLaneSummary(storeId = storeId, rows = timeGroups) | ||||
| } | } | ||||
| private fun checkDoPickOrderHasNonIssueLines(doPickOrderId: Long): Boolean { | private fun checkDoPickOrderHasNonIssueLines(doPickOrderId: Long): Boolean { | ||||
| @@ -374,7 +385,7 @@ open class DoPickOrderService( | |||||
| return true // 出错时不过滤 | return true // 出错时不过滤 | ||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:把 assignByLane 移到类里面 | |||||
| // 修复:把 assignByLane 移到类里面 | |||||
| open fun assignByLane(request: AssignByLaneRequest): MessageResponse { | open fun assignByLane(request: AssignByLaneRequest): MessageResponse { | ||||
| val user = userRepository.findById(request.userId).orElse(null) | val user = userRepository.findById(request.userId).orElse(null) | ||||
| ?: return MessageResponse( | ?: return MessageResponse( | ||||
| @@ -382,10 +393,10 @@ open class DoPickOrderService( | |||||
| message = "User not found", errorPosition = null, entity = null | message = "User not found", errorPosition = null, entity = null | ||||
| ) | ) | ||||
| // ✅ 转换 storeId 格式:'2/F' -> '2F/F', '4/F' -> '4F/F' | |||||
| // 转换 storeId 格式:'2/F' -> '2F/F', '4/F' -> '4F/F' | |||||
| val actualStoreId = when (request.storeId) { | val actualStoreId = when (request.storeId) { | ||||
| "2/F" -> "2/F" // ✅ 保持原格式 | |||||
| "4/F" -> "4/F" // ✅ 保持原格式 | |||||
| "2/F" -> "2/F" // 保持原格式 | |||||
| "4/F" -> "4/F" // 保持原格式 | |||||
| else -> request.storeId | else -> request.storeId | ||||
| } | } | ||||
| println("🔍 DEBUG: assignByLane - Converting storeId from '${request.storeId}' to '$actualStoreId'") | println("🔍 DEBUG: assignByLane - Converting storeId from '${request.storeId}' to '$actualStoreId'") | ||||
| @@ -406,14 +417,14 @@ open class DoPickOrderService( | |||||
| val firstOrder = candidates.first() | val firstOrder = candidates.first() | ||||
| // ✅ 更新 do_pick_order | |||||
| // 更新 do_pick_order | |||||
| firstOrder.handledBy = request.userId | firstOrder.handledBy = request.userId | ||||
| firstOrder.handlerName = user.name | firstOrder.handlerName = user.name | ||||
| firstOrder.ticketStatus = DoPickOrderStatus.released | firstOrder.ticketStatus = DoPickOrderStatus.released | ||||
| firstOrder.ticketReleaseTime = LocalDateTime.now() | firstOrder.ticketReleaseTime = LocalDateTime.now() | ||||
| doPickOrderRepository.save(firstOrder) | doPickOrderRepository.save(firstOrder) | ||||
| // ✅ 关键修改:获取这个 do_pick_order 下的所有 pick orders 并分配给用户 | |||||
| // 关键修改:获取这个 do_pick_order 下的所有 pick orders 并分配给用户 | |||||
| val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(firstOrder.id!!) | val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(firstOrder.id!!) | ||||
| println("🔍 DEBUG: Found ${doPickOrderLines.size} pick orders in do_pick_order ${firstOrder.id}") | println("🔍 DEBUG: Found ${doPickOrderLines.size} pick orders in do_pick_order ${firstOrder.id}") | ||||
| @@ -431,7 +442,7 @@ open class DoPickOrderService( | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 同步更新 do_pick_order_record(如果有的话) | |||||
| // 同步更新 do_pick_order_record(如果有的话) | |||||
| doPickOrderLines.forEach { line -> | doPickOrderLines.forEach { line -> | ||||
| if (line.pickOrderId != null) { | if (line.pickOrderId != null) { | ||||
| val records = doPickOrderRecordRepository.findByPickOrderId(line.pickOrderId!!) | val records = doPickOrderRecordRepository.findByPickOrderId(line.pickOrderId!!) | ||||
| @@ -588,4 +599,4 @@ open class DoPickOrderService( | |||||
| open fun getTicketReleaseTable(): List<DoPickOrder>{ | open fun getTicketReleaseTable(): List<DoPickOrder>{ | ||||
| return doPickOrderRepository.findAllByDeletedFalseOrderByTicketReleaseTimeDesc() | return doPickOrderRepository.findAllByDeletedFalseOrderByTicketReleaseTimeDesc() | ||||
| } | } | ||||
| }// ✅ 类结束 | |||||
| }// 类结束 | |||||
| @@ -183,7 +183,7 @@ class DoReleaseCoordinatorService( | |||||
| """.trimIndent() | """.trimIndent() | ||||
| val rowsUpdated = jdbcDao.executeUpdate(updateSql, emptyMap<String, Any>()) | val rowsUpdated = jdbcDao.executeUpdate(updateSql, emptyMap<String, Any>()) | ||||
| println("✅ Updated $rowsUpdated ticket numbers") | |||||
| println(" Updated $rowsUpdated ticket numbers") | |||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("❌ Error updating ticket numbers: ${e.message}") | println("❌ Error updating ticket numbers: ${e.message}") | ||||
| e.printStackTrace() | e.printStackTrace() | ||||
| @@ -332,16 +332,16 @@ class DoReleaseCoordinatorService( | |||||
| """.trimIndent() | """.trimIndent() | ||||
| println("🔍 DEBUG: SQL length: ${sql.length} characters") // ✅ 添加这行 | |||||
| println("🔍 DEBUG: SQL first 500 chars: ${sql.take(500)}") // ✅ 添加这行 | |||||
| println("🔍 DEBUG: SQL length: ${sql.length} characters") // 添加这行 | |||||
| println("🔍 DEBUG: SQL first 500 chars: ${sql.take(500)}") // 添加这行 | |||||
| val results = jdbcDao.queryForList(sql) | val results = jdbcDao.queryForList(sql) | ||||
| println("🔍 DEBUG: Results type: ${results.javaClass.name}") // ✅ 添加这行 | |||||
| println("🔍 DEBUG: Results size: ${results.size}") // ✅ 添加这行 | |||||
| println("🔍 DEBUG: Results type: ${results.javaClass.name}") // 添加这行 | |||||
| println("🔍 DEBUG: Results size: ${results.size}") // 添加这行 | |||||
| if (results.isNotEmpty()) { | if (results.isNotEmpty()) { | ||||
| println("🔍 DEBUG: First result keys: ${results[0].keys}") // ✅ 添加这行 | |||||
| println("🔍 DEBUG: First result: ${results[0]}") // ✅ 添加这行 | |||||
| println("🔍 DEBUG: First result keys: ${results[0].keys}") // 添加这行 | |||||
| println("🔍 DEBUG: First result: ${results[0]}") // 添加这行 | |||||
| } | } | ||||
| val sortedIds = results.mapNotNull { row -> | val sortedIds = results.mapNotNull { row -> | ||||
| @@ -359,7 +359,7 @@ class DoReleaseCoordinatorService( | |||||
| } | } | ||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("❌ ERROR: ${e.message}") | println("❌ ERROR: ${e.message}") | ||||
| println("❌ ERROR Stack Trace:") // ✅ 添加这行 | |||||
| println("❌ ERROR Stack Trace:") // 添加这行 | |||||
| e.printStackTrace() | e.printStackTrace() | ||||
| return ids | return ids | ||||
| } | } | ||||
| @@ -407,7 +407,7 @@ class DoReleaseCoordinatorService( | |||||
| } | } | ||||
| println("❌ DO $id skipped: ${e.message}") | println("❌ DO $id skipped: ${e.message}") | ||||
| // ✅ 调用 PickExecutionIssueService 创建 issue 记录 | |||||
| // 调用 PickExecutionIssueService 创建 issue 记录 | |||||
| try { | try { | ||||
| val issueCategory = when { | val issueCategory = when { | ||||
| e.message?.contains("Unable to find") == true && | e.message?.contains("Unable to find") == true && | ||||
| @@ -430,9 +430,9 @@ class DoReleaseCoordinatorService( | |||||
| println("⚠️ Failed to create issue for DO $id: ${issueException.message}") | println("⚠️ Failed to create issue for DO $id: ${issueException.message}") | ||||
| } | } | ||||
| } | } | ||||
| } // ✅ forEach 循环在这里结束 | |||||
| } // forEach 循环在这里结束 | |||||
| // ✅ 第二步:按日期、楼层、店铺分组(在 forEach 之后) | |||||
| // 第二步:按日期、楼层、店铺分组(在 forEach 之后) | |||||
| val sortedResults = releaseResults.sortedWith( | val sortedResults = releaseResults.sortedWith( | ||||
| compareBy( | compareBy( | ||||
| { it.estimatedArrivalDate }, | { it.estimatedArrivalDate }, | ||||
| @@ -444,7 +444,7 @@ class DoReleaseCoordinatorService( | |||||
| ) | ) | ||||
| ) | ) | ||||
| // ✅ 然后按正确的顺序分组(保持排序后的顺序) | |||||
| // 然后按正确的顺序分组(保持排序后的顺序) | |||||
| val grouped = sortedResults.groupBy { | val grouped = sortedResults.groupBy { | ||||
| Triple(it.estimatedArrivalDate, it.preferredFloor, it.shopId) | Triple(it.estimatedArrivalDate, it.preferredFloor, it.shopId) | ||||
| } | } | ||||
| @@ -468,7 +468,7 @@ class DoReleaseCoordinatorService( | |||||
| updateBatchTicketNumbers() | updateBatchTicketNumbers() | ||||
| } | } | ||||
| println("✅ Batch completed: ${status.success.get()} success, ${status.failed.size} failed") | |||||
| println(" Batch completed: ${status.success.get()} success, ${status.failed.size} failed") | |||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("❌ Batch release exception: ${e.javaClass.simpleName} - ${e.message}") | println("❌ Batch release exception: ${e.javaClass.simpleName} - ${e.message}") | ||||
| @@ -511,7 +511,7 @@ class DoReleaseCoordinatorService( | |||||
| requiredDeliveryDate = first.estimatedArrivalDate | requiredDeliveryDate = first.estimatedArrivalDate | ||||
| ) | ) | ||||
| // ✅ 直接使用 doPickOrderRepository.save() 而不是 doPickOrderService.save() | |||||
| // 直接使用 doPickOrderRepository.save() 而不是 doPickOrderService.save() | |||||
| val saved = doPickOrderRepository.save(doPickOrder) | val saved = doPickOrderRepository.save(doPickOrder) | ||||
| println("🔍 DEBUG: Saved DoPickOrder - ID: ${saved.id}, Ticket: ${saved.ticketNo}") | println("🔍 DEBUG: Saved DoPickOrder - ID: ${saved.id}, Ticket: ${saved.ticketNo}") | ||||
| @@ -524,7 +524,7 @@ class DoReleaseCoordinatorService( | |||||
| return@forEach // 跳过这个 | return@forEach // 跳过这个 | ||||
| } | } | ||||
| // ✅ 先创建 DoPickOrderLine,然后检查库存问题 | |||||
| // 先创建 DoPickOrderLine,然后检查库存问题 | |||||
| val line = DoPickOrderLine().apply { | val line = DoPickOrderLine().apply { | ||||
| doPickOrderId = saved.id | doPickOrderId = saved.id | ||||
| pickOrderId = result.pickOrderId | pickOrderId = result.pickOrderId | ||||
| @@ -537,7 +537,7 @@ class DoReleaseCoordinatorService( | |||||
| println("🔍 DEBUG: Created DoPickOrderLine for pick order ${result.pickOrderId}") | println("🔍 DEBUG: Created DoPickOrderLine for pick order ${result.pickOrderId}") | ||||
| } | } | ||||
| // ✅ 现在检查整个 DoPickOrder 是否有库存问题 | |||||
| // 现在检查整个 DoPickOrder 是否有库存问题 | |||||
| val hasStockIssues = checkPickOrderHasStockIssues(saved.id!!) | val hasStockIssues = checkPickOrderHasStockIssues(saved.id!!) | ||||
| if (hasStockIssues) { | if (hasStockIssues) { | ||||
| // 更新所有相关的 DoPickOrderLine 状态为 "issue" | // 更新所有相关的 DoPickOrderLine 状态为 "issue" | ||||
| @@ -180,7 +180,7 @@ class DeliveryOrderController( | |||||
| @PostMapping("/release") | @PostMapping("/release") | ||||
| fun releaseDeliveryOrder(@RequestBody request: ReleaseDoRequest): MessageResponse { | fun releaseDeliveryOrder(@RequestBody request: ReleaseDoRequest): MessageResponse { | ||||
| // ✅ Simply pass the request directly - userId comes from frontend session | |||||
| // Simply pass the request directly - userId comes from frontend session | |||||
| return deliveryOrderService.releaseDeliveryOrder(request) | return deliveryOrderService.releaseDeliveryOrder(request) | ||||
| } | } | ||||
| @@ -75,7 +75,7 @@ class DoPickOrderController( | |||||
| } | } | ||||
| @PostMapping("/assign-by-lane") | @PostMapping("/assign-by-lane") | ||||
| fun assignByLane(@RequestBody request: AssignByLaneRequest): MessageResponse { | fun assignByLane(@RequestBody request: AssignByLaneRequest): MessageResponse { | ||||
| return doPickOrderAssignmentService.assignByLane(request) // ✅ 使用新的 Service | |||||
| return doPickOrderAssignmentService.assignByLane(request) // 使用新的 Service | |||||
| } | } | ||||
| @PostMapping("/batch-release/async") | @PostMapping("/batch-release/async") | ||||
| @@ -44,7 +44,7 @@ open class JoPickOrderService( | |||||
| return joPickOrderRecordRepository.save(record) | return joPickOrderRecordRepository.save(record) | ||||
| } | } | ||||
| // ✅ Update JoPickOrder status to released | |||||
| // Update JoPickOrder status to released | |||||
| open fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<JoPickOrder> { | open fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<JoPickOrder> { | ||||
| val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId) | val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId) | ||||
| @@ -68,7 +68,7 @@ open class JoPickOrderService( | |||||
| } | } | ||||
| return joPickOrderRepository.saveAll(joPickOrders) | return joPickOrderRepository.saveAll(joPickOrders) | ||||
| } | } | ||||
| // ✅ Complete JoPickOrder | |||||
| // Complete JoPickOrder | |||||
| open fun completeJoPickOrdersForPickOrder(pickOrderId: Long): List<JoPickOrder> { | open fun completeJoPickOrdersForPickOrder(pickOrderId: Long): List<JoPickOrder> { | ||||
| val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId) | val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId) | ||||
| joPickOrders.forEach { | joPickOrders.forEach { | ||||
| @@ -78,7 +78,7 @@ open class JoPickOrderService( | |||||
| return joPickOrderRepository.saveAll(joPickOrders) | return joPickOrderRepository.saveAll(joPickOrders) | ||||
| } | } | ||||
| // ✅ Update JoPickOrderRecord status to released | |||||
| // Update JoPickOrderRecord status to released | |||||
| open fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List<JoPickOrderRecord> { | open fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List<JoPickOrderRecord> { | ||||
| val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId) | val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId) | ||||
| @@ -103,7 +103,7 @@ open class JoPickOrderService( | |||||
| return joPickOrderRecordRepository.saveAll(joPickOrderRecords) | return joPickOrderRecordRepository.saveAll(joPickOrderRecords) | ||||
| } | } | ||||
| // ✅ Complete JoPickOrderRecord | |||||
| // Complete JoPickOrderRecord | |||||
| open fun completeJoPickOrderRecordsForPickOrder(pickOrderId: Long): List<JoPickOrderRecord> { | open fun completeJoPickOrderRecordsForPickOrder(pickOrderId: Long): List<JoPickOrderRecord> { | ||||
| val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId) | val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId) | ||||
| joPickOrderRecords.forEach { | joPickOrderRecords.forEach { | ||||
| @@ -113,17 +113,17 @@ open class JoPickOrderService( | |||||
| return joPickOrderRecordRepository.saveAll(joPickOrderRecords) | return joPickOrderRecordRepository.saveAll(joPickOrderRecords) | ||||
| } | } | ||||
| // ✅ Find JoPickOrder records by pick order ID | |||||
| // Find JoPickOrder records by pick order ID | |||||
| open fun findByPickOrderId(pickOrderId: Long): List<JoPickOrder> { | open fun findByPickOrderId(pickOrderId: Long): List<JoPickOrder> { | ||||
| return joPickOrderRepository.findByPickOrderId(pickOrderId) | return joPickOrderRepository.findByPickOrderId(pickOrderId) | ||||
| } | } | ||||
| // ✅ Find JoPickOrderRecord records by pick order ID | |||||
| // Find JoPickOrderRecord records by pick order ID | |||||
| open fun findRecordsByPickOrderId(pickOrderId: Long): List<JoPickOrderRecord> { | open fun findRecordsByPickOrderId(pickOrderId: Long): List<JoPickOrderRecord> { | ||||
| return joPickOrderRecordRepository.findByPickOrderId(pickOrderId) | return joPickOrderRecordRepository.findByPickOrderId(pickOrderId) | ||||
| } | } | ||||
| // ✅ Create JoPickOrder records for a pick order | |||||
| // Create JoPickOrder records for a pick order | |||||
| @Transactional | @Transactional | ||||
| open fun createJoPickOrdersForPickOrder(pickOrderId: Long, itemIds: List<Long>): List<JoPickOrder> { | open fun createJoPickOrdersForPickOrder(pickOrderId: Long, itemIds: List<Long>): List<JoPickOrder> { | ||||
| // Get pick order and item details | // Get pick order and item details | ||||
| @@ -155,7 +155,7 @@ open class JoPickOrderService( | |||||
| } | } | ||||
| return joPickOrderRepository.saveAll(joPickOrders) | return joPickOrderRepository.saveAll(joPickOrders) | ||||
| } | } | ||||
| // ✅ Create JoPickOrderRecord records for a pick order | |||||
| // Create JoPickOrderRecord records for a pick order | |||||
| @Transactional | @Transactional | ||||
| open fun createJoPickOrderRecordsForPickOrder(pickOrderId: Long, itemIds: List<Long>): List<JoPickOrderRecord> { | open fun createJoPickOrderRecordsForPickOrder(pickOrderId: Long, itemIds: List<Long>): List<JoPickOrderRecord> { | ||||
| // Get pick order and item details | // Get pick order and item details | ||||
| @@ -248,7 +248,7 @@ open class JoPickOrderService( | |||||
| ) | ) | ||||
| } | } | ||||
| // ✅ 修复:简化 SQL 查询,移除不存在的字段 | |||||
| // 修复:简化 SQL 查询,移除不存在的字段 | |||||
| val pickOrderIdsStr = pickOrderIds.joinToString(",") | val pickOrderIdsStr = pickOrderIds.joinToString(",") | ||||
| val sql = """ | val sql = """ | ||||
| @@ -286,7 +286,7 @@ open class JoPickOrderService( | |||||
| w.name as location, | w.name as location, | ||||
| COALESCE(uc.udfudesc, 'N/A') as stockUnit, | COALESCE(uc.udfudesc, 'N/A') as stockUnit, | ||||
| -- ✅ Simplified Router Information using warehouse | |||||
| -- Simplified Router Information using warehouse | |||||
| w.`order` as routerIndex, | w.`order` as routerIndex, | ||||
| w.code as routerRoute, | w.code as routerRoute, | ||||
| w.code as routerArea, | w.code as routerArea, | ||||
| @@ -363,7 +363,7 @@ open class JoPickOrderService( | |||||
| LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId | 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.inventory_lot_line ill ON spl.suggestedLotLineId = ill.id | ||||
| LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId | LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId | ||||
| LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId -- ✅ 直接关联 warehouse | |||||
| LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId -- 直接关联 warehouse | |||||
| LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ill.id AND sol.deleted = false | LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ill.id AND sol.deleted = false | ||||
| LEFT JOIN fpsmsdb.jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId | LEFT JOIN fpsmsdb.jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId | ||||
| WHERE po.deleted = false | WHERE po.deleted = false | ||||
| @@ -376,7 +376,7 @@ open class JoPickOrderService( | |||||
| AND (spl.pickOrderLineId IS NOT NULL OR sol.pickOrderLineId IS NOT NULL) | AND (spl.pickOrderLineId IS NOT NULL OR sol.pickOrderLineId IS NOT NULL) | ||||
| ORDER BY | ORDER BY | ||||
| CASE WHEN sol.status = 'rejected' THEN 0 ELSE 1 END, | CASE WHEN sol.status = 'rejected' THEN 0 ELSE 1 END, | ||||
| COALESCE(w.`order`, 999999) ASC, -- ✅ 使用 warehouse.order 排序 | |||||
| COALESCE(w.`order`, 999999) ASC, -- 使用 warehouse.order 排序 | |||||
| po.code ASC, | po.code ASC, | ||||
| i.code ASC, | i.code ASC, | ||||
| il.expiryDate ASC, | il.expiryDate ASC, | ||||
| @@ -386,7 +386,7 @@ open class JoPickOrderService( | |||||
| println("🔍 Executing SQL for job order hierarchical structure: $sql") | println("🔍 Executing SQL for job order hierarchical structure: $sql") | ||||
| println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr") | println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr") | ||||
| // ✅ 修复:直接执行 SQL 查询并构建分层结构 | |||||
| // 修复:直接执行 SQL 查询并构建分层结构 | |||||
| try { | try { | ||||
| val results = jdbcDao.queryForList(sql, mapOf("userId" to userId)) | val results = jdbcDao.queryForList(sql, mapOf("userId" to userId)) | ||||
| @@ -410,7 +410,7 @@ open class JoPickOrderService( | |||||
| "jobOrder" to mapOf( | "jobOrder" to mapOf( | ||||
| "id" to firstRow["jobOrderId"], | "id" to firstRow["jobOrderId"], | ||||
| "code" to firstRow["jobOrderCode"], | "code" to firstRow["jobOrderCode"], | ||||
| "name" to "Job Order ${firstRow["jobOrderCode"]}" // ✅ 使用生成的名称 | |||||
| "name" to "Job Order ${firstRow["jobOrderCode"]}" // 使用生成的名称 | |||||
| ) | ) | ||||
| ) | ) | ||||
| } | } | ||||
| @@ -474,7 +474,7 @@ open class JoPickOrderService( | |||||
| } | } | ||||
| // Get completed job order pick orders (for second tab) | // Get completed job order pick orders (for second tab) | ||||
| // ✅ Fix the getCompletedJobOrderLotsHierarchical method | |||||
| // Fix the getCompletedJobOrderLotsHierarchical method | |||||
| open fun getCompletedJobOrderLotsHierarchical(userId: Long): Map<String, Any?> { | open fun getCompletedJobOrderLotsHierarchical(userId: Long): Map<String, Any?> { | ||||
| println("=== Debug: getCompletedJobOrderLotsHierarchical ===") | println("=== Debug: getCompletedJobOrderLotsHierarchical ===") | ||||
| println("today: ${LocalDate.now()}") | println("today: ${LocalDate.now()}") | ||||
| @@ -520,7 +520,7 @@ open fun getCompletedJobOrderLotsHierarchical(userId: Long): Map<String, Any?> { | |||||
| println("🔍 DEBUG: Using latest completed order: ${latestCompletedOrder.code}") | println("🔍 DEBUG: Using latest completed order: ${latestCompletedOrder.code}") | ||||
| // ✅ Use the same SQL query approach as getAllJobOrderLotsWithDetailsHierarchical | |||||
| // Use the same SQL query approach as getAllJobOrderLotsWithDetailsHierarchical | |||||
| val pickOrderIds = listOf(latestCompletedOrder.id!!) | val pickOrderIds = listOf(latestCompletedOrder.id!!) | ||||
| val pickOrderIdsStr = pickOrderIds.joinToString(",") | val pickOrderIdsStr = pickOrderIds.joinToString(",") | ||||
| @@ -657,7 +657,7 @@ open fun getCompletedJobOrderLotsHierarchical(userId: Long): Map<String, Any?> { | |||||
| println("🔍 Executing SQL for completed job order hierarchical structure: $sql") | println("🔍 Executing SQL for completed job order hierarchical structure: $sql") | ||||
| println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr") | println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr") | ||||
| // ✅ Execute SQL query and build hierarchical structure | |||||
| // Execute SQL query and build hierarchical structure | |||||
| try { | try { | ||||
| val results = jdbcDao.queryForList(sql, mapOf("userId" to userId)) | val results = jdbcDao.queryForList(sql, mapOf("userId" to userId)) | ||||
| @@ -799,7 +799,7 @@ open fun getUnassignedJobOrderPickOrders(): List<Map<String, Any?>> { | |||||
| println("=== getUnassignedJobOrderPickOrders ===") | println("=== getUnassignedJobOrderPickOrders ===") | ||||
| return try { | return try { | ||||
| // ✅ 修复:使用正确的 repository 方法 | |||||
| // 修复:使用正确的 repository 方法 | |||||
| val unassignedPickOrders = pickOrderRepository.findAllByStatusAndAssignToIsNullAndDeletedFalse( | val unassignedPickOrders = pickOrderRepository.findAllByStatusAndAssignToIsNullAndDeletedFalse( | ||||
| com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED | com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED | ||||
| ).filter { pickOrder -> | ).filter { pickOrder -> | ||||
| @@ -840,7 +840,7 @@ open fun getUnassignedJobOrderPickOrders(): List<Map<String, Any?>> { | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:assignJobOrderPickOrderToUser 方法中的自引用问题 | |||||
| // 修复:assignJobOrderPickOrderToUser 方法中的自引用问题 | |||||
| @Transactional | @Transactional | ||||
| open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): MessageResponse { | open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): MessageResponse { | ||||
| println("=== assignJobOrderPickOrderToUser ===") | println("=== assignJobOrderPickOrderToUser ===") | ||||
| @@ -874,7 +874,7 @@ open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): Message | |||||
| pickOrder.assignTo = userService.find(userId).orElse(null) | pickOrder.assignTo = userService.find(userId).orElse(null) | ||||
| pickOrderRepository.save(pickOrder) | pickOrderRepository.save(pickOrder) | ||||
| // ✅ 修复:使用 this 而不是 joPickOrderService | |||||
| // 修复:使用 this 而不是 joPickOrderService | |||||
| this.updateHandledByForPickOrder(pickOrderId, userId) | this.updateHandledByForPickOrder(pickOrderId, userId) | ||||
| this.updateRecordHandledByForPickOrder(pickOrderId, userId) | this.updateRecordHandledByForPickOrder(pickOrderId, userId) | ||||
| MessageResponse( | MessageResponse( | ||||
| @@ -898,7 +898,7 @@ open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): Message | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||
| // ✅ Fix the updateMatchStatus method | |||||
| // Fix the updateMatchStatus method | |||||
| open fun updateMatchStatus(pickOrderId: Long, itemId: Long, userId: Long, qty: Int): MessageResponse { | open fun updateMatchStatus(pickOrderId: Long, itemId: Long, userId: Long, qty: Int): MessageResponse { | ||||
| try { | try { | ||||
| println("=== Debug: updateMatchStatus ===") | println("=== Debug: updateMatchStatus ===") | ||||
| @@ -919,23 +919,23 @@ open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): Message | |||||
| val joPickOrderEntity = joPickOrder.get() | val joPickOrderEntity = joPickOrder.get() | ||||
| // ✅ 设置扫描状态和相关字段 | |||||
| // 设置扫描状态和相关字段 | |||||
| joPickOrderEntity.matchStatus = JoPickOrderStatus.scanned | joPickOrderEntity.matchStatus = JoPickOrderStatus.scanned | ||||
| joPickOrderEntity.matchBy = userId | joPickOrderEntity.matchBy = userId | ||||
| joPickOrderEntity.matchQty = qty // ✅ 使用传递的 qty | |||||
| joPickOrderEntity.matchQty = qty // 使用传递的 qty | |||||
| joPickOrderRepository.save(joPickOrderEntity) | joPickOrderRepository.save(joPickOrderEntity) | ||||
| // ✅ 同时更新 jo_pick_order_record | |||||
| // 同时更新 jo_pick_order_record | |||||
| val joPickOrderRecord = joPickOrderRecordRepository.findByPickOrderIdAndItemId(pickOrderId, itemId) | val joPickOrderRecord = joPickOrderRecordRepository.findByPickOrderIdAndItemId(pickOrderId, itemId) | ||||
| if (joPickOrderRecord.isPresent) { | if (joPickOrderRecord.isPresent) { | ||||
| val recordEntity = joPickOrderRecord.get() | val recordEntity = joPickOrderRecord.get() | ||||
| recordEntity.matchStatus = JoPickOrderStatus.scanned | recordEntity.matchStatus = JoPickOrderStatus.scanned | ||||
| recordEntity.matchBy = userId | recordEntity.matchBy = userId | ||||
| recordEntity.matchQty = qty // ✅ 使用相同的 qty | |||||
| recordEntity.matchQty = qty // 使用相同的 qty | |||||
| joPickOrderRecordRepository.save(recordEntity) | joPickOrderRecordRepository.save(recordEntity) | ||||
| } | } | ||||
| println("✅ Updated match status: pickOrderId=$pickOrderId, itemId=$itemId, matchQty=$qty") | |||||
| println(" Updated match status: pickOrderId=$pickOrderId, itemId=$itemId, matchQty=$qty") | |||||
| return MessageResponse( | return MessageResponse( | ||||
| id = null, | id = null, | ||||
| @@ -979,17 +979,17 @@ open fun submitSecondScanQty(request: SecondScanSubmitRequest): MessageResponse | |||||
| val joPickOrderEntity = joPickOrder.get() | val joPickOrderEntity = joPickOrder.get() | ||||
| joPickOrderEntity.matchQty = request.qty.toInt() | joPickOrderEntity.matchQty = request.qty.toInt() | ||||
| // ✅ Set status to completed when submitting quantity | |||||
| // Set status to completed when submitting quantity | |||||
| joPickOrderEntity.matchStatus = JoPickOrderStatus.completed | joPickOrderEntity.matchStatus = JoPickOrderStatus.completed | ||||
| // ✅ 添加:如果 ticketCompleteTime 还没设置,现在设置(通常已经在拣货完成时设置了) | |||||
| // 添加:如果 ticketCompleteTime 还没设置,现在设置(通常已经在拣货完成时设置了) | |||||
| if (joPickOrderEntity.ticketCompleteTime == null) { | if (joPickOrderEntity.ticketCompleteTime == null) { | ||||
| joPickOrderEntity.ticketCompleteTime = LocalDateTime.now() | joPickOrderEntity.ticketCompleteTime = LocalDateTime.now() | ||||
| } | } | ||||
| joPickOrderRepository.save(joPickOrderEntity) | joPickOrderRepository.save(joPickOrderEntity) | ||||
| println("✅ Updated jo_pick_order: status=${joPickOrderEntity.matchStatus}, qty=${joPickOrderEntity.matchQty}, completeTime=${joPickOrderEntity.ticketCompleteTime}") | |||||
| println(" Updated jo_pick_order: status=${joPickOrderEntity.matchStatus}, qty=${joPickOrderEntity.matchQty}, completeTime=${joPickOrderEntity.ticketCompleteTime}") | |||||
| return MessageResponse( | return MessageResponse( | ||||
| id = null, | id = null, | ||||
| @@ -1014,7 +1014,7 @@ open fun submitSecondScanQty(request: SecondScanSubmitRequest): MessageResponse | |||||
| } | } | ||||
| // ✅ Fix the recordSecondScanIssue method | |||||
| // Fix the recordSecondScanIssue method | |||||
| open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse { | open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse { | ||||
| try { | try { | ||||
| println("=== Debug: recordSecondScanIssue ===") | println("=== Debug: recordSecondScanIssue ===") | ||||
| @@ -1033,7 +1033,7 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse | |||||
| ) | ) | ||||
| } | } | ||||
| // ✅ Get pick order and pick order line details with item and lot information | |||||
| // Get pick order and pick order line details with item and lot information | |||||
| val pickOrderDetails = jdbcDao.queryForList( | val pickOrderDetails = jdbcDao.queryForList( | ||||
| """ | """ | ||||
| SELECT | SELECT | ||||
| @@ -1060,7 +1060,7 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse | |||||
| ) | ) | ||||
| ) | ) | ||||
| // ✅ 修复:使用正确的变量名 pickOrderDetails | |||||
| // 修复:使用正确的变量名 pickOrderDetails | |||||
| if (pickOrderDetails.isEmpty()) { | if (pickOrderDetails.isEmpty()) { | ||||
| return MessageResponse( | return MessageResponse( | ||||
| id = null, | id = null, | ||||
| @@ -1072,7 +1072,7 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse | |||||
| ) | ) | ||||
| } | } | ||||
| // ✅ 修复:使用正确的变量名 pickOrderDetails | |||||
| // 修复:使用正确的变量名 pickOrderDetails | |||||
| val orderData = pickOrderDetails.first() | val orderData = pickOrderDetails.first() | ||||
| val pickOrderCode = orderData["pickOrderCode"] as String? | val pickOrderCode = orderData["pickOrderCode"] as String? | ||||
| val pickOrderCreateDate = orderData["pickOrderCreateDate"]?.let { | val pickOrderCreateDate = orderData["pickOrderCreateDate"]?.let { | ||||
| @@ -1086,7 +1086,7 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse | |||||
| BigDecimal(it.toString()) | BigDecimal(it.toString()) | ||||
| } | } | ||||
| // ✅ 获取 suggested lot 信息(用于 lot_id, lot_no, store_location) | |||||
| // 获取 suggested lot 信息(用于 lot_id, lot_no, store_location) | |||||
| val lotResults = jdbcDao.queryForList( | val lotResults = jdbcDao.queryForList( | ||||
| """ | """ | ||||
| SELECT | SELECT | ||||
| @@ -1127,41 +1127,41 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse | |||||
| joPickOrderRepository.save(joPickOrderEntity) | joPickOrderRepository.save(joPickOrderEntity) | ||||
| // ✅ 生成 issueNo | |||||
| // 生成 issueNo | |||||
| val issueNo = generateIssueNoForJo() | val issueNo = generateIssueNoForJo() | ||||
| // ✅ Create pick execution issue with complete data | |||||
| // ✅ Create pick execution issue with complete data | |||||
| // Create pick execution issue with complete data | |||||
| // Create pick execution issue with complete data | |||||
| val pickExecutionIssue = PickExecutionIssue( | val pickExecutionIssue = PickExecutionIssue( | ||||
| id = null, // ✅ 1 | |||||
| pickOrderId = request.pickOrderId, // ✅ 2 | |||||
| pickOrderCode = pickOrderCode ?: "PI-${request.pickOrderId}", // ✅ 3 | |||||
| pickOrderCreateDate = pickOrderCreateDate, // ✅ 4 | |||||
| pickExecutionDate = LocalDate.now(), // ✅ 5 | |||||
| pickOrderLineId = pickOrderLineId, // ✅ 6 | |||||
| issueNo = issueNo, // ✅ 7 | |||||
| issueCategory = IssueCategory.match_issue, // ✅ 8 - 因为这是 JO 的 match 问题 | |||||
| itemId = request.itemId, // ✅ 9 | |||||
| itemCode = itemCode, // ✅ 10 | |||||
| itemDescription = itemName, // ✅ 11 | |||||
| lotId = lotId, // ✅ 12 | |||||
| lotNo = lotNo, // ✅ 13 | |||||
| storeLocation = storeLocation, // ✅ 14 | |||||
| requiredQty = requiredQty, // ✅ 15 | |||||
| actualPickQty = request.qty.toBigDecimal(), // ✅ 16 | |||||
| missQty = request.missQty.toBigDecimal(), // ✅ 17 | |||||
| badItemQty = request.badItemQty.toBigDecimal(), // ✅ 18 | |||||
| issueRemark = request.reason, // ✅ 19 | |||||
| pickerName = pickerName, // ✅ 20 | |||||
| handleStatus = HandleStatus.pending, // ✅ 21 | |||||
| handleDate = null, // ✅ 22 | |||||
| handledBy = null, // ✅ 23 | |||||
| created = LocalDateTime.now(), // ✅ 24 | |||||
| createdBy = request.createdBy.toString(), // ✅ 25 | |||||
| version = 0, // ✅ 26 | |||||
| modified = LocalDateTime.now(), // ✅ 27 | |||||
| modifiedBy = null, // ✅ 28 | |||||
| deleted = false // ✅ 29 | |||||
| id = null, // 1 | |||||
| pickOrderId = request.pickOrderId, // 2 | |||||
| pickOrderCode = pickOrderCode ?: "PI-${request.pickOrderId}", // 3 | |||||
| pickOrderCreateDate = pickOrderCreateDate, // 4 | |||||
| pickExecutionDate = LocalDate.now(), // 5 | |||||
| pickOrderLineId = pickOrderLineId, // 6 | |||||
| issueNo = issueNo, // 7 | |||||
| issueCategory = IssueCategory.match_issue, // 8 - 因为这是 JO 的 match 问题 | |||||
| itemId = request.itemId, // 9 | |||||
| itemCode = itemCode, // 10 | |||||
| itemDescription = itemName, // 11 | |||||
| lotId = lotId, // 12 | |||||
| lotNo = lotNo, // 13 | |||||
| storeLocation = storeLocation, // 14 | |||||
| requiredQty = requiredQty, // 15 | |||||
| actualPickQty = request.qty.toBigDecimal(), // 16 | |||||
| missQty = request.missQty.toBigDecimal(), // 17 | |||||
| badItemQty = request.badItemQty.toBigDecimal(), // 18 | |||||
| issueRemark = request.reason, // 19 | |||||
| pickerName = pickerName, // 20 | |||||
| handleStatus = HandleStatus.pending, // 21 | |||||
| handleDate = null, // 22 | |||||
| handledBy = null, // 23 | |||||
| created = LocalDateTime.now(), // 24 | |||||
| createdBy = request.createdBy.toString(), // 25 | |||||
| version = 0, // 26 | |||||
| modified = LocalDateTime.now(), // 27 | |||||
| modifiedBy = null, // 28 | |||||
| deleted = false // 29 | |||||
| ) | ) | ||||
| pickExecutionIssueRepository.save(pickExecutionIssue) | pickExecutionIssueRepository.save(pickExecutionIssue) | ||||
| @@ -244,7 +244,7 @@ open class JobOrderService( | |||||
| pickOrderEntity.status = com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED | pickOrderEntity.status = com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED | ||||
| pickOrderRepository.saveAndFlush(pickOrderEntity) | pickOrderRepository.saveAndFlush(pickOrderEntity) | ||||
| // ✅ 添加 suggested pick lots 创建逻辑 | |||||
| // 添加 suggested pick lots 创建逻辑 | |||||
| val lines = pickOrderLineRepository.findAllByPickOrderId(pickOrderEntity.id!!) | val lines = pickOrderLineRepository.findAllByPickOrderId(pickOrderEntity.id!!) | ||||
| if (lines.isNotEmpty()) { | if (lines.isNotEmpty()) { | ||||
| val suggestions = suggestedPickLotService.suggestionForPickOrderLines( | val suggestions = suggestedPickLotService.suggestionForPickOrderLines( | ||||
| @@ -253,7 +253,7 @@ open class JobOrderService( | |||||
| val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList) | val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList) | ||||
| // ✅ Hold inventory quantities | |||||
| // Hold inventory quantities | |||||
| val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn( | val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn( | ||||
| saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id } | saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id } | ||||
| ) | ) | ||||
| @@ -269,7 +269,7 @@ open class JobOrderService( | |||||
| } | } | ||||
| inventoryLotLineRepository.saveAll(inventoryLotLines) | inventoryLotLineRepository.saveAll(inventoryLotLines) | ||||
| // ✅ Create stock out record and pre-create stock out lines | |||||
| // Create stock out record and pre-create stock out lines | |||||
| val stockOut = StockOut().apply { | val stockOut = StockOut().apply { | ||||
| this.type = "job" | this.type = "job" | ||||
| this.consoPickOrderCode = consoCode | this.consoPickOrderCode = consoCode | ||||
| @@ -278,7 +278,7 @@ open class JobOrderService( | |||||
| } | } | ||||
| val savedStockOut = stockOutRepository.saveAndFlush(stockOut) | val savedStockOut = stockOutRepository.saveAndFlush(stockOut) | ||||
| // ✅ Pre-create stock out lines for suggested lots | |||||
| // Pre-create stock out lines for suggested lots | |||||
| saveSuggestedPickLots.forEach { lot -> | saveSuggestedPickLots.forEach { lot -> | ||||
| val polId = lot.pickOrderLine?.id | val polId = lot.pickOrderLine?.id | ||||
| val illId = lot.suggestedLotLine?.id | val illId = lot.suggestedLotLine?.id | ||||
| @@ -134,8 +134,8 @@ class JobOrderController( | |||||
| @RequestBody data: Map<String, Any> | @RequestBody data: Map<String, Any> | ||||
| ): MessageResponse { | ): MessageResponse { | ||||
| val request = SecondScanSubmitRequest( | val request = SecondScanSubmitRequest( | ||||
| pickOrderId = pickOrderId, // ✅ Use path variable | |||||
| itemId = itemId, // ✅ Use path variable | |||||
| pickOrderId = pickOrderId, // Use path variable | |||||
| itemId = itemId, // Use path variable | |||||
| qty = (data["qty"] as Number).toDouble(), | qty = (data["qty"] as Number).toDouble(), | ||||
| isMissing = data["isMissing"] as? Boolean ?: false, | isMissing = data["isMissing"] as? Boolean ?: false, | ||||
| isBad = data["isBad"] as? Boolean ?: false | isBad = data["isBad"] as? Boolean ?: false | ||||
| @@ -150,10 +150,10 @@ fun recordSecondScanIssue( | |||||
| @RequestBody data: Map<String, Any> | @RequestBody data: Map<String, Any> | ||||
| ): MessageResponse { | ): MessageResponse { | ||||
| val request = SecondScanIssueRequest( | val request = SecondScanIssueRequest( | ||||
| pickOrderId = pickOrderId, // ✅ path 变量 | |||||
| itemId = itemId, // ✅ path 变量 | |||||
| pickOrderId = pickOrderId, // path 变量 | |||||
| itemId = itemId, // path 变量 | |||||
| qty = (data["qty"] as Number).toDouble(), | qty = (data["qty"] as Number).toDouble(), | ||||
| // ✅ 新增:安全读取 missQty/badItemQty/type,默认 0/"match" | |||||
| // 新增:安全读取 missQty/badItemQty/type,默认 0/"match" | |||||
| missQty = (data["missQty"] as? Number)?.toDouble() ?: 0.0, | missQty = (data["missQty"] as? Number)?.toDouble() ?: 0.0, | ||||
| badItemQty = (data["badItemQty"] as? Number)?.toDouble() ?: 0.0, | badItemQty = (data["badItemQty"] as? Number)?.toDouble() ?: 0.0, | ||||
| isMissing = data["isMissing"] as? Boolean ?: false, | isMissing = data["isMissing"] as? Boolean ?: false, | ||||
| @@ -4,8 +4,8 @@ data class SecondScanIssueRequest( | |||||
| val pickOrderId: Long, | val pickOrderId: Long, | ||||
| val itemId: Long, | val itemId: Long, | ||||
| val qty: Double, // 这是 actual pick qty (verified qty) | val qty: Double, // 这是 actual pick qty (verified qty) | ||||
| val missQty: Double = 0.0, // ✅ 添加:单独的 miss qty | |||||
| val badItemQty: Double = 0.0, // ✅ 添加:单独的 bad item qty | |||||
| val missQty: Double = 0.0, // 添加:单独的 miss qty | |||||
| val badItemQty: Double = 0.0, // 添加:单独的 bad item qty | |||||
| val isMissing: Boolean, | val isMissing: Boolean, | ||||
| val isBad: Boolean, | val isBad: Boolean, | ||||
| val reason: String, | val reason: String, | ||||
| @@ -4,9 +4,9 @@ import jakarta.validation.constraints.NotNull | |||||
| data class SecondScanSubmitRequest( | data class SecondScanSubmitRequest( | ||||
| @NotNull | @NotNull | ||||
| val pickOrderId: Long, // ✅ Add missing property | |||||
| val pickOrderId: Long, // Add missing property | |||||
| @NotNull | @NotNull | ||||
| val itemId: Long, // ✅ Add missing property | |||||
| val itemId: Long, // Add missing property | |||||
| @NotNull | @NotNull | ||||
| val qty: Double, | val qty: Double, | ||||
| val isMissing: Boolean = false, | val isMissing: Boolean = false, | ||||
| @@ -103,7 +103,7 @@ val doPickOrderId: Long? = null, | |||||
| @Column(name = "deleted", nullable = false) | @Column(name = "deleted", nullable = false) | ||||
| val deleted: Boolean = false | val deleted: Boolean = false | ||||
| ) { | ) { | ||||
| // ✅ 添加默认构造函数 | |||||
| // 添加默认构造函数 | |||||
| constructor() : this( | constructor() : this( | ||||
| pickOrderId = null, | pickOrderId = null, | ||||
| pickOrderCode = "", | pickOrderCode = "", | ||||
| @@ -13,15 +13,15 @@ interface TruckRepository : AbstractRepository<Truck, Long> { | |||||
| @Query("SELECT t FROM Truck t WHERE t.shop.id = :shopId AND t.deleted = false ORDER BY t.id ASC") | @Query("SELECT t FROM Truck t WHERE t.shop.id = :shopId AND t.deleted = false ORDER BY t.id ASC") | ||||
| fun findFirstByShopIdAndDeletedFalse(@Param("shopId") shopId: Long): List<Truck> | fun findFirstByShopIdAndDeletedFalse(@Param("shopId") shopId: Long): List<Truck> | ||||
| // ✅ 使用新的 TruckLanceCode 字段名 | |||||
| // 使用新的 TruckLanceCode 字段名 | |||||
| @Query("SELECT t FROM Truck t WHERE t.truckLanceCode = :truckLanceCode AND t.deleted = false") | @Query("SELECT t FROM Truck t WHERE t.truckLanceCode = :truckLanceCode AND t.deleted = false") | ||||
| fun findByTruckLanceCodeAndDeletedFalse(@Param("truckLanceCode") truckLanceCode: String): Truck? | fun findByTruckLanceCodeAndDeletedFalse(@Param("truckLanceCode") truckLanceCode: String): Truck? | ||||
| // ✅ 按 ShopCode 查询 | |||||
| // 按 ShopCode 查询 | |||||
| @Query("SELECT t FROM Truck t WHERE t.shopCode = :shopCode AND t.deleted = false") | @Query("SELECT t FROM Truck t WHERE t.shopCode = :shopCode AND t.deleted = false") | ||||
| fun findByShopCodeAndDeletedFalse(@Param("shopCode") shopCode: String): List<Truck> | fun findByShopCodeAndDeletedFalse(@Param("shopCode") shopCode: String): List<Truck> | ||||
| // ✅ 按 Store_id 查询 | |||||
| // 按 Store_id 查询 | |||||
| fun findByStoreIdAndDeletedFalse(storeId: Int): List<Truck> | fun findByStoreIdAndDeletedFalse(storeId: Int): List<Truck> | ||||
| } | } | ||||
| @@ -5,7 +5,7 @@ import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue | import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository | import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.StockOutLine | import com.ffii.fpsms.modules.stock.entity.StockOutLine | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // ✅ 添加这行 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // 添加这行 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus | import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus | ||||
| import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository | import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotLine | import com.ffii.fpsms.modules.stock.entity.InventoryLotLine | ||||
| @@ -70,15 +70,15 @@ open class PickExecutionIssueService( | |||||
| val pickOrder = pickOrderRepository.findById(request.pickOrderId).orElse(null) | val pickOrder = pickOrderRepository.findById(request.pickOrderId).orElse(null) | ||||
| // 2. 创建 pick execution issue 记录 | // 2. 创建 pick execution issue 记录 | ||||
| val pickExecutionIssue = PickExecutionIssue( | val pickExecutionIssue = PickExecutionIssue( | ||||
| id = null, // ✅ 添加 id | |||||
| id = null, // 添加 id | |||||
| pickOrderId = request.pickOrderId, | pickOrderId = request.pickOrderId, | ||||
| pickOrderCode = request.pickOrderCode, | pickOrderCode = request.pickOrderCode, | ||||
| pickOrderCreateDate = request.pickOrderCreateDate, | pickOrderCreateDate = request.pickOrderCreateDate, | ||||
| pickExecutionDate = request.pickExecutionDate ?: LocalDate.now(), | pickExecutionDate = request.pickExecutionDate ?: LocalDate.now(), | ||||
| pickOrderLineId = request.pickOrderLineId, | pickOrderLineId = request.pickOrderLineId, | ||||
| issueNo = generateIssueNo(), | issueNo = generateIssueNo(), | ||||
| joPickOrderId = pickOrder?.jobOrder?.id, // ✅ 添加 | |||||
| doPickOrderId = if (pickOrder?.type?.value == "do") pickOrder.id else null, // ✅ 添加 | |||||
| joPickOrderId = pickOrder?.jobOrder?.id, // 添加 | |||||
| doPickOrderId = if (pickOrder?.type?.value == "do") pickOrder.id else null, // 添加 | |||||
| issueCategory = IssueCategory.valueOf( | issueCategory = IssueCategory.valueOf( | ||||
| request.issueCategory ?: "lot_issue" | request.issueCategory ?: "lot_issue" | ||||
| ), | ), | ||||
| @@ -93,16 +93,16 @@ open class PickExecutionIssueService( | |||||
| missQty = request.missQty, | missQty = request.missQty, | ||||
| badItemQty = request.badItemQty, | badItemQty = request.badItemQty, | ||||
| issueRemark = request.issueRemark, | issueRemark = request.issueRemark, | ||||
| pickerName = request.pickerName, // ✅ 确保从 request 中获取 | |||||
| handleStatus = HandleStatus.pending, // ✅ 添加 | |||||
| handleDate = null, // ✅ 添加 | |||||
| pickerName = request.pickerName, // 确保从 request 中获取 | |||||
| handleStatus = HandleStatus.pending, // 添加 | |||||
| handleDate = null, // 添加 | |||||
| handledBy = request.handledBy, | handledBy = request.handledBy, | ||||
| created = LocalDateTime.now(), | created = LocalDateTime.now(), | ||||
| createdBy = "system", | createdBy = "system", | ||||
| version = 0, // ✅ 添加 | |||||
| version = 0, // 添加 | |||||
| modified = LocalDateTime.now(), | modified = LocalDateTime.now(), | ||||
| modifiedBy = "system", | modifiedBy = "system", | ||||
| deleted = false // ✅ 添加 | |||||
| deleted = false // 添加 | |||||
| ) | ) | ||||
| val savedIssue = pickExecutionIssueRepository.save(pickExecutionIssue) | val savedIssue = pickExecutionIssueRepository.save(pickExecutionIssue) | ||||
| @@ -139,7 +139,7 @@ open class PickExecutionIssueService( | |||||
| handleBothMissAndBadItem(request, missQty, badItemQty) | handleBothMissAndBadItem(request, missQty, badItemQty) | ||||
| } | } | ||||
| // ✅ 修复:情况4: 有 miss item 的情况(无论 actualPickQty 是多少) | |||||
| // 修复:情况4: 有 miss item 的情况(无论 actualPickQty 是多少) | |||||
| missQty > BigDecimal.ZERO -> { | missQty > BigDecimal.ZERO -> { | ||||
| handleMissItemWithPartialPick(request, actualPickQty, missQty) | handleMissItemWithPartialPick(request, actualPickQty, missQty) | ||||
| } | } | ||||
| @@ -264,7 +264,7 @@ private fun checkAndCompletePickOrder(consoCode: String) { | |||||
| // 4. 如果所有行都完成或被拒绝,则完成 pick order | // 4. 如果所有行都完成或被拒绝,则完成 pick order | ||||
| if (unfinishedLines.isEmpty()) { | if (unfinishedLines.isEmpty()) { | ||||
| println("✅ All stock out lines completed or rejected, completing pick order...") | |||||
| println(" All stock out lines completed or rejected, completing pick order...") | |||||
| // 4.1 更新 StockOut 状态 | // 4.1 更新 StockOut 状态 | ||||
| stockOut.status = StockOutStatus.COMPLETE.status | stockOut.status = StockOutStatus.COMPLETE.status | ||||
| @@ -281,7 +281,7 @@ private fun checkAndCompletePickOrder(consoCode: String) { | |||||
| println("Updated pick order line ${line.id} to COMPLETED") | println("Updated pick order line ${line.id} to COMPLETED") | ||||
| } | } | ||||
| pickOrderLineRepository.saveAll(pickOrderLines) | pickOrderLineRepository.saveAll(pickOrderLines) | ||||
| println("✅ Updated ${pickOrderLines.size} pick order lines to COMPLETED status") | |||||
| println(" Updated ${pickOrderLines.size} pick order lines to COMPLETED status") | |||||
| // 4.3 获取所有受影响的 pick orders | // 4.3 获取所有受影响的 pick orders | ||||
| val pickOrderIds = pickOrderLines.mapNotNull { it.pickOrder?.id }.distinct() | val pickOrderIds = pickOrderLines.mapNotNull { it.pickOrder?.id }.distinct() | ||||
| @@ -302,15 +302,15 @@ private fun checkAndCompletePickOrder(consoCode: String) { | |||||
| pickOrder.status = PickOrderStatus.COMPLETED | pickOrder.status = PickOrderStatus.COMPLETED | ||||
| pickOrder.completeDate = LocalDateTime.now() | pickOrder.completeDate = LocalDateTime.now() | ||||
| pickOrderRepository.save(pickOrder) | pickOrderRepository.save(pickOrder) | ||||
| println("✅ Updated pick order ${pickOrder.code} to COMPLETED status") | |||||
| println(" Updated pick order ${pickOrder.code} to COMPLETED status") | |||||
| // 4.5 处理 DO pick order 相关记录 | // 4.5 处理 DO pick order 相关记录 | ||||
| try { | try { | ||||
| val removedCount = doPickOrderService.removeDoPickOrdersForPickOrder(pickOrderId) | val removedCount = doPickOrderService.removeDoPickOrdersForPickOrder(pickOrderId) | ||||
| println("✅ Removed $removedCount do_pick_order records for completed pick order ${pickOrderId}") | |||||
| println(" Removed $removedCount do_pick_order records for completed pick order ${pickOrderId}") | |||||
| doPickOrderService.completeDoPickOrderRecordsForPickOrder(pickOrderId) | doPickOrderService.completeDoPickOrderRecordsForPickOrder(pickOrderId) | ||||
| println("✅ Updated do_pick_order_record status to COMPLETED for pick order ${pickOrderId}") | |||||
| println(" Updated do_pick_order_record status to COMPLETED for pick order ${pickOrderId}") | |||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("⚠️ Error updating DO pick order records: ${e.message}") | println("⚠️ Error updating DO pick order records: ${e.message}") | ||||
| } | } | ||||
| @@ -330,7 +330,7 @@ private fun checkAndCompletePickOrder(consoCode: String) { | |||||
| } | } | ||||
| joPickOrderRecordRepository.saveAll(joPickOrderRecords) | joPickOrderRecordRepository.saveAll(joPickOrderRecords) | ||||
| println("✅ Set jo_pick_order ticketCompleteTime for pick order ${pickOrderId}") | |||||
| println(" Set jo_pick_order ticketCompleteTime for pick order ${pickOrderId}") | |||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("⚠️ Error updating JO pick order records: ${e.message}") | println("⚠️ Error updating JO pick order records: ${e.message}") | ||||
| } | } | ||||
| @@ -346,7 +346,7 @@ private fun checkAndCompletePickOrder(consoCode: String) { | |||||
| } | } | ||||
| } | } | ||||
| // FPSMS-backend/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt | // FPSMS-backend/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt | ||||
| // ✅ 修复:处理有部分拣货但有 miss item 的情况 | |||||
| // 修复:处理有部分拣货但有 miss item 的情况 | |||||
| @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | ||||
| private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, actualPickQty: BigDecimal, missQty: BigDecimal) { | private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, actualPickQty: BigDecimal, missQty: BigDecimal) { | ||||
| println("=== HANDLING MISS ITEM WITH PARTIAL PICK (FIXED LOGIC) ===") | println("=== HANDLING MISS ITEM WITH PARTIAL PICK (FIXED LOGIC) ===") | ||||
| @@ -358,16 +358,16 @@ private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, ac | |||||
| val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) | val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) | ||||
| if (inventoryLotLine != null) { | if (inventoryLotLine != null) { | ||||
| // ✅ 修复1:只处理已拣货的部分:更新 outQty | |||||
| // 修复1:只处理已拣货的部分:更新 outQty | |||||
| val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO | val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO | ||||
| val newOutQty = currentOutQty.add(actualPickQty) | val newOutQty = currentOutQty.add(actualPickQty) | ||||
| inventoryLotLine.outQty = newOutQty | inventoryLotLine.outQty = newOutQty | ||||
| // ✅ 修复2:Miss item 不减少 inQty,而是标记为 unavailable | |||||
| // 修复2:Miss item 不减少 inQty,而是标记为 unavailable | |||||
| // 因为 miss item 意味着这些物品实际上不存在或找不到 | // 因为 miss item 意味着这些物品实际上不存在或找不到 | ||||
| // 所以应该标记整个批次为 unavailable,而不是减少 inQty | // 所以应该标记整个批次为 unavailable,而不是减少 inQty | ||||
| // ✅ 修复3:如果 missQty > 0,标记批次为 unavailable | |||||
| // 修复3:如果 missQty > 0,标记批次为 unavailable | |||||
| if (missQty > BigDecimal.ZERO) { | if (missQty > BigDecimal.ZERO) { | ||||
| inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE | inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE | ||||
| } | } | ||||
| @@ -381,11 +381,11 @@ private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, ac | |||||
| println(" - Set status to UNAVAILABLE due to missQty: ${missQty}") | println(" - Set status to UNAVAILABLE due to missQty: ${missQty}") | ||||
| } | } | ||||
| // ✅ 修复4:更新 inventory 表的 unavailableQty | |||||
| // 修复4:更新 inventory 表的 unavailableQty | |||||
| // 对于 miss item,应该将 missQty 计入 unavailableQty | // 对于 miss item,应该将 missQty 计入 unavailableQty | ||||
| updateInventoryUnavailableQty(itemId, missQty) | updateInventoryUnavailableQty(itemId, missQty) | ||||
| // ✅ 修复5:更新 stock_out_line 状态为 rejected(因为还有 miss item) | |||||
| // 修复5:更新 stock_out_line 状态为 rejected(因为还有 miss item) | |||||
| updateStockOutLineStatus(request, "rejected") | updateStockOutLineStatus(request, "rejected") | ||||
| // 重新建议拣货批次(针对 miss 的数量) | // 重新建议拣货批次(针对 miss 的数量) | ||||
| @@ -397,7 +397,7 @@ private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, ac | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:Miss item 处理逻辑 | |||||
| // 修复:Miss item 处理逻辑 | |||||
| @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | ||||
| private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigDecimal) { | private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigDecimal) { | ||||
| println("=== HANDLING MISS ITEM ONLY (FIXED LOGIC) ===") | println("=== HANDLING MISS ITEM ONLY (FIXED LOGIC) ===") | ||||
| @@ -408,7 +408,7 @@ private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigD | |||||
| val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) | val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) | ||||
| if (inventoryLotLine != null) { | if (inventoryLotLine != null) { | ||||
| // ✅ 修复:Miss item 意味着剩余的所有物品都找不到 | |||||
| // 修复:Miss item 意味着剩余的所有物品都找不到 | |||||
| val currentInQty = inventoryLotLine.inQty ?: BigDecimal.ZERO | val currentInQty = inventoryLotLine.inQty ?: BigDecimal.ZERO | ||||
| val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO | val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO | ||||
| val remainingQty = currentInQty.minus(currentOutQty) | val remainingQty = currentInQty.minus(currentOutQty) | ||||
| @@ -425,16 +425,16 @@ private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigD | |||||
| println(" - Unavailable qty (should be remaining qty): ${remainingQty}") | println(" - Unavailable qty (should be remaining qty): ${remainingQty}") | ||||
| } | } | ||||
| // ✅ 修复:更新 inventory 表的 unavailableQty | |||||
| // 修复:更新 inventory 表的 unavailableQty | |||||
| // 应该是剩余的数量,而不是 missQty | // 应该是剩余的数量,而不是 missQty | ||||
| val currentInQty = inventoryLotLine?.inQty ?: BigDecimal.ZERO | val currentInQty = inventoryLotLine?.inQty ?: BigDecimal.ZERO | ||||
| val currentOutQty = inventoryLotLine?.outQty ?: BigDecimal.ZERO | val currentOutQty = inventoryLotLine?.outQty ?: BigDecimal.ZERO | ||||
| val remainingQty = currentInQty.minus(currentOutQty) | val remainingQty = currentInQty.minus(currentOutQty) | ||||
| // ✅ 修复:只增加剩余数量,不要重复计算 missQty | |||||
| // 修复:只增加剩余数量,不要重复计算 missQty | |||||
| updateInventoryUnavailableQty(itemId, remainingQty) | updateInventoryUnavailableQty(itemId, remainingQty) | ||||
| // ✅ 修复:更新 stock_out_line 状态为 rejected | |||||
| // 修复:更新 stock_out_line 状态为 rejected | |||||
| updateStockOutLineStatus(request, "rejected") | updateStockOutLineStatus(request, "rejected") | ||||
| // 重新建议拣货批次 | // 重新建议拣货批次 | ||||
| @@ -446,7 +446,7 @@ private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigD | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:Bad item 处理逻辑 | |||||
| // 修复:Bad item 处理逻辑 | |||||
| @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | ||||
| private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: BigDecimal) { | private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: BigDecimal) { | ||||
| println("=== HANDLING BAD ITEM ONLY (FIXED LOGIC) ===") | println("=== HANDLING BAD ITEM ONLY (FIXED LOGIC) ===") | ||||
| @@ -457,7 +457,7 @@ private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: Bi | |||||
| val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) | val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) | ||||
| if (inventoryLotLine != null) { | if (inventoryLotLine != null) { | ||||
| // ✅ 修复:Bad item 不减少 inQty,而是标记为 unavailable | |||||
| // 修复:Bad item 不减少 inQty,而是标记为 unavailable | |||||
| // 因为 bad item 意味着这些物品质量有问题,不能使用 | // 因为 bad item 意味着这些物品质量有问题,不能使用 | ||||
| inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE | inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE | ||||
| inventoryLotLine.modified = LocalDateTime.now() | inventoryLotLine.modified = LocalDateTime.now() | ||||
| @@ -467,10 +467,10 @@ private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: Bi | |||||
| println("Bad item only: Set lot ${lotId} status to UNAVAILABLE") | println("Bad item only: Set lot ${lotId} status to UNAVAILABLE") | ||||
| } | } | ||||
| // ✅ 修复:更新 inventory 表的 unavailableQty | |||||
| // 修复:更新 inventory 表的 unavailableQty | |||||
| updateInventoryUnavailableQty(itemId, badItemQty) | updateInventoryUnavailableQty(itemId, badItemQty) | ||||
| // ✅ 修复:更新 stock_out_line 状态为 rejected | |||||
| // 修复:更新 stock_out_line 状态为 rejected | |||||
| updateStockOutLineStatus(request, "rejected") | updateStockOutLineStatus(request, "rejected") | ||||
| // 重新建议拣货批次 | // 重新建议拣货批次 | ||||
| @@ -482,7 +482,7 @@ private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: Bi | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:Both miss and bad item 处理逻辑 | |||||
| // 修复:Both miss and bad item 处理逻辑 | |||||
| @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | ||||
| private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty: BigDecimal, badItemQty: BigDecimal) { | private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty: BigDecimal, badItemQty: BigDecimal) { | ||||
| println("=== HANDLING BOTH MISS AND BAD ITEM (FIXED LOGIC) ===") | println("=== HANDLING BOTH MISS AND BAD ITEM (FIXED LOGIC) ===") | ||||
| @@ -494,7 +494,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| val totalUnavailableQty = missQty.add(badItemQty) | val totalUnavailableQty = missQty.add(badItemQty) | ||||
| if (inventoryLotLine != null) { | if (inventoryLotLine != null) { | ||||
| // ✅ 修复:Miss + Bad item 不减少 inQty,而是标记为 unavailable | |||||
| // 修复:Miss + Bad item 不减少 inQty,而是标记为 unavailable | |||||
| inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE | inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE | ||||
| inventoryLotLine.modified = LocalDateTime.now() | inventoryLotLine.modified = LocalDateTime.now() | ||||
| inventoryLotLine.modifiedBy = "system" | inventoryLotLine.modifiedBy = "system" | ||||
| @@ -506,10 +506,10 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| println(" - Total Unavailable Qty: ${totalUnavailableQty}") | println(" - Total Unavailable Qty: ${totalUnavailableQty}") | ||||
| } | } | ||||
| // ✅ 修复:更新 inventory 表的 unavailableQty | |||||
| // 修复:更新 inventory 表的 unavailableQty | |||||
| updateInventoryUnavailableQty(itemId, totalUnavailableQty) | updateInventoryUnavailableQty(itemId, totalUnavailableQty) | ||||
| // ✅ 修复:更新 stock_out_line 状态为 rejected | |||||
| // 修复:更新 stock_out_line 状态为 rejected | |||||
| updateStockOutLineStatus(request, "rejected") | updateStockOutLineStatus(request, "rejected") | ||||
| // 重新建议拣货批次 | // 重新建议拣货批次 | ||||
| @@ -521,19 +521,19 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:正常拣货处理逻辑 | |||||
| // 修复:正常拣货处理逻辑 | |||||
| @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | ||||
| private fun handleNormalPick(request: PickExecutionIssueRequest, actualPickQty: BigDecimal) { | private fun handleNormalPick(request: PickExecutionIssueRequest, actualPickQty: BigDecimal) { | ||||
| println("=== HANDLING NORMAL PICK ===") | println("=== HANDLING NORMAL PICK ===") | ||||
| // ✅ 修复:更新 stock_out_line,但不要累积 qty | |||||
| // 修复:更新 stock_out_line,但不要累积 qty | |||||
| val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( | val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( | ||||
| request.pickOrderLineId, | request.pickOrderLineId, | ||||
| request.lotId ?: 0L | request.lotId ?: 0L | ||||
| ) | ) | ||||
| stockOutLines.forEach { stockOutLine -> | stockOutLines.forEach { stockOutLine -> | ||||
| // ✅ 修复:直接设置 qty 为 actualPickQty,不要累积 | |||||
| // 修复:直接设置 qty 为 actualPickQty,不要累积 | |||||
| val requiredQty = request.requiredQty?.toDouble() ?: 0.0 | val requiredQty = request.requiredQty?.toDouble() ?: 0.0 | ||||
| val actualPickQtyDouble = actualPickQty.toDouble() | val actualPickQtyDouble = actualPickQty.toDouble() | ||||
| val newStatus = if (actualPickQtyDouble >= requiredQty) { | val newStatus = if (actualPickQtyDouble >= requiredQty) { | ||||
| @@ -543,7 +543,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| } | } | ||||
| stockOutLine.status = newStatus | stockOutLine.status = newStatus | ||||
| stockOutLine.qty = actualPickQtyDouble // ✅ 直接设置,不累积 | |||||
| stockOutLine.qty = actualPickQtyDouble // 直接设置,不累积 | |||||
| stockOutLine.modified = LocalDateTime.now() | stockOutLine.modified = LocalDateTime.now() | ||||
| stockOutLine.modifiedBy = "system" | stockOutLine.modifiedBy = "system" | ||||
| stockOutLineRepository.save(stockOutLine) | stockOutLineRepository.save(stockOutLine) | ||||
| @@ -551,11 +551,11 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| println("Updated stock out line ${stockOutLine.id}: status=${newStatus}, qty=${actualPickQtyDouble}") | println("Updated stock out line ${stockOutLine.id}: status=${newStatus}, qty=${actualPickQtyDouble}") | ||||
| } | } | ||||
| // ✅ 修复:更新 inventory_lot_line 的 outQty | |||||
| // 修复:更新 inventory_lot_line 的 outQty | |||||
| val lotId = request.lotId ?: return | val lotId = request.lotId ?: return | ||||
| val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) | val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) | ||||
| if (inventoryLotLine != null) { | if (inventoryLotLine != null) { | ||||
| // ✅ 修复:计算新的 outQty,考虑之前的 outQty | |||||
| // 修复:计算新的 outQty,考虑之前的 outQty | |||||
| val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO | val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO | ||||
| val previousPickedQty = currentOutQty.minus(actualPickQty) // 计算之前已拣的数量 | val previousPickedQty = currentOutQty.minus(actualPickQty) // 计算之前已拣的数量 | ||||
| val newOutQty = previousPickedQty.add(actualPickQty) // 更新为新的总拣货数量 | val newOutQty = previousPickedQty.add(actualPickQty) // 更新为新的总拣货数量 | ||||
| @@ -569,7 +569,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 新方法:统一更新 inventory 表的 unavailableQty | |||||
| // 新方法:统一更新 inventory 表的 unavailableQty | |||||
| private fun updateInventoryUnavailableQty(itemId: Long, unavailableQty: BigDecimal) { | private fun updateInventoryUnavailableQty(itemId: Long, unavailableQty: BigDecimal) { | ||||
| try { | try { | ||||
| println("=== INVENTORY UNAVAILABLE QTY UPDATE (TRIGGER HANDLED) ===") | println("=== INVENTORY UNAVAILABLE QTY UPDATE (TRIGGER HANDLED) ===") | ||||
| @@ -582,7 +582,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:更新 stock_out_line 状态 | |||||
| // 修复:更新 stock_out_line 状态 | |||||
| private fun updateStockOutLineStatus(request: PickExecutionIssueRequest, status: String) { | private fun updateStockOutLineStatus(request: PickExecutionIssueRequest, status: String) { | ||||
| val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( | val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( | ||||
| request.pickOrderLineId, | request.pickOrderLineId, | ||||
| @@ -592,7 +592,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| stockOutLines.forEach { stockOutLine -> | stockOutLines.forEach { stockOutLine -> | ||||
| stockOutLine.status = status | stockOutLine.status = status | ||||
| // ✅ FIX: Update qty to actualPickQty before setting status to rejected | |||||
| // FIX: Update qty to actualPickQty before setting status to rejected | |||||
| if (status == "rejected" && request.actualPickQty != null) { | if (status == "rejected" && request.actualPickQty != null) { | ||||
| stockOutLine.qty = request.actualPickQty.toDouble() | stockOutLine.qty = request.actualPickQty.toDouble() | ||||
| println("Updated stock out line ${stockOutLine.id} qty to: ${request.actualPickQty}") | println("Updated stock out line ${stockOutLine.id} qty to: ${request.actualPickQty}") | ||||
| @@ -605,7 +605,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| println("Updated stock out line ${stockOutLine.id} status to: ${status}") | println("Updated stock out line ${stockOutLine.id} status to: ${status}") | ||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:使用 REQUIRES_NEW 传播级别,避免事务冲突 | |||||
| // 修复:使用 REQUIRES_NEW 传播级别,避免事务冲突 | |||||
| @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | ||||
| private fun resuggestPickOrder(pickOrderId: Long?) { | private fun resuggestPickOrder(pickOrderId: Long?) { | ||||
| if (pickOrderId != null) { | if (pickOrderId != null) { | ||||
| @@ -639,7 +639,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| "jo" -> { | "jo" -> { | ||||
| // Case 1: 只返回 Job Order 的 issues | // Case 1: 只返回 Job Order 的 issues | ||||
| val joIssues = pickExecutionIssueRepository.findJoIssues() | val joIssues = pickExecutionIssueRepository.findJoIssues() | ||||
| println("✅ Finding JO issues: ${joIssues.size} records") | |||||
| println(" Finding JO issues: ${joIssues.size} records") | |||||
| joIssues.forEach { | joIssues.forEach { | ||||
| println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}") | println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}") | ||||
| } | } | ||||
| @@ -648,7 +648,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| "do" -> { | "do" -> { | ||||
| // Case 2: 只返回 Delivery Order 的 issues | // Case 2: 只返回 Delivery Order 的 issues | ||||
| val doIssues = pickExecutionIssueRepository.findDoIssues() | val doIssues = pickExecutionIssueRepository.findDoIssues() | ||||
| println("✅ Finding DO issues: ${doIssues.size} records") | |||||
| println(" Finding DO issues: ${doIssues.size} records") | |||||
| doIssues.forEach { | doIssues.forEach { | ||||
| println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}") | println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}") | ||||
| } | } | ||||
| @@ -657,7 +657,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| "material" -> { | "material" -> { | ||||
| // Case 3: 只返回 Material 的 issues (普通 pick order) | // Case 3: 只返回 Material 的 issues (普通 pick order) | ||||
| val materialIssues = pickExecutionIssueRepository.findMaterialIssues() | val materialIssues = pickExecutionIssueRepository.findMaterialIssues() | ||||
| println("✅ Finding Material issues: ${materialIssues.size} records") | |||||
| println(" Finding Material issues: ${materialIssues.size} records") | |||||
| materialIssues.forEach { | materialIssues.forEach { | ||||
| println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}") | println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}") | ||||
| } | } | ||||
| @@ -666,7 +666,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty | |||||
| else -> { | else -> { | ||||
| // Case 4: 返回所有 issues | // Case 4: 返回所有 issues | ||||
| val allIssues = pickExecutionIssueRepository.findByDeletedFalse() | val allIssues = pickExecutionIssueRepository.findByDeletedFalse() | ||||
| println("✅ Finding ALL issues: ${allIssues.size} records") | |||||
| println(" Finding ALL issues: ${allIssues.size} records") | |||||
| allIssues | allIssues | ||||
| } | } | ||||
| } | } | ||||
| @@ -772,7 +772,7 @@ open fun createBatchReleaseIssue( | |||||
| createdCount++ | createdCount++ | ||||
| } | } | ||||
| println("✅ Created $createdCount ${issueCategory.name} issues for DO $deliveryOrderId") | |||||
| println(" Created $createdCount ${issueCategory.name} issues for DO $deliveryOrderId") | |||||
| return createdCount | return createdCount | ||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| @@ -3,7 +3,7 @@ package com.ffii.fpsms.modules.pickOrder.web | |||||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue | import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue | ||||
| import com.ffii.fpsms.modules.pickOrder.service.PickExecutionIssueService // ✅ 修复导入路径 | |||||
| import com.ffii.fpsms.modules.pickOrder.service.PickExecutionIssueService // 修复导入路径 | |||||
| import com.ffii.fpsms.modules.stock.web.model.PickExecutionIssueRequest | import com.ffii.fpsms.modules.stock.web.model.PickExecutionIssueRequest | ||||
| import org.springframework.web.bind.annotation.* | import org.springframework.web.bind.annotation.* | ||||
| @@ -14,7 +14,7 @@ import com.ffii.fpsms.modules.master.entity.Bom | |||||
| @Entity | @Entity | ||||
| @Table(name = "productprocess") | @Table(name = "productprocess") | ||||
| open class ProductProcess : BaseEntity<Long>() { | open class ProductProcess : BaseEntity<Long>() { | ||||
| @Size(max = 50) | @Size(max = 50) | ||||
| @NotNull | @NotNull | ||||
| @Column(name = "productprocesscode", nullable = false, unique = true, length = 50) | @Column(name = "productprocesscode", nullable = false, unique = true, length = 50) | ||||
| @@ -14,12 +14,12 @@ import com.ffii.fpsms.modules.master.entity.BomProcess | |||||
| open class ProductProcessLine : BaseEntity<Long>() { | open class ProductProcessLine : BaseEntity<Long>() { | ||||
| // ✅ 添加 @ManyToOne | |||||
| // 添加 @ManyToOne | |||||
| @ManyToOne(fetch = FetchType.LAZY) | @ManyToOne(fetch = FetchType.LAZY) | ||||
| @JoinColumn(name = "operatorId") | @JoinColumn(name = "operatorId") | ||||
| open var operator: User? = null | open var operator: User? = null | ||||
| // ✅ 添加 @ManyToOne | |||||
| // 添加 @ManyToOne | |||||
| @ManyToOne(fetch = FetchType.LAZY) | @ManyToOne(fetch = FetchType.LAZY) | ||||
| @JoinColumn(name = "equipmentId") | @JoinColumn(name = "equipmentId") | ||||
| open var equipment: Equipment? = null | open var equipment: Equipment? = null | ||||
| @@ -35,7 +35,7 @@ open class ProductProcessLine : BaseEntity<Long>() { | |||||
| @Column(name = "equipment_name", length = 100) | @Column(name = "equipment_name", length = 100) | ||||
| open var equipmentType: String? = null | open var equipmentType: String? = null | ||||
| // ✅ 添加 @ManyToOne | |||||
| // 添加 @ManyToOne | |||||
| @ManyToOne(fetch = FetchType.LAZY) | @ManyToOne(fetch = FetchType.LAZY) | ||||
| @JoinColumn(name = "byproductId") | @JoinColumn(name = "byproductId") | ||||
| open var byproduct: Items? = null | open var byproduct: Items? = null | ||||
| @@ -8,4 +8,6 @@ interface ProductProcessLineRepository : JpaRepository<ProductProcessLine, Long> | |||||
| fun findByProductProcess_Id(productProcessId: Long): List<ProductProcessLine> | fun findByProductProcess_Id(productProcessId: Long): List<ProductProcessLine> | ||||
| fun findByProductProcess_IdAndHandler_Id(productProcessId: Long, handlerId: Long): List<ProductProcessLine> | fun findByProductProcess_IdAndHandler_Id(productProcessId: Long, handlerId: Long): List<ProductProcessLine> | ||||
| fun findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId: Long): List<ProductProcessLine> | fun findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId: Long): List<ProductProcessLine> | ||||
| fun findByProductProcess_IdIn(ids: List<Long>): List<ProductProcessLine> | |||||
| } | } | ||||
| @@ -8,8 +8,8 @@ import jakarta.validation.constraints.Size | |||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
| @Entity | @Entity | ||||
| @Table(name = "productionprocessissue") // ✅ 修复:改为正确的表名 | |||||
| open class ProductionProcessIssue : BaseEntity<Long>() { // ✅ 修复:改为正确的类名 | |||||
| @Table(name = "productionprocessissue") // 修复:改为正确的表名 | |||||
| open class ProductionProcessIssue : BaseEntity<Long>() { // 修复:改为正确的类名 | |||||
| @ManyToOne(fetch = FetchType.LAZY) | @ManyToOne(fetch = FetchType.LAZY) | ||||
| @JoinColumn(name = "productprocessid", nullable = false) | @JoinColumn(name = "productprocessid", nullable = false) | ||||
| @@ -45,7 +45,7 @@ open class ProductProcessService( | |||||
| open fun findAll(pageable: Pageable): Page<ProductProcess> { | open fun findAll(pageable: Pageable): Page<ProductProcess> { | ||||
| println("📋 Service: Finding all ProductProcess with page: ${pageable.pageNumber}, size: ${pageable.pageSize}") | println("📋 Service: Finding all ProductProcess with page: ${pageable.pageNumber}, size: ${pageable.pageSize}") | ||||
| val result = productProcessRepository.findAll(pageable) | val result = productProcessRepository.findAll(pageable) | ||||
| println("✅ Service: Found ${result.totalElements} records") | |||||
| println(" Service: Found ${result.totalElements} records") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -89,14 +89,14 @@ open class ProductProcessService( | |||||
| } | } | ||||
| val savedProcess = productProcessRepository.save(productProcess) | val savedProcess = productProcessRepository.save(productProcess) | ||||
| println("✅ Service: ProductProcess created with ID: ${savedProcess.id}") | |||||
| println(" Service: ProductProcess created with ID: ${savedProcess.id}") | |||||
| // 4. 查询 BOM 的所有工序步骤 | // 4. 查询 BOM 的所有工序步骤 | ||||
| val bomProcesses = bomProcessRepository.findByBomIdOrderBySeqNo(request.bomId) | val bomProcesses = bomProcessRepository.findByBomIdOrderBySeqNo(request.bomId) | ||||
| println("🔍 Service: Found ${bomProcesses.size} BOM processes") | println("🔍 Service: Found ${bomProcesses.size} BOM processes") | ||||
| // 5. 为每个 BOM Process 创建 ProductProcessLine | // 5. 为每个 BOM Process 创建 ProductProcessLine | ||||
| bomProcesses.forEachIndexed { index, bomProcess -> // ✅ 修复 forEach | |||||
| bomProcesses.forEachIndexed { index, bomProcess -> // 修复 forEach | |||||
| val line = ProductProcessLine().apply { | val line = ProductProcessLine().apply { | ||||
| this.productProcess = savedProcess | this.productProcess = savedProcess | ||||
| this.bomProcess = bomProcess | this.bomProcess = bomProcess | ||||
| @@ -109,7 +109,7 @@ open class ProductProcessService( | |||||
| println("➕ Service: Created line ${index + 1} - seq: ${line.seqNo}, name: ${line.name}") | println("➕ Service: Created line ${index + 1} - seq: ${line.seqNo}, name: ${line.name}") | ||||
| } | } | ||||
| println("✅ Service: All ${bomProcesses.size} lines created automatically") | |||||
| println(" Service: All ${bomProcesses.size} lines created automatically") | |||||
| return SaveProductProcessResponse( | return SaveProductProcessResponse( | ||||
| id = savedProcess.id!!, | id = savedProcess.id!!, | ||||
| @@ -126,11 +126,11 @@ open class ProductProcessService( | |||||
| } | } | ||||
| // ✅ 添加:查询工序的所有步骤 | |||||
| // 添加:查询工序的所有步骤 | |||||
| open fun getLines(productProcessId: Long): List<ProductProcessLine> { | open fun getLines(productProcessId: Long): List<ProductProcessLine> { | ||||
| println("📋 Service: Getting lines for process ID: $productProcessId") | println("📋 Service: Getting lines for process ID: $productProcessId") | ||||
| val lines = productProcessLineRepository.findByProductProcess_Id(productProcessId) | val lines = productProcessLineRepository.findByProductProcess_Id(productProcessId) | ||||
| println("✅ Service: Found ${lines.size} lines") | |||||
| println(" Service: Found ${lines.size} lines") | |||||
| return lines | return lines | ||||
| } | } | ||||
| @@ -140,7 +140,7 @@ open class ProductProcessService( | |||||
| productProcess.status = ProductProcessStatus.IN_PROGRESS | productProcess.status = ProductProcessStatus.IN_PROGRESS | ||||
| productProcess.startTime = LocalDateTime.now() | productProcess.startTime = LocalDateTime.now() | ||||
| val saved = productProcessRepository.save(productProcess) | val saved = productProcessRepository.save(productProcess) | ||||
| println("✅ Service: Process started, status: ${saved.status}") | |||||
| println(" Service: Process started, status: ${saved.status}") | |||||
| return saved | return saved | ||||
| } | } | ||||
| @@ -160,7 +160,7 @@ open class ProductProcessService( | |||||
| productProcessRepository.save(productProcess) | productProcessRepository.save(productProcess) | ||||
| val savedIssue = productionProcessIssueRepository.save(issue) | val savedIssue = productionProcessIssueRepository.save(issue) | ||||
| println("✅ Service: Process stopped, issue ID: ${savedIssue.id}") | |||||
| println(" Service: Process stopped, issue ID: ${savedIssue.id}") | |||||
| return savedIssue | return savedIssue | ||||
| } | } | ||||
| @@ -178,7 +178,7 @@ open class ProductProcessService( | |||||
| productProcessRepository.save(productProcess) | productProcessRepository.save(productProcess) | ||||
| val savedIssue = productionProcessIssueRepository.save(issue) | val savedIssue = productionProcessIssueRepository.save(issue) | ||||
| println("✅ Service: Process resumed, total stop time: ${savedIssue.totalTime} minutes") | |||||
| println(" Service: Process resumed, total stop time: ${savedIssue.totalTime} minutes") | |||||
| return savedIssue | return savedIssue | ||||
| } | } | ||||
| @@ -188,7 +188,7 @@ open class ProductProcessService( | |||||
| productProcess.status = ProductProcessStatus.COMPLETED | productProcess.status = ProductProcessStatus.COMPLETED | ||||
| productProcess.endTime = LocalDateTime.now() | productProcess.endTime = LocalDateTime.now() | ||||
| val saved = productProcessRepository.save(productProcess) | val saved = productProcessRepository.save(productProcess) | ||||
| println("✅ Service: Process completed") | |||||
| println(" Service: Process completed") | |||||
| return saved | return saved | ||||
| } | } | ||||
| @@ -206,7 +206,7 @@ open class ProductProcessService( | |||||
| } | } | ||||
| val saved = productProcessLineRepository.save(line) | val saved = productProcessLineRepository.save(line) | ||||
| println("✅ Service: Line added with ID: ${saved.id}") | |||||
| println(" Service: Line added with ID: ${saved.id}") | |||||
| return SaveProductProcessLineResponse(saved.id!!) | return SaveProductProcessLineResponse(saved.id!!) | ||||
| } | } | ||||
| @@ -230,20 +230,20 @@ open class ProductProcessService( | |||||
| } | } | ||||
| val saved = productProcessLineRepository.save(line) | val saved = productProcessLineRepository.save(line) | ||||
| println("✅ Service: Line output updated") | |||||
| println(" Service: Line output updated") | |||||
| return saved | return saved | ||||
| } | } | ||||
| open fun getIssues(productProcessId: Long): List<ProductionProcessIssue> { | open fun getIssues(productProcessId: Long): List<ProductionProcessIssue> { | ||||
| println("📋 Service: Getting issues for ProductProcess ID: $productProcessId") | println("📋 Service: Getting issues for ProductProcess ID: $productProcessId") | ||||
| val issues = productionProcessIssueRepository.findByProductProcess_Id(productProcessId) | val issues = productionProcessIssueRepository.findByProductProcess_Id(productProcessId) | ||||
| println("✅ Service: Found ${issues.size} issues") | |||||
| println(" Service: Found ${issues.size} issues") | |||||
| return issues | return issues | ||||
| } | } | ||||
| open fun findByJobOrderId(jobOrderId: Long): List<ProductProcess> { | open fun findByJobOrderId(jobOrderId: Long): List<ProductProcess> { | ||||
| println("🔍 Service: Finding ProductProcess by jobOrderId: $jobOrderId") | println("🔍 Service: Finding ProductProcess by jobOrderId: $jobOrderId") | ||||
| val result = productProcessRepository.findByJobOrder_Id(jobOrderId) | val result = productProcessRepository.findByJobOrder_Id(jobOrderId) | ||||
| println("✅ Service: Found ${result.size} processes") | |||||
| println(" Service: Found ${result.size} processes") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -280,7 +280,7 @@ open class ProductProcessService( | |||||
| ) | ) | ||||
| } | } | ||||
| println("✅ Service: Returning ${result.size} processes with ${result.sumOf { it.lines.size }} total lines") | |||||
| println(" Service: Returning ${result.size} processes with ${result.sumOf { it.lines.size }} total lines") | |||||
| return result | return result | ||||
| } | } | ||||
| open fun findAllAsDto(pageable: Pageable): Page<ProductProcessSimpleResponse> { | open fun findAllAsDto(pageable: Pageable): Page<ProductProcessSimpleResponse> { | ||||
| @@ -300,7 +300,7 @@ open class ProductProcessService( | |||||
| ) | ) | ||||
| } | } | ||||
| println("✅ Service: Converted ${dtoList.size} entities to DTOs") | |||||
| println(" Service: Converted ${dtoList.size} entities to DTOs") | |||||
| return org.springframework.data.domain.PageImpl(dtoList, pageable, entityPage.totalElements) | return org.springframework.data.domain.PageImpl(dtoList, pageable, entityPage.totalElements) | ||||
| } | } | ||||
| @@ -435,7 +435,7 @@ open class ProductProcessService( | |||||
| val line = productProcessLineRepository.findById(lineId) | val line = productProcessLineRepository.findById(lineId) | ||||
| .orElseThrow { IllegalArgumentException("Line not found") } | .orElseThrow { IllegalArgumentException("Line not found") } | ||||
| // ✅ 修复:使用 UserRepository 获取 User 对象 | |||||
| // 修复:使用 UserRepository 获取 User 对象 | |||||
| val user = userRepository.findById(userId) | val user = userRepository.findById(userId) | ||||
| .orElseThrow { IllegalArgumentException("User not found with id: $userId") } | .orElseThrow { IllegalArgumentException("User not found with id: $userId") } | ||||
| @@ -443,13 +443,13 @@ open class ProductProcessService( | |||||
| line.startTime = LocalDateTime.now() | line.startTime = LocalDateTime.now() | ||||
| val saved = productProcessLineRepository.save(line) | val saved = productProcessLineRepository.save(line) | ||||
| println("✅ Service: Line started, handlerId: ${saved.handler?.id}") | |||||
| println(" Service: Line started, handlerId: ${saved.handler?.id}") | |||||
| return saved | return saved | ||||
| } | } | ||||
| open fun findPendingLinesByHandlerId(handlerId: Long): List<ProductProcessLine> { | open fun findPendingLinesByHandlerId(handlerId: Long): List<ProductProcessLine> { | ||||
| println("🔍 Service: Finding pending lines for handlerId: $handlerId") | println("🔍 Service: Finding pending lines for handlerId: $handlerId") | ||||
| val lines = productProcessLineRepository.findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId) | val lines = productProcessLineRepository.findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId) | ||||
| println("✅ Service: Found ${lines.size} pending lines") | |||||
| println(" Service: Found ${lines.size} pending lines") | |||||
| return lines | return lines | ||||
| } | } | ||||
| @@ -753,42 +753,45 @@ open fun updateProductProcessLineQty(request: UpdateProductProcessLineQtyRequest | |||||
| errorPosition = null, | errorPosition = null, | ||||
| ) | ) | ||||
| } | } | ||||
| /* | |||||
| open fun getAllJoborderProductProcessInfo(): List<AllJoborderProductProcessInfoResponse> { | |||||
| val productProcesses = productProcessRepository.findAllByDeletedIsFalse() | |||||
| val productProcessId=productProcesses.map { it.id } | |||||
| val productProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcessId.map { it.id }) | |||||
| return productProcesses.map { process -> | |||||
| AllJoborderProductProcessInfoResponse( | |||||
| id = productProcesses.id, | |||||
| productProcessCode = productProcesses.productProcessCode, | |||||
| status = productProcesses.status, | |||||
| startTime = productProcesses.startTime, | |||||
| endTime = productProcesses.endTime, | |||||
| date = productProcesses.date, | |||||
| bomId = productProcesses.bom.id, | |||||
| bomName = productProcesses.bom.name, | |||||
| jobOrderId = productProcesses.jobOrder.id, | |||||
| lines = productProcessLines.map { line -> | |||||
| ProductProcessInfoResponse( | |||||
| id = line.id?:0, | |||||
| productProcessCode = process.productProcessCode?:"", | |||||
| operatorId = process.operator?.id?:0, | |||||
| operatorName = process.operator?.name?:"", | |||||
| equipmentId = line.equipment?.id?:0, | |||||
| equipmentName = process.equipment?.name?:"", | |||||
| startTime = line.startTime?:LocalDateTime.now(), | |||||
| endTime = line.endTime?:LocalDateTime.now(), | |||||
| date = line.date?:LocalDate.now(), | |||||
| status = line.status?:"", | |||||
| bomId = process.bom?.id?:0, | |||||
| bomName = process.bom?.name?:"", | |||||
| jobOrderId = process.jobOrder?.id?:0 | |||||
| ) | |||||
| } | |||||
| ) | |||||
| open fun getAllJoborderProductProcessInfo(): List<AllJoborderProductProcessInfoResponse> { | |||||
| val productProcesses = productProcessRepository.findAllByDeletedIsFalse() | |||||
| val productProcessIds = productProcesses.map { it.id } | |||||
| return productProcesses.map { productProcesses -> | |||||
| val productProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcesses.id?:0L) | |||||
| val jobOrder = jobOrderRepository.findById(productProcesses.jobOrder?.id?:0L).orElse(null) | |||||
| val FinishedProductProcessLineCount = productProcessLineRepository.findByProductProcess_Id(productProcesses.id?:0L).count { it.status == "Completed" } | |||||
| AllJoborderProductProcessInfoResponse( | |||||
| id = productProcesses.id ?: 0L, | |||||
| productProcessCode = productProcesses.productProcessCode, | |||||
| status = productProcesses.status.value, | |||||
| startTime = productProcesses.startTime, | |||||
| endTime = productProcesses.endTime, | |||||
| date = productProcesses.date, | |||||
| bomId = productProcesses.bom?.id, | |||||
| itemName = productProcesses.item?.name, | |||||
| jobOrderId = productProcesses.jobOrder?.id, | |||||
| jobOrderCode = jobOrder?.code, | |||||
| productProcessLineCount = productProcessLines.size, | |||||
| FinishedProductProcessLineCount = FinishedProductProcessLineCount, | |||||
| lines = productProcessLines | |||||
| .map { line -> | |||||
| ProductProcessInfoResponse( | |||||
| id = line.id ?: 0, | |||||
| operatorId = line.operator?.id, | |||||
| operatorName = line.operator?.name ?: "", | |||||
| equipmentId = line.equipment?.id, | |||||
| equipmentName = line.equipmentType?: "", | |||||
| startTime = line.startTime, | |||||
| endTime = line.endTime, | |||||
| status = line.status ?: "" | |||||
| ) | |||||
| } | |||||
| ) | |||||
| } | |||||
| } | } | ||||
| } | |||||
| */ | |||||
| } | } | ||||
| @@ -16,18 +16,18 @@ class ProductProcessController( | |||||
| ) { | ) { | ||||
| @GetMapping | @GetMapping | ||||
| fun findAll(pageable: Pageable): Page<ProductProcessSimpleResponse> { // ✅ 改为返回 DTO | |||||
| fun findAll(pageable: Pageable): Page<ProductProcessSimpleResponse> { // 改为返回 DTO | |||||
| println("📋 Controller: GET /product-process - Finding all") | println("📋 Controller: GET /product-process - Finding all") | ||||
| val result = productProcessService.findAllAsDto(pageable) // ✅ 使用新方法 | |||||
| println("✅ Controller: Returning ${result.totalElements} records") | |||||
| val result = productProcessService.findAllAsDto(pageable) // 使用新方法 | |||||
| println(" Controller: Returning ${result.totalElements} records") | |||||
| return result | return result | ||||
| } | } | ||||
| @GetMapping("/{id}") | @GetMapping("/{id}") | ||||
| fun findById(@PathVariable id: Long): ProductProcessSimpleResponse { // ✅ 改为返回 DTO | |||||
| fun findById(@PathVariable id: Long): ProductProcessSimpleResponse { // 改为返回 DTO | |||||
| println("🔍 Controller: GET /product-process/$id") | println("🔍 Controller: GET /product-process/$id") | ||||
| val result = productProcessService.findByIdAsDto(id) // ✅ 使用新方法 | |||||
| println("✅ Controller: Found: ${result.productProcessCode}") | |||||
| val result = productProcessService.findByIdAsDto(id) // 使用新方法 | |||||
| println(" Controller: Found: ${result.productProcessCode}") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -35,24 +35,24 @@ class ProductProcessController( | |||||
| fun findByCode(@PathVariable code: String): ProductProcess { | fun findByCode(@PathVariable code: String): ProductProcess { | ||||
| println("🔍 Controller: GET /product-process/code/$code") | println("🔍 Controller: GET /product-process/code/$code") | ||||
| val result = productProcessService.findByCode(code) | val result = productProcessService.findByCode(code) | ||||
| println("✅ Controller: Found ID: ${result.id}") | |||||
| println(" Controller: Found ID: ${result.id}") | |||||
| return result | return result | ||||
| } | } | ||||
| @PostMapping | @PostMapping | ||||
| fun create(@RequestBody request: SaveProductProcessRequest): SaveProductProcessResponse { | fun create(@RequestBody request: SaveProductProcessRequest): SaveProductProcessResponse { | ||||
| println("💾 Controller: POST /product-process - Creating ProductProcess") | println("💾 Controller: POST /product-process - Creating ProductProcess") | ||||
| //println("📦 Controller: Request - bomId: ${request.bomId}, jobOrderId: ${request.jobOrderId}, date: ${request.date}") // ✅ 修复 | |||||
| //println("📦 Controller: Request - bomId: ${request.bomId}, jobOrderId: ${request.jobOrderId}, date: ${request.date}") // 修复 | |||||
| val result = productProcessService.create(request) | val result = productProcessService.create(request) | ||||
| println("✅ Controller: Created ProductProcess - ID: ${result.id}, Code: ${result.productProcessCode}, Lines: ${result.linesCreated}") // ✅ 改进日志 | |||||
| println(" Controller: Created ProductProcess - ID: ${result.id}, Code: ${result.productProcessCode}, Lines: ${result.linesCreated}") // 改进日志 | |||||
| return result | return result | ||||
| } | } | ||||
| @PostMapping("/{id}/start") | @PostMapping("/{id}/start") | ||||
| fun startProcess(@PathVariable id: Long, @RequestParam operatorId: Long): ProductProcessSimpleResponse { | fun startProcess(@PathVariable id: Long, @RequestParam operatorId: Long): ProductProcessSimpleResponse { | ||||
| println("▶️ Controller: POST /product-process/$id/start - operatorId: $operatorId") | println("▶️ Controller: POST /product-process/$id/start - operatorId: $operatorId") | ||||
| val result = productProcessService.startProcessAsDto(id, operatorId) // ✅ 改用 AsDto 方法 | |||||
| println("✅ Controller: Process started") | |||||
| val result = productProcessService.startProcessAsDto(id, operatorId) // 改用 AsDto 方法 | |||||
| println(" Controller: Process started") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -60,11 +60,11 @@ class ProductProcessController( | |||||
| fun stopProcess( | fun stopProcess( | ||||
| @PathVariable id: Long, | @PathVariable id: Long, | ||||
| @RequestBody request: StopProcessRequest | @RequestBody request: StopProcessRequest | ||||
| ): ProductionProcessIssueResponse { // ✅ 改为 Issue Response | |||||
| ): ProductionProcessIssueResponse { // 改为 Issue Response | |||||
| println("⏸️ Controller: POST /product-process/$id/stop") | println("⏸️ Controller: POST /product-process/$id/stop") | ||||
| println("📦 Controller: Reason: ${request.reason}") | println("📦 Controller: Reason: ${request.reason}") | ||||
| val result = productProcessService.stopProcessAsDto(id, request) // ✅ 使用返回 DTO 的方法 | |||||
| println("✅ Controller: Process stopped, issue ID: ${result.id}") | |||||
| val result = productProcessService.stopProcessAsDto(id, request) // 使用返回 DTO 的方法 | |||||
| println(" Controller: Process stopped, issue ID: ${result.id}") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -72,18 +72,18 @@ class ProductProcessController( | |||||
| fun resumeProcess( | fun resumeProcess( | ||||
| @PathVariable id: Long, | @PathVariable id: Long, | ||||
| @PathVariable issueId: Long | @PathVariable issueId: Long | ||||
| ): ProductionProcessIssueResponse { // ✅ 改为 Issue Response | |||||
| ): ProductionProcessIssueResponse { // 改为 Issue Response | |||||
| println("▶️ Controller: POST /product-process/$id/resume/$issueId") | println("▶️ Controller: POST /product-process/$id/resume/$issueId") | ||||
| val result = productProcessService.resumeProcessAsDto(id, issueId) // ✅ 使用返回 DTO 的方法 | |||||
| println("✅ Controller: Process resumed, stop duration: ${result.totalTime} min") | |||||
| val result = productProcessService.resumeProcessAsDto(id, issueId) // 使用返回 DTO 的方法 | |||||
| println(" Controller: Process resumed, stop duration: ${result.totalTime} min") | |||||
| return result | return result | ||||
| } | } | ||||
| @PostMapping("/{id}/complete") | @PostMapping("/{id}/complete") | ||||
| fun completeProcess(@PathVariable id: Long): ProductProcessSimpleResponse { | fun completeProcess(@PathVariable id: Long): ProductProcessSimpleResponse { | ||||
| println("✔️ Controller: POST /product-process/$id/complete") | println("✔️ Controller: POST /product-process/$id/complete") | ||||
| val result = productProcessService.completeProcessAsDto(id) // ✅ 改用 AsDto 方法 | |||||
| println("✅ Controller: Process completed") | |||||
| val result = productProcessService.completeProcessAsDto(id) // 改用 AsDto 方法 | |||||
| println(" Controller: Process completed") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -95,7 +95,7 @@ class ProductProcessController( | |||||
| println("➕ Controller: POST /product-process/$id/lines") | println("➕ Controller: POST /product-process/$id/lines") | ||||
| println("📦 Controller: Line - name: ${request.name}, equipment: ${request.equipmentType}") | println("📦 Controller: Line - name: ${request.name}, equipment: ${request.equipmentType}") | ||||
| val result = productProcessService.addLine(id, request) | val result = productProcessService.addLine(id, request) | ||||
| println("✅ Controller: Line added with ID: ${result.id}") | |||||
| println(" Controller: Line added with ID: ${result.id}") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -103,10 +103,10 @@ class ProductProcessController( | |||||
| fun updateLineOutput( | fun updateLineOutput( | ||||
| @PathVariable lineId: Long, | @PathVariable lineId: Long, | ||||
| @RequestBody request: UpdateLineOutputRequest | @RequestBody request: UpdateLineOutputRequest | ||||
| ): ProductProcessLineResponse { // ✅ 改为 ProductProcessLineResponse | |||||
| ): ProductProcessLineResponse { // 改为 ProductProcessLineResponse | |||||
| println("📊 Controller: PUT /lines/$lineId/output") | println("📊 Controller: PUT /lines/$lineId/output") | ||||
| val result = productProcessService.updateLineOutputAsDto(lineId, request) | val result = productProcessService.updateLineOutputAsDto(lineId, request) | ||||
| println("✅ Controller: Line output updated") | |||||
| println(" Controller: Line output updated") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -114,32 +114,32 @@ class ProductProcessController( | |||||
| fun getLines(@PathVariable id: Long): List<ProductProcessLineResponse> { | fun getLines(@PathVariable id: Long): List<ProductProcessLineResponse> { | ||||
| println("📋 Controller: GET /product-process/$id/lines") | println("📋 Controller: GET /product-process/$id/lines") | ||||
| val result = productProcessService.getLinesAsDto(id) | val result = productProcessService.getLinesAsDto(id) | ||||
| println("✅ Controller: Found ${result.size} lines") | |||||
| println(" Controller: Found ${result.size} lines") | |||||
| return result | return result | ||||
| } | } | ||||
| @GetMapping("/{id}/issues") | @GetMapping("/{id}/issues") | ||||
| fun getIssues(@PathVariable id: Long): List<ProductionProcessIssueResponse> { // ✅ 改为 Issue Response List | |||||
| fun getIssues(@PathVariable id: Long): List<ProductionProcessIssueResponse> { // 改为 Issue Response List | |||||
| println("📋 Controller: GET /product-process/$id/issues") | println("📋 Controller: GET /product-process/$id/issues") | ||||
| val result = productProcessService.getIssuesAsDto(id) // ✅ 使用返回 DTO 的方法 | |||||
| println("✅ Controller: Found ${result.size} issues") | |||||
| val result = productProcessService.getIssuesAsDto(id) // 使用返回 DTO 的方法 | |||||
| println(" Controller: Found ${result.size} issues") | |||||
| return result | return result | ||||
| } | } | ||||
| @GetMapping("/by-job-order/{jobOrderId}") | @GetMapping("/by-job-order/{jobOrderId}") | ||||
| fun findByJobOrderId(@PathVariable jobOrderId: Long): List<ProductProcessWithLinesResponse> { // ✅ 改为返回 DTO | |||||
| fun findByJobOrderId(@PathVariable jobOrderId: Long): List<ProductProcessWithLinesResponse> { // 改为返回 DTO | |||||
| println("🔍 Controller: GET /product-process/by-job-order/$jobOrderId") | println("🔍 Controller: GET /product-process/by-job-order/$jobOrderId") | ||||
| val result = productProcessService.findByJobOrderIdWithLines(jobOrderId) // ✅ 直接使用 with-lines 方法 | |||||
| println("✅ Controller: Found ${result.size} processes for Job Order $jobOrderId") | |||||
| val result = productProcessService.findByJobOrderIdWithLines(jobOrderId) // 直接使用 with-lines 方法 | |||||
| println(" Controller: Found ${result.size} processes for Job Order $jobOrderId") | |||||
| return result | return result | ||||
| } | } | ||||
| // ✅ 额外添加:获取带 lines 的完整数据 | |||||
| // 额外添加:获取带 lines 的完整数据 | |||||
| @GetMapping("/by-job-order/{jobOrderId}/with-lines") | @GetMapping("/by-job-order/{jobOrderId}/with-lines") | ||||
| fun findByJobOrderIdWithLines(@PathVariable jobOrderId: Long): List<ProductProcessWithLinesResponse> { | fun findByJobOrderIdWithLines(@PathVariable jobOrderId: Long): List<ProductProcessWithLinesResponse> { | ||||
| println("🔍 Controller: GET /product-process/by-job-order/$jobOrderId/with-lines") | println("🔍 Controller: GET /product-process/by-job-order/$jobOrderId/with-lines") | ||||
| val result = productProcessService.findByJobOrderIdWithLines(jobOrderId) | val result = productProcessService.findByJobOrderIdWithLines(jobOrderId) | ||||
| println("✅ Controller: Found ${result.size} processes with lines for Job Order $jobOrderId") | |||||
| println(" Controller: Found ${result.size} processes with lines for Job Order $jobOrderId") | |||||
| return result | return result | ||||
| } | } | ||||
| @@ -147,7 +147,7 @@ class ProductProcessController( | |||||
| fun startLine(@PathVariable lineId: Long, @RequestParam userId: Long): ProductProcessLineResponse { | fun startLine(@PathVariable lineId: Long, @RequestParam userId: Long): ProductProcessLineResponse { | ||||
| println("▶️ Controller: POST /product-process/lines/$lineId/start - userId: $userId") | println("▶️ Controller: POST /product-process/lines/$lineId/start - userId: $userId") | ||||
| val entity = productProcessService.startLine(lineId, userId) | val entity = productProcessService.startLine(lineId, userId) | ||||
| // ✅ 修复:返回正确的 DTO | |||||
| // 修复:返回正确的 DTO | |||||
| return productProcessService.getLinesAsDto(entity.productProcess?.id!!).find { it.id == entity.id!! }!! | return productProcessService.getLinesAsDto(entity.productProcess?.id!!).find { it.id == entity.id!! }!! | ||||
| } | } | ||||
| @@ -183,10 +183,10 @@ class ProductProcessController( | |||||
| fun demoupdateqty(@PathVariable lineId: Long, @RequestBody request: UpdateProductProcessLineQtyRequest): MessageResponse { | fun demoupdateqty(@PathVariable lineId: Long, @RequestBody request: UpdateProductProcessLineQtyRequest): MessageResponse { | ||||
| return productProcessService.updateProductProcessLineQty(request) | return productProcessService.updateProductProcessLineQty(request) | ||||
| } | } | ||||
| /* | |||||
| @GetMapping("/Demo/Process/all") | @GetMapping("/Demo/Process/all") | ||||
| fun demoprocessall(): List<AllJoborderProductProcessInfoResponse> { | fun demoprocessall(): List<AllJoborderProductProcessInfoResponse> { | ||||
| return productProcessService.getAllJoborderProductProcessInfo() | return productProcessService.getAllJoborderProductProcessInfo() | ||||
| } | } | ||||
| */ | |||||
| } | } | ||||
| @@ -27,8 +27,8 @@ data class ProductionProcessIssueResponse( | |||||
| ) | ) | ||||
| data class SaveProductProcessResponse( | data class SaveProductProcessResponse( | ||||
| val id: Long, | val id: Long, | ||||
| val productProcessCode: String? = null, // ✅ 添加 | |||||
| val linesCreated: Int = 0 // ✅ 添加 | |||||
| val productProcessCode: String? = null, // 添加 | |||||
| val linesCreated: Int = 0 // 添加 | |||||
| ) | ) | ||||
| data class ProductProcessWithLinesResponse( | data class ProductProcessWithLinesResponse( | ||||
| val id: Long, | val id: Long, | ||||
| @@ -142,22 +142,22 @@ data class AllJoborderProductProcessInfoResponse( | |||||
| val endTime: LocalDateTime?, | val endTime: LocalDateTime?, | ||||
| val date: LocalDate?, | val date: LocalDate?, | ||||
| val bomId: Long?, | val bomId: Long?, | ||||
| val bomName: String?, | |||||
| val itemName: String?, | |||||
| val jobOrderId: Long?, | val jobOrderId: Long?, | ||||
| val jobOrderCode: String?, | |||||
| val productProcessLineCount: Int, | |||||
| val FinishedProductProcessLineCount: Int, | |||||
| val lines: List<ProductProcessInfoResponse> | val lines: List<ProductProcessInfoResponse> | ||||
| ) | ) | ||||
| data class ProductProcessInfoResponse( | data class ProductProcessInfoResponse( | ||||
| val id: Long, | val id: Long, | ||||
| val productProcessCode: String?, | |||||
| val operatorId: Long?, | val operatorId: Long?, | ||||
| val operatorName: String?, | val operatorName: String?, | ||||
| val equipmentId: Long?, | val equipmentId: Long?, | ||||
| val equipmentName: String?, | val equipmentName: String?, | ||||
| val startTime: LocalDateTime?, | val startTime: LocalDateTime?, | ||||
| val endTime: LocalDateTime?, | val endTime: LocalDateTime?, | ||||
| val date: LocalDate?, | |||||
| val status: String, | val status: String, | ||||
| val bomId: Long?, | |||||
| val bomName: String?, | |||||
| val jobOrderId: Long? | |||||
| ) | ) | ||||
| @@ -121,7 +121,7 @@ open class InventoryLotLineService( | |||||
| val updatedLotLine = saveInventoryLotLine(updateRequest) | val updatedLotLine = saveInventoryLotLine(updateRequest) | ||||
| // ✅ ADD THIS: Update inventory table after lot line status change | |||||
| // ADD THIS: Update inventory table after lot line status change | |||||
| updateInventoryTable(updatedLotLine) | updateInventoryTable(updatedLotLine) | ||||
| return MessageResponse( | return MessageResponse( | ||||
| @@ -134,7 +134,7 @@ open class InventoryLotLineService( | |||||
| ) | ) | ||||
| } | } | ||||
| // ✅ ADD THIS: New method to update inventory table | |||||
| // ADD THIS: New method to update inventory table | |||||
| private fun updateInventoryTable(inventoryLotLine: InventoryLotLine) { | private fun updateInventoryTable(inventoryLotLine: InventoryLotLine) { | ||||
| try { | try { | ||||
| // Get the item ID from the inventory lot | // Get the item ID from the inventory lot | ||||
| @@ -97,7 +97,7 @@ val existingStockOutLine = stockOutLineRepository.findByPickOrderLineIdAndInvent | |||||
| ) | ) | ||||
| if (existingStockOutLine.isNotEmpty()) { | if (existingStockOutLine.isNotEmpty()) { | ||||
| // ✅ 如果已存在,返回 null 表示不需要创建 | |||||
| // 如果已存在,返回 null 表示不需要创建 | |||||
| return MessageResponse( | return MessageResponse( | ||||
| id = null, | id = null, | ||||
| name = "Stock out line already exists", | name = "Stock out line already exists", | ||||
| @@ -189,7 +189,7 @@ fun handleQc(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): Li | |||||
| @Transactional | @Transactional | ||||
| open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): MessageResponse { | open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): MessageResponse { | ||||
| try { | try { | ||||
| // ✅ Get stockOutId from pickOrderLineId with detailed error | |||||
| // Get stockOutId from pickOrderLineId with detailed error | |||||
| val stockOutId = getStockOutIdFromPickOrderLine(request.pickOrderLineId) | val stockOutId = getStockOutIdFromPickOrderLine(request.pickOrderLineId) | ||||
| println("Found stockOutId: $stockOutId for pickOrderLineId: ${request.pickOrderLineId}") | println("Found stockOutId: $stockOutId for pickOrderLineId: ${request.pickOrderLineId}") | ||||
| @@ -253,16 +253,16 @@ open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): Mes | |||||
| } | } | ||||
| } | } | ||||
| // ✅ Update helper method with detailed error messages | |||||
| // Update helper method with detailed error messages | |||||
| private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | ||||
| println("Getting stockOutId for pickOrderLineId: $pickOrderLineId") | println("Getting stockOutId for pickOrderLineId: $pickOrderLineId") | ||||
| // ✅ Fixed: Use poId instead of pick_order_id | |||||
| // Fixed: Use poId instead of pick_order_id | |||||
| val sql = """ | val sql = """ | ||||
| SELECT so.id as stockOutId, so.consoPickOrderCode, po.consoCode | SELECT so.id as stockOutId, so.consoPickOrderCode, po.consoCode | ||||
| FROM stock_out so | FROM stock_out so | ||||
| JOIN pick_order po ON po.consoCode = so.consoPickOrderCode | JOIN pick_order po ON po.consoCode = so.consoPickOrderCode | ||||
| JOIN pick_order_line pol ON pol.poId = po.id -- ✅ Fixed: Use poId | |||||
| JOIN pick_order_line pol ON pol.poId = po.id -- Fixed: Use poId | |||||
| WHERE pol.id = :pickOrderLineId | WHERE pol.id = :pickOrderLineId | ||||
| """.trimIndent() | """.trimIndent() | ||||
| @@ -569,7 +569,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | |||||
| throw e | throw e | ||||
| } | } | ||||
| } | } | ||||
| // ✅ ADD THIS: Handle lot rejection when stock out line is rejected | |||||
| // ADD THIS: Handle lot rejection when stock out line is rejected | |||||
| private fun handleLotRejectionFromStockOutLine(stockOutLine: StockOutLine) { | private fun handleLotRejectionFromStockOutLine(stockOutLine: StockOutLine) { | ||||
| try { | try { | ||||
| println("=== HANDLING LOT REJECTION FROM STOCK OUT LINE ===") | println("=== HANDLING LOT REJECTION FROM STOCK OUT LINE ===") | ||||
| @@ -612,7 +612,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | |||||
| } | } | ||||
| } | } | ||||
| // ✅ ADD THIS: Update inventory table after lot rejection | |||||
| // ADD THIS: Update inventory table after lot rejection | |||||
| private fun updateInventoryTableAfterLotRejection(inventoryLotLine: InventoryLotLine) { | private fun updateInventoryTableAfterLotRejection(inventoryLotLine: InventoryLotLine) { | ||||
| try { | try { | ||||
| println("=== UPDATING INVENTORY TABLE ===") | println("=== UPDATING INVENTORY TABLE ===") | ||||
| @@ -5,9 +5,9 @@ import com.ffii.fpsms.modules.pickOrder.entity.PickOrder | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine | import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository | import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // ✅ 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory | import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus // ✅ 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus // 添加 | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository | import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.* | import com.ffii.fpsms.modules.stock.entity.* | ||||
| import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus | import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus | ||||
| @@ -20,8 +20,8 @@ import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotResponse | |||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import java.math.BigDecimal | import java.math.BigDecimal | ||||
| import java.time.LocalDate | import java.time.LocalDate | ||||
| import java.time.LocalDateTime // ✅ 添加 | |||||
| import java.time.format.DateTimeFormatter // ✅ 添加 | |||||
| import java.time.LocalDateTime // 添加 | |||||
| import java.time.format.DateTimeFormatter // 添加 | |||||
| import kotlin.jvm.optionals.getOrDefault | import kotlin.jvm.optionals.getOrDefault | ||||
| import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
| @@ -46,7 +46,7 @@ open class SuggestedPickLotService( | |||||
| val pickOrderLineRepository: PickOrderLineRepository, | val pickOrderLineRepository: PickOrderLineRepository, | ||||
| val inventoryLotLineService: InventoryLotLineService, | val inventoryLotLineService: InventoryLotLineService, | ||||
| val itemUomService: ItemUomService, | val itemUomService: ItemUomService, | ||||
| val pickExecutionIssueRepository: PickExecutionIssueRepository, // ✅ 添加逗号 | |||||
| val pickExecutionIssueRepository: PickExecutionIssueRepository, // 添加逗号 | |||||
| val pickOrderRepository: PickOrderRepository, | val pickOrderRepository: PickOrderRepository, | ||||
| val inventoryRepository: InventoryRepository, | val inventoryRepository: InventoryRepository, | ||||
| val failInventoryLotLineRepository: FailInventoryLotLineRepository, | val failInventoryLotLineRepository: FailInventoryLotLineRepository, | ||||
| @@ -114,13 +114,13 @@ open class SuggestedPickLotService( | |||||
| val lotLines = availableInventoryLotLines[line.item?.id].orEmpty() | val lotLines = availableInventoryLotLines[line.item?.id].orEmpty() | ||||
| val ratio = one // (salesUnit?.ratioN ?: one).divide(salesUnit?.ratioD ?: one, 10, RoundingMode.HALF_UP) | val ratio = one // (salesUnit?.ratioN ?: one).divide(salesUnit?.ratioD ?: one, 10, RoundingMode.HALF_UP) | ||||
| // ✅ FIX: Calculate remaining quantity needed (not the full required quantity) | |||||
| // FIX: Calculate remaining quantity needed (not the full required quantity) | |||||
| val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(line.id!!) | val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(line.id!!) | ||||
| val totalPickedQty = stockOutLines | val totalPickedQty = stockOutLines | ||||
| .filter { | .filter { | ||||
| it.status == "completed" || | it.status == "completed" || | ||||
| it.status == "partially_completed" || | it.status == "partially_completed" || | ||||
| (it.status == "rejected" && (it.qty ?: zero) > zero) // ✅ 包含已 picked 的 rejected | |||||
| (it.status == "rejected" && (it.qty ?: zero) > zero) // 包含已 picked 的 rejected | |||||
| } | } | ||||
| .sumOf { it.qty ?: zero } | .sumOf { it.qty ?: zero } | ||||
| val requiredQty = line.qty ?: zero | val requiredQty = line.qty ?: zero | ||||
| @@ -132,7 +132,7 @@ open class SuggestedPickLotService( | |||||
| println("Remaining qty needed: $remainingQty") | println("Remaining qty needed: $remainingQty") | ||||
| println("Stock out lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}") | println("Stock out lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}") | ||||
| // ✅ FIX: Use remainingQty instead of line.qty | |||||
| // FIX: Use remainingQty instead of line.qty | |||||
| var remainingQtyToAllocate = remainingQty | var remainingQtyToAllocate = remainingQty | ||||
| println("remaining1 $remainingQtyToAllocate (sales units)") | println("remaining1 $remainingQtyToAllocate (sales units)") | ||||
| val updatedLotLines = mutableListOf<InventoryLotLineInfo>() | val updatedLotLines = mutableListOf<InventoryLotLineInfo>() | ||||
| @@ -142,7 +142,7 @@ open class SuggestedPickLotService( | |||||
| println("calculateRemainingQtyForInfo(lotLine) ${calculateRemainingQtyForInfo(lotLine)}") | println("calculateRemainingQtyForInfo(lotLine) ${calculateRemainingQtyForInfo(lotLine)}") | ||||
| // ✅ 修复:计算可用数量,转换为销售单位 | |||||
| // 修复:计算可用数量,转换为销售单位 | |||||
| val availableQtyInBaseUnits = calculateRemainingQtyForInfo(lotLine) | val availableQtyInBaseUnits = calculateRemainingQtyForInfo(lotLine) | ||||
| val holdQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero | val holdQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero | ||||
| val availableQtyInSalesUnits = availableQtyInBaseUnits | val availableQtyInSalesUnits = availableQtyInBaseUnits | ||||
| @@ -159,11 +159,11 @@ open class SuggestedPickLotService( | |||||
| val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() } | val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() } | ||||
| val originalHoldQty = inventoryLotLine?.holdQty | val originalHoldQty = inventoryLotLine?.holdQty | ||||
| // ✅ 修复:在销售单位中计算分配数量 | |||||
| // 修复:在销售单位中计算分配数量 | |||||
| val assignQtyInSalesUnits = minOf(availableQtyInSalesUnits, remainingQtyToAllocate) | val assignQtyInSalesUnits = minOf(availableQtyInSalesUnits, remainingQtyToAllocate) | ||||
| remainingQtyToAllocate = remainingQtyToAllocate.minus(assignQtyInSalesUnits) | remainingQtyToAllocate = remainingQtyToAllocate.minus(assignQtyInSalesUnits) | ||||
| val newHoldQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero | val newHoldQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero | ||||
| // ✅ 修复:将销售单位转换为基础单位来更新 holdQty | |||||
| // 修复:将销售单位转换为基础单位来更新 holdQty | |||||
| val assignQtyInBaseUnits = assignQtyInSalesUnits.multiply(ratio) | val assignQtyInBaseUnits = assignQtyInSalesUnits.multiply(ratio) | ||||
| holdQtyMap[lotLine.id] = (holdQtyMap[lotLine.id] ?: zero).plus(assignQtyInBaseUnits) | holdQtyMap[lotLine.id] = (holdQtyMap[lotLine.id] ?: zero).plus(assignQtyInBaseUnits) | ||||
| @@ -171,10 +171,10 @@ open class SuggestedPickLotService( | |||||
| type = SuggestedPickLotType.PICK_ORDER | type = SuggestedPickLotType.PICK_ORDER | ||||
| suggestedLotLine = inventoryLotLine | suggestedLotLine = inventoryLotLine | ||||
| pickOrderLine = line | pickOrderLine = line | ||||
| qty = assignQtyInSalesUnits // ✅ 保存销售单位 | |||||
| qty = assignQtyInSalesUnits // 保存销售单位 | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 修复:计算现有 suggestions 中 pending/checked 状态满足的数量 | |||||
| // 修复:计算现有 suggestions 中 pending/checked 状态满足的数量 | |||||
| var existingSatisfiedQty = BigDecimal.ZERO | var existingSatisfiedQty = BigDecimal.ZERO | ||||
| // 查询现有的 suggestions 用于这个 pick order line | // 查询现有的 suggestions 用于这个 pick order line | ||||
| @@ -194,7 +194,7 @@ open class SuggestedPickLotService( | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 调整 remainingQtyToAllocate,减去已经通过现有 suggestions 满足的数量 | |||||
| // 调整 remainingQtyToAllocate,减去已经通过现有 suggestions 满足的数量 | |||||
| remainingQtyToAllocate = remainingQtyToAllocate.minus(existingSatisfiedQty) | remainingQtyToAllocate = remainingQtyToAllocate.minus(existingSatisfiedQty) | ||||
| println("Existing satisfied qty: $existingSatisfiedQty") | println("Existing satisfied qty: $existingSatisfiedQty") | ||||
| println("Adjusted remaining qty: $remainingQtyToAllocate") | println("Adjusted remaining qty: $remainingQtyToAllocate") | ||||
| @@ -209,7 +209,7 @@ open class SuggestedPickLotService( | |||||
| type = SuggestedPickLotType.PICK_ORDER | type = SuggestedPickLotType.PICK_ORDER | ||||
| suggestedLotLine = null | suggestedLotLine = null | ||||
| pickOrderLine = line | pickOrderLine = line | ||||
| qty = remainingQtyToAllocate // ✅ 保存销售单位 | |||||
| qty = remainingQtyToAllocate // 保存销售单位 | |||||
| } | } | ||||
| try { | try { | ||||
| /* | /* | ||||
| @@ -337,7 +337,7 @@ open class SuggestedPickLotService( | |||||
| } | } | ||||
| val savedStockOutLine = stockOutLIneRepository.saveAndFlush(stockOutLine) | val savedStockOutLine = stockOutLIneRepository.saveAndFlush(stockOutLine) | ||||
| println("✅ Created stock out line ID: ${savedStockOutLine.id} for suggestion ID: ${suggestion.id}") | |||||
| println(" Created stock out line ID: ${savedStockOutLine.id} for suggestion ID: ${suggestion.id}") | |||||
| return savedStockOutLine | return savedStockOutLine | ||||
| @@ -357,7 +357,7 @@ open class SuggestedPickLotService( | |||||
| println("Pick Order Code: ${pickOrder.code}") | println("Pick Order Code: ${pickOrder.code}") | ||||
| println("Pick Order Status: ${pickOrder.status}") | println("Pick Order Status: ${pickOrder.status}") | ||||
| // ✅ NEW: Get ALL pick orders for the same items | |||||
| // NEW: Get ALL pick orders for the same items | |||||
| val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct() | val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct() | ||||
| println("Item IDs in current pick order: $itemIds") | println("Item IDs in current pick order: $itemIds") | ||||
| @@ -373,7 +373,7 @@ open class SuggestedPickLotService( | |||||
| allCompetingPickOrders.addAll(competingOrders) | allCompetingPickOrders.addAll(competingOrders) | ||||
| } | } | ||||
| // ✅ FIX: Only resuggest pick orders that have rejected stock out lines | |||||
| // FIX: Only resuggest pick orders that have rejected stock out lines | |||||
| val allPickOrdersToResuggest = (listOf(pickOrder) + allCompetingPickOrders) | val allPickOrdersToResuggest = (listOf(pickOrder) + allCompetingPickOrders) | ||||
| .filter { pickOrderToCheck -> | .filter { pickOrderToCheck -> | ||||
| // Only resuggest if the pick order has rejected stock out lines | // Only resuggest if the pick order has rejected stock out lines | ||||
| @@ -397,7 +397,7 @@ open class SuggestedPickLotService( | |||||
| println("Filtered pick orders to resuggest: ${allPickOrdersToResuggest.size}") | println("Filtered pick orders to resuggest: ${allPickOrdersToResuggest.size}") | ||||
| println("Pick orders being resuggested: ${allPickOrdersToResuggest.map { "${it.code}(${it.status})" }}") | println("Pick orders being resuggested: ${allPickOrdersToResuggest.map { "${it.code}(${it.status})" }}") | ||||
| // ✅ FIX: Only resuggest if there are actually pick orders with rejected lots | |||||
| // FIX: Only resuggest if there are actually pick orders with rejected lots | |||||
| if (allPickOrdersToResuggest.isEmpty()) { | if (allPickOrdersToResuggest.isEmpty()) { | ||||
| println("No pick orders need resuggesting - no rejected lots found") | println("No pick orders need resuggesting - no rejected lots found") | ||||
| return MessageResponse( | return MessageResponse( | ||||
| @@ -410,21 +410,21 @@ open class SuggestedPickLotService( | |||||
| ) | ) | ||||
| } | } | ||||
| // ✅ FIX: Get all pick order line IDs for the orders to resuggest | |||||
| // FIX: Get all pick order line IDs for the orders to resuggest | |||||
| val allPickOrderLineIds = allPickOrdersToResuggest | val allPickOrderLineIds = allPickOrdersToResuggest | ||||
| .flatMap { it.pickOrderLines } | .flatMap { it.pickOrderLines } | ||||
| .mapNotNull { it.id } | .mapNotNull { it.id } | ||||
| println("All pick order line IDs to resuggest: $allPickOrderLineIds") | println("All pick order line IDs to resuggest: $allPickOrderLineIds") | ||||
| // ✅ FIX: Get all existing suggestions for these pick order lines | |||||
| // FIX: Get all existing suggestions for these pick order lines | |||||
| val allSuggestions = suggestedPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIds) | val allSuggestions = suggestedPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIds) | ||||
| println("Found ${allSuggestions.size} existing suggestions") | println("Found ${allSuggestions.size} existing suggestions") | ||||
| // ✅ 删除第 376-395 行的旧代码,替换为: | |||||
| // ✅ FIX: Separate suggestions to keep (those WITHOUT rejected stock out lines) and delete | |||||
| // 删除第 376-395 行的旧代码,替换为: | |||||
| // FIX: Separate suggestions to keep (those WITHOUT rejected stock out lines) and delete | |||||
| val suggestionsToKeep = allSuggestions.filter { suggestion -> | val suggestionsToKeep = allSuggestions.filter { suggestion -> | ||||
| val pickOrderLineId = suggestion.pickOrderLine?.id | val pickOrderLineId = suggestion.pickOrderLine?.id | ||||
| val suggestedLotLineId = suggestion.suggestedLotLine?.id | val suggestedLotLineId = suggestion.suggestedLotLine?.id | ||||
| @@ -434,14 +434,14 @@ val suggestionsToKeep = allSuggestions.filter { suggestion -> | |||||
| pickOrderLineId, | pickOrderLineId, | ||||
| suggestedLotLineId | suggestedLotLineId | ||||
| ) | ) | ||||
| // ✅ 保留没有 rejected stock out lines 的 suggestions | |||||
| // 保留没有 rejected stock out lines 的 suggestions | |||||
| !stockOutLines.any { it.status == "rejected" } | !stockOutLines.any { it.status == "rejected" } | ||||
| } else { | } else { | ||||
| true // 保留有问题的 suggestions 用于调试 | true // 保留有问题的 suggestions 用于调试 | ||||
| } | } | ||||
| } | } | ||||
| // ✅ 只删除有 rejected stock out lines 的 suggestions | |||||
| // 只删除有 rejected stock out lines 的 suggestions | |||||
| val suggestionsToDelete = allSuggestions.filter { suggestion -> | val suggestionsToDelete = allSuggestions.filter { suggestion -> | ||||
| val pickOrderLineId = suggestion.pickOrderLine?.id | val pickOrderLineId = suggestion.pickOrderLine?.id | ||||
| val suggestedLotLineId = suggestion.suggestedLotLine?.id | val suggestedLotLineId = suggestion.suggestedLotLine?.id | ||||
| @@ -451,7 +451,7 @@ val suggestionsToDelete = allSuggestions.filter { suggestion -> | |||||
| pickOrderLineId, | pickOrderLineId, | ||||
| suggestedLotLineId | suggestedLotLineId | ||||
| ) | ) | ||||
| stockOutLines.any { it.status == "rejected" } // ✅ 只删除 rejected 的 | |||||
| stockOutLines.any { it.status == "rejected" } // 只删除 rejected 的 | |||||
| } else { | } else { | ||||
| suggestedLotLineId == null | suggestedLotLineId == null | ||||
| } | } | ||||
| @@ -461,7 +461,7 @@ val suggestionsToDelete = allSuggestions.filter { suggestion -> | |||||
| println("Suggestions to keep (with rejected stock out lines): ${suggestionsToKeep.size}") | println("Suggestions to keep (with rejected stock out lines): ${suggestionsToKeep.size}") | ||||
| println("Suggestions to delete: ${suggestionsToDelete.size}") | println("Suggestions to delete: ${suggestionsToDelete.size}") | ||||
| // ✅ FIX: Clear holdQty ONLY for lots that have rejected stock out lines | |||||
| // FIX: Clear holdQty ONLY for lots that have rejected stock out lines | |||||
| val rejectedLotIds = suggestionsToDelete.mapNotNull { it.suggestedLotLine?.id }.distinct() | val rejectedLotIds = suggestionsToDelete.mapNotNull { it.suggestedLotLine?.id }.distinct() | ||||
| println("Rejected lot IDs (clearing holdQty only): $rejectedLotIds") | println("Rejected lot IDs (clearing holdQty only): $rejectedLotIds") | ||||
| @@ -477,7 +477,7 @@ val suggestionsToDelete = allSuggestions.filter { suggestion -> | |||||
| println("Keeping all suggestions (including rejected ones for display)") | println("Keeping all suggestions (including rejected ones for display)") | ||||
| // ✅ NEW: Build holdQtyMap with existing holdQty from other pick orders | |||||
| // NEW: Build holdQtyMap with existing holdQty from other pick orders | |||||
| val existingHoldQtyMap = mutableMapOf<Long?, BigDecimal?>() | val existingHoldQtyMap = mutableMapOf<Long?, BigDecimal?>() | ||||
| // Get all lots that are being used by other pick orders (including those NOT being resuggested) | // Get all lots that are being used by other pick orders (including those NOT being resuggested) | ||||
| @@ -507,9 +507,9 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| println("Final existing holdQtyMap: $existingHoldQtyMap") | println("Final existing holdQtyMap: $existingHoldQtyMap") | ||||
| // ✅ FIX: Create new suggestions for all pick orders to resuggest | |||||
| // FIX: Create new suggestions for all pick orders to resuggest | |||||
| allPickOrdersToResuggest.forEach { pickOrderToResuggest -> | allPickOrdersToResuggest.forEach { pickOrderToResuggest -> | ||||
| // ✅ 只获取有 rejected stock out lines 的 pick order lines | |||||
| // 只获取有 rejected stock out lines 的 pick order lines | |||||
| val problematicPickOrderLines = pickOrderToResuggest.pickOrderLines.filter { pol -> | val problematicPickOrderLines = pickOrderToResuggest.pickOrderLines.filter { pol -> | ||||
| val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!) | val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!) | ||||
| stockOutLines.any { it.status == "rejected" } | stockOutLines.any { it.status == "rejected" } | ||||
| @@ -518,7 +518,7 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| if (problematicPickOrderLines.isNotEmpty()) { | if (problematicPickOrderLines.isNotEmpty()) { | ||||
| println("=== Creating new suggestions for pick order: ${pickOrderToResuggest.code} ===") | println("=== Creating new suggestions for pick order: ${pickOrderToResuggest.code} ===") | ||||
| // ✅ 调用 suggestionForPickOrderLines 生成新的 suggestions | |||||
| // 调用 suggestionForPickOrderLines 生成新的 suggestions | |||||
| val request = SuggestedPickLotForPolRequest( | val request = SuggestedPickLotForPolRequest( | ||||
| pickOrderLines = problematicPickOrderLines, | pickOrderLines = problematicPickOrderLines, | ||||
| holdQtyMap = existingHoldQtyMap.toMutableMap() | holdQtyMap = existingHoldQtyMap.toMutableMap() | ||||
| @@ -533,24 +533,24 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| if (response.suggestedList.isNotEmpty()) { | if (response.suggestedList.isNotEmpty()) { | ||||
| println("Saving ${response.suggestedList.size} new suggestions") | println("Saving ${response.suggestedList.size} new suggestions") | ||||
| // ✅ 获取现有的 pending/checked 状态的 suggestions(可以更新的) | |||||
| // 获取现有的 pending/checked 状态的 suggestions(可以更新的) | |||||
| val existingUpdatableSuggestions = suggestionsToKeep | val existingUpdatableSuggestions = suggestionsToKeep | ||||
| .filter { it.suggestedLotLine?.id != null } | .filter { it.suggestedLotLine?.id != null } | ||||
| .groupBy { it.pickOrderLine?.id to it.suggestedLotLine?.id } | .groupBy { it.pickOrderLine?.id to it.suggestedLotLine?.id } | ||||
| .mapValues { it.value.first() } // 每个 (lineId, lotId) 只取第一个 | .mapValues { it.value.first() } // 每个 (lineId, lotId) 只取第一个 | ||||
| // ✅ 处理新的 suggestions:更新现有的或创建新的 | |||||
| // 处理新的 suggestions:更新现有的或创建新的 | |||||
| val suggestionsToSave = response.suggestedList.mapNotNull { newSugg -> | val suggestionsToSave = response.suggestedList.mapNotNull { newSugg -> | ||||
| val key = newSugg.pickOrderLine?.id to newSugg.suggestedLotLine?.id | val key = newSugg.pickOrderLine?.id to newSugg.suggestedLotLine?.id | ||||
| val lineId = newSugg.pickOrderLine?.id | val lineId = newSugg.pickOrderLine?.id | ||||
| val lotId = newSugg.suggestedLotLine?.id | val lotId = newSugg.suggestedLotLine?.id | ||||
| if (lineId != null && lotId != null) { | if (lineId != null && lotId != null) { | ||||
| // ✅ 检查这个 lot 是否已有 suggestion | |||||
| // 检查这个 lot 是否已有 suggestion | |||||
| val existingSugg = existingUpdatableSuggestions[key] | val existingSugg = existingUpdatableSuggestions[key] | ||||
| if (existingSugg != null) { | if (existingSugg != null) { | ||||
| // ✅ 检查现有 suggestion 的 stock_out_line 状态 | |||||
| // 检查现有 suggestion 的 stock_out_line 状态 | |||||
| val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( | val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( | ||||
| lineId, lotId | lineId, lotId | ||||
| ) | ) | ||||
| @@ -560,23 +560,23 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| } | } | ||||
| if (canUpdate) { | if (canUpdate) { | ||||
| // ✅ Case 1: 更新现有的 suggestion | |||||
| // Case 1: 更新现有的 suggestion | |||||
| existingSugg.qty = newSugg.qty | existingSugg.qty = newSugg.qty | ||||
| existingSugg.modified = LocalDateTime.now() | existingSugg.modified = LocalDateTime.now() | ||||
| existingSugg.modifiedBy = "system" | existingSugg.modifiedBy = "system" | ||||
| println("⚠️ Updated existing suggestion ${existingSugg.id} for lot $lotId: new qty=${newSugg.qty}") | println("⚠️ Updated existing suggestion ${existingSugg.id} for lot $lotId: new qty=${newSugg.qty}") | ||||
| existingSugg | existingSugg | ||||
| } else { | } else { | ||||
| // ✅ Case 2: 已完成/拒绝,跳过(不更新,也不创建新的) | |||||
| // Case 2: 已完成/拒绝,跳过(不更新,也不创建新的) | |||||
| println("⏭️ Skipping lot $lotId - already ${stockOutLines.first().status}") | println("⏭️ Skipping lot $lotId - already ${stockOutLines.first().status}") | ||||
| null | null | ||||
| } | } | ||||
| } else { | } else { | ||||
| // ✅ 没有现有的 suggestion,创建新的 | |||||
| // 没有现有的 suggestion,创建新的 | |||||
| newSugg | newSugg | ||||
| } | } | ||||
| } else if (lotId == null) { | } else if (lotId == null) { | ||||
| // ✅ lotId=null:检查是否已有 resuggest_issue | |||||
| // lotId=null:检查是否已有 resuggest_issue | |||||
| val existingResuggestIssues = pickExecutionIssueRepository | val existingResuggestIssues = pickExecutionIssueRepository | ||||
| .findByPickOrderLineIdAndDeletedFalse(lineId ?: 0L) | .findByPickOrderLineIdAndDeletedFalse(lineId ?: 0L) | ||||
| .filter { it.issueCategory.name == "resuggest_issue" } | .filter { it.issueCategory.name == "resuggest_issue" } | ||||
| @@ -601,20 +601,20 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| if (updatedSuggestions.isNotEmpty()) { | if (updatedSuggestions.isNotEmpty()) { | ||||
| val savedUpdated = suggestedPickLotRepository.saveAllAndFlush(updatedSuggestions) | val savedUpdated = suggestedPickLotRepository.saveAllAndFlush(updatedSuggestions) | ||||
| allSavedSuggestions.addAll(savedUpdated) | allSavedSuggestions.addAll(savedUpdated) | ||||
| println("✅ Updated ${savedUpdated.size} existing suggestions") | |||||
| println(" Updated ${savedUpdated.size} existing suggestions") | |||||
| } | } | ||||
| // 保存新的 suggestions | // 保存新的 suggestions | ||||
| if (newSuggestions.isNotEmpty()) { | if (newSuggestions.isNotEmpty()) { | ||||
| val savedNew = suggestedPickLotRepository.saveAllAndFlush(newSuggestions) | val savedNew = suggestedPickLotRepository.saveAllAndFlush(newSuggestions) | ||||
| allSavedSuggestions.addAll(savedNew) | allSavedSuggestions.addAll(savedNew) | ||||
| println("✅ Created ${savedNew.size} new suggestions") | |||||
| println(" Created ${savedNew.size} new suggestions") | |||||
| } | } | ||||
| val savedSuggestions = allSavedSuggestions | val savedSuggestions = allSavedSuggestions | ||||
| println("Saved/Updated ${savedSuggestions.size} suggestions") | println("Saved/Updated ${savedSuggestions.size} suggestions") | ||||
| // ✅ 为每个新 suggestion 创建 stock out line 或 issue | |||||
| // 为每个新 suggestion 创建 stock out line 或 issue | |||||
| savedSuggestions.forEach { suggestion -> | savedSuggestions.forEach { suggestion -> | ||||
| if (suggestion.suggestedLotLine != null) { | if (suggestion.suggestedLotLine != null) { | ||||
| val isNewSuggestion = response.suggestedList.any { | val isNewSuggestion = response.suggestedList.any { | ||||
| @@ -624,10 +624,10 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| } | } | ||||
| val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest) | val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest) | ||||
| if (stockOutLine != null) { | if (stockOutLine != null) { | ||||
| println("✅ Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}") | |||||
| println(" Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}") | |||||
| } | } | ||||
| } else { | } else { | ||||
| // ✅ 如果 lot 是 null,表示没有可用的 lot,创建 resuggest_issue | |||||
| // 如果 lot 是 null,表示没有可用的 lot,创建 resuggest_issue | |||||
| println("❌ No available lot for pick order line ${suggestion.pickOrderLine?.id}, creating resuggest_issue") | println("❌ No available lot for pick order line ${suggestion.pickOrderLine?.id}, creating resuggest_issue") | ||||
| val pickOrderLine = suggestion.pickOrderLine | val pickOrderLine = suggestion.pickOrderLine | ||||
| @@ -637,7 +637,7 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| .findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!) | .findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!) | ||||
| .filter { it.status == "rejected" } | .filter { it.status == "rejected" } | ||||
| println("Rejected stock out lines: ${rejectedStockOutLines.size}") | println("Rejected stock out lines: ${rejectedStockOutLines.size}") | ||||
| // ✅ 修复:只创建一个 resuggest_issue(如果有 rejected lines) | |||||
| // 修复:只创建一个 resuggest_issue(如果有 rejected lines) | |||||
| if (rejectedStockOutLines.isNotEmpty()) { | if (rejectedStockOutLines.isNotEmpty()) { | ||||
| println("Creating resuggest failure issue") | println("Creating resuggest failure issue") | ||||
| createResuggestFailureIssue( | createResuggestFailureIssue( | ||||
| @@ -653,7 +653,7 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| } | } | ||||
| } | } | ||||
| // ✅ 更新 holdQty | |||||
| // 更新 holdQty | |||||
| response.holdQtyMap.forEach { (lotId, newHoldQty) -> | response.holdQtyMap.forEach { (lotId, newHoldQty) -> | ||||
| if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) { | if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) { | ||||
| val lot = inventoryLotLineRepository.findById(lotId).orElse(null) | val lot = inventoryLotLineRepository.findById(lotId).orElse(null) | ||||
| @@ -670,7 +670,7 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| } | } | ||||
| } | } | ||||
| } else { | } else { | ||||
| // ✅ 如果完全没有生成任何 suggestions | |||||
| // 如果完全没有生成任何 suggestions | |||||
| println("No suggestions generated at all for pick order: ${pickOrderToResuggest.code}") | println("No suggestions generated at all for pick order: ${pickOrderToResuggest.code}") | ||||
| problematicPickOrderLines.forEach { pickOrderLine -> | problematicPickOrderLines.forEach { pickOrderLine -> | ||||
| @@ -693,7 +693,7 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| // ✅ FIX: Update inventory table for each pick order | |||||
| // FIX: Update inventory table for each pick order | |||||
| allPickOrdersToResuggest.forEach { pickOrderToUpdate -> | allPickOrdersToResuggest.forEach { pickOrderToUpdate -> | ||||
| println("=== Updating inventory table for pick order: ${pickOrderToUpdate.code} ===") | println("=== Updating inventory table for pick order: ${pickOrderToUpdate.code} ===") | ||||
| updateInventoryTableAfterResuggest(pickOrderToUpdate) | updateInventoryTableAfterResuggest(pickOrderToUpdate) | ||||
| @@ -727,12 +727,12 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| private fun createResuggestFailureIssue( | private fun createResuggestFailureIssue( | ||||
| pickOrder: PickOrder, | pickOrder: PickOrder, | ||||
| pickOrderLine: PickOrderLine, | pickOrderLine: PickOrderLine, | ||||
| rejectedStockOutLine: StockOutLineInfo // ✅ 使用 StockOutLineInfo | |||||
| rejectedStockOutLine: StockOutLineInfo // 使用 StockOutLineInfo | |||||
| ) { | ) { | ||||
| try { | try { | ||||
| val item = pickOrderLine.item | val item = pickOrderLine.item | ||||
| // ✅ 从 StockOutLineInfo 获取 inventoryLotLineId | |||||
| // 从 StockOutLineInfo 获取 inventoryLotLineId | |||||
| val inventoryLotLineId = rejectedStockOutLine.inventoryLotLineId | val inventoryLotLineId = rejectedStockOutLine.inventoryLotLineId | ||||
| val inventoryLotLine = if (inventoryLotLineId != null) { | val inventoryLotLine = if (inventoryLotLineId != null) { | ||||
| inventoryLotLineRepository.findById(inventoryLotLineId).orElse(null) | inventoryLotLineRepository.findById(inventoryLotLineId).orElse(null) | ||||
| @@ -758,8 +758,8 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| lotNo = inventoryLotLine?.inventoryLot?.lotNo, | lotNo = inventoryLotLine?.inventoryLot?.lotNo, | ||||
| storeLocation = inventoryLotLine?.warehouse?.name, | storeLocation = inventoryLotLine?.warehouse?.name, | ||||
| requiredQty = pickOrderLine.qty, | requiredQty = pickOrderLine.qty, | ||||
| actualPickQty = rejectedStockOutLine.qty ?: BigDecimal.ZERO, // ✅ 直接使用,不需要 toBigDecimal() | |||||
| missQty = (pickOrderLine.qty ?: BigDecimal.ZERO).minus(rejectedStockOutLine.qty ?: BigDecimal.ZERO), // ✅ 直接使用 | |||||
| actualPickQty = rejectedStockOutLine.qty ?: BigDecimal.ZERO, // 直接使用,不需要 toBigDecimal() | |||||
| missQty = (pickOrderLine.qty ?: BigDecimal.ZERO).minus(rejectedStockOutLine.qty ?: BigDecimal.ZERO), // 直接使用 | |||||
| badItemQty = BigDecimal.ZERO, | badItemQty = BigDecimal.ZERO, | ||||
| issueRemark = "Resuggest failed: No alternative lots available for rejected lot ${inventoryLotLine?.inventoryLot?.lotNo}", | issueRemark = "Resuggest failed: No alternative lots available for rejected lot ${inventoryLotLine?.inventoryLot?.lotNo}", | ||||
| pickerName = null, | pickerName = null, | ||||
| @@ -775,7 +775,7 @@ println("Keeping all suggestions (including rejected ones for display)") | |||||
| ) | ) | ||||
| pickExecutionIssueRepository.save(issue) | pickExecutionIssueRepository.save(issue) | ||||
| println("✅ Created resuggest_issue: ${issue.issueNo} for pick order ${pickOrder.code}") | |||||
| println(" Created resuggest_issue: ${issue.issueNo} for pick order ${pickOrder.code}") | |||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("❌ Error creating resuggest_issue: ${e.message}") | println("❌ Error creating resuggest_issue: ${e.message}") | ||||
| @@ -849,16 +849,16 @@ private fun generateOptimalSuggestionsForAllPickOrders( | |||||
| .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } | .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } | ||||
| .sortedBy { it.expiryDate } | .sortedBy { it.expiryDate } | ||||
| // ✅ FIX: Get fresh lot data and reset holdQty to 0 for calculation | |||||
| // FIX: Get fresh lot data and reset holdQty to 0 for calculation | |||||
| val lotEntities = inventoryLotLineRepository.findAllByIdIn(availableLots.mapNotNull { it.id }) | val lotEntities = inventoryLotLineRepository.findAllByIdIn(availableLots.mapNotNull { it.id }) | ||||
| lotEntities.forEach { lot -> lot.holdQty = BigDecimal.ZERO } | lotEntities.forEach { lot -> lot.holdQty = BigDecimal.ZERO } | ||||
| // ✅ FIX: Calculate remaining quantity for each pick order line | |||||
| // ✅ FIX: Calculate remaining quantity for each pick order line | |||||
| // FIX: Calculate remaining quantity for each pick order line | |||||
| // FIX: Calculate remaining quantity for each pick order line | |||||
| val remainingQtyPerLine = pickOrderLines.associate { pol -> | val remainingQtyPerLine = pickOrderLines.associate { pol -> | ||||
| val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!) | val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!) | ||||
| // ✅ FIX: Count picked qty from ALL statuses except 'rejected' | |||||
| // FIX: Count picked qty from ALL statuses except 'rejected' | |||||
| // This includes 'completed', 'pending', 'checked', 'partially_completed' | // This includes 'completed', 'pending', 'checked', 'partially_completed' | ||||
| val totalPickedQty = stockOutLines | val totalPickedQty = stockOutLines | ||||
| .sumOf { it.qty ?: BigDecimal.ZERO } | .sumOf { it.qty ?: BigDecimal.ZERO } | ||||
| @@ -872,7 +872,7 @@ private fun generateOptimalSuggestionsForAllPickOrders( | |||||
| pol.id to remainingQty | pol.id to remainingQty | ||||
| }.toMutableMap() | }.toMutableMap() | ||||
| // ✅ FIX: Filter out pick order lines that don't need more qty | |||||
| // FIX: Filter out pick order lines that don't need more qty | |||||
| val remainingPickOrderLines = pickOrderLines.filter { pol -> | val remainingPickOrderLines = pickOrderLines.filter { pol -> | ||||
| val remainingQty = remainingQtyPerLine[pol.id] ?: zero | val remainingQty = remainingQtyPerLine[pol.id] ?: zero | ||||
| remainingQty > zero | remainingQty > zero | ||||
| @@ -915,7 +915,7 @@ private fun generateOptimalSuggestionsForAllPickOrders( | |||||
| } | } | ||||
| } | } | ||||
| // ✅ FIX: Create insufficient stock suggestions for remaining quantities | |||||
| // FIX: Create insufficient stock suggestions for remaining quantities | |||||
| remainingQtyPerLine.forEach { (lineId, remainingQty) -> | remainingQtyPerLine.forEach { (lineId, remainingQty) -> | ||||
| if (remainingQty > zero) { | if (remainingQty > zero) { | ||||
| val pickOrderLine = pickOrderLines.find { it.id == lineId } | val pickOrderLine = pickOrderLines.find { it.id == lineId } | ||||
| @@ -939,11 +939,11 @@ private fun updateInventoryTableAfterResuggest(pickOrder: PickOrder) { | |||||
| val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct() | val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct() | ||||
| itemIds.forEach { itemId -> | itemIds.forEach { itemId -> | ||||
| // ✅ FIX: Calculate onHoldQty for ALL pick orders that use this item, not just the current one | |||||
| // FIX: Calculate onHoldQty for ALL pick orders that use this item, not just the current one | |||||
| val onHoldQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE) | val onHoldQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE) | ||||
| .sumOf { it.holdQty ?: BigDecimal.ZERO } | .sumOf { it.holdQty ?: BigDecimal.ZERO } | ||||
| // ✅ FIX: Use enum method instead of string method | |||||
| // FIX: Use enum method instead of string method | |||||
| val unavailableQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.UNAVAILABLE) | val unavailableQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.UNAVAILABLE) | ||||
| .sumOf { | .sumOf { | ||||
| val inQty = it.inQty ?: BigDecimal.ZERO | val inQty = it.inQty ?: BigDecimal.ZERO | ||||
| @@ -1110,17 +1110,17 @@ private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): L | |||||
| val salesUnit = itemUomService.findSalesUnitByItemId(itemId) | val salesUnit = itemUomService.findSalesUnitByItemId(itemId) | ||||
| val ratio = one | val ratio = one | ||||
| // ✅ FIX: Get ALL inventory lots (both available and unavailable) | |||||
| // FIX: Get ALL inventory lots (both available and unavailable) | |||||
| val allLots = inventoryLotLineService | val allLots = inventoryLotLineService | ||||
| .allInventoryLotLinesByItemIdIn(listOf(itemId)) | .allInventoryLotLinesByItemIdIn(listOf(itemId)) | ||||
| .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } | .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } | ||||
| .sortedBy { it.expiryDate } | .sortedBy { it.expiryDate } | ||||
| // ✅ FIX: Separate available and unavailable lots | |||||
| // FIX: Separate available and unavailable lots | |||||
| val availableLots = allLots.filter { it.status == InventoryLotLineStatus.AVAILABLE.value } | val availableLots = allLots.filter { it.status == InventoryLotLineStatus.AVAILABLE.value } | ||||
| val unavailableLots = allLots.filter { it.status == InventoryLotLineStatus.UNAVAILABLE.value } | val unavailableLots = allLots.filter { it.status == InventoryLotLineStatus.UNAVAILABLE.value } | ||||
| // ✅ FIX: Calculate total quantity that was previously held by unavailable lots | |||||
| // FIX: Calculate total quantity that was previously held by unavailable lots | |||||
| var totalUnavailableHoldQty = BigDecimal.ZERO | var totalUnavailableHoldQty = BigDecimal.ZERO | ||||
| val modifiedUnavailableLots = mutableListOf<InventoryLotLine>() | val modifiedUnavailableLots = mutableListOf<InventoryLotLine>() | ||||
| @@ -1128,19 +1128,19 @@ private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): L | |||||
| val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) } | val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) } | ||||
| lot?.let { | lot?.let { | ||||
| totalUnavailableHoldQty = totalUnavailableHoldQty.plus(it.holdQty ?: zero) | totalUnavailableHoldQty = totalUnavailableHoldQty.plus(it.holdQty ?: zero) | ||||
| // ✅ Reset holdQty for unavailable lots | |||||
| // Reset holdQty for unavailable lots | |||||
| it.holdQty = BigDecimal.ZERO | it.holdQty = BigDecimal.ZERO | ||||
| modifiedUnavailableLots.add(it) // ✅ Keep reference to modified entity | |||||
| modifiedUnavailableLots.add(it) // Keep reference to modified entity | |||||
| } | } | ||||
| } | } | ||||
| // ✅ FIX: Save the modified entities (not fresh ones from database) | |||||
| // FIX: Save the modified entities (not fresh ones from database) | |||||
| if (modifiedUnavailableLots.isNotEmpty()) { | if (modifiedUnavailableLots.isNotEmpty()) { | ||||
| inventoryLotLineRepository.saveAll(modifiedUnavailableLots) | inventoryLotLineRepository.saveAll(modifiedUnavailableLots) | ||||
| println("Reset holdQty for ${modifiedUnavailableLots.size} unavailable lots") | println("Reset holdQty for ${modifiedUnavailableLots.size} unavailable lots") | ||||
| } | } | ||||
| // ✅ FIX: Add the unavailable hold quantity to the required quantity | |||||
| // FIX: Add the unavailable hold quantity to the required quantity | |||||
| //val totalRequiredQty = requiredQty.plus(totalUnavailableHoldQty.divide(ratio, 2, RoundingMode.HALF_UP)) | //val totalRequiredQty = requiredQty.plus(totalUnavailableHoldQty.divide(ratio, 2, RoundingMode.HALF_UP)) | ||||
| val totalRequiredQty = requiredQty | val totalRequiredQty = requiredQty | ||||
| var remainingQtyInSalesUnits = totalRequiredQty | var remainingQtyInSalesUnits = totalRequiredQty | ||||
| @@ -1222,7 +1222,7 @@ private fun generateCorrectSuggestionsWithOriginalHolds( | |||||
| .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } | .filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } | ||||
| .sortedBy { it.expiryDate } | .sortedBy { it.expiryDate } | ||||
| // ✅ Calculate total quantity that needs to be redistributed | |||||
| // Calculate total quantity that needs to be redistributed | |||||
| var totalRedistributeQty = requiredQty | var totalRedistributeQty = requiredQty | ||||
| originalHoldQtyMap.forEach { (lotId, originalHoldQty) -> | originalHoldQtyMap.forEach { (lotId, originalHoldQty) -> | ||||
| if (originalHoldQty > zero) { | if (originalHoldQty > zero) { | ||||
| @@ -1478,7 +1478,7 @@ open fun updateSuggestedLotLineId(suggestedPickLotId: Long, newLotLineId: Long): | |||||
| suggestedPickLot.suggestedLotLine = newInventoryLotLine | suggestedPickLot.suggestedLotLine = newInventoryLotLine | ||||
| val savedSuggestedPickLot = suggestedPickLotRepository.save(suggestedPickLot) | val savedSuggestedPickLot = suggestedPickLotRepository.save(suggestedPickLot) | ||||
| println("✅ Successfully updated suggested pick lot ${suggestedPickLotId} to use lot line ${newLotLineId}") | |||||
| println(" Successfully updated suggested pick lot ${suggestedPickLotId} to use lot line ${newLotLineId}") | |||||
| return MessageResponse( | return MessageResponse( | ||||
| id = savedSuggestedPickLot.id, | id = savedSuggestedPickLot.id, | ||||
| @@ -1509,7 +1509,7 @@ private fun createInsufficientStockIssue( | |||||
| insufficientQty: BigDecimal | insufficientQty: BigDecimal | ||||
| ) { | ) { | ||||
| try { | try { | ||||
| // ✅ 检查是否已存在相同的 issue(避免重复创建) | |||||
| // 检查是否已存在相同的 issue(避免重复创建) | |||||
| val existingIssues = pickExecutionIssueRepository | val existingIssues = pickExecutionIssueRepository | ||||
| .findByPickOrderLineIdAndDeletedFalse(pickOrderLine.id ?: 0L) | .findByPickOrderLineIdAndDeletedFalse(pickOrderLine.id ?: 0L) | ||||
| .filter { | .filter { | ||||
| @@ -1557,7 +1557,7 @@ private fun createInsufficientStockIssue( | |||||
| ) | ) | ||||
| pickExecutionIssueRepository.save(issue) | pickExecutionIssueRepository.save(issue) | ||||
| println("✅ Auto-created issue ${issue.issueNo} for insufficient stock (line ${pickOrderLine.id}, qty ${insufficientQty})") | |||||
| println(" Auto-created issue ${issue.issueNo} for insufficient stock (line ${pickOrderLine.id}, qty ${insufficientQty})") | |||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("❌ Error creating insufficient stock issue: ${e.message}") | println("❌ Error creating insufficient stock issue: ${e.message}") | ||||
| @@ -89,7 +89,7 @@ class InventoryLotLineController ( | |||||
| try { | try { | ||||
| val result = inventoryLotLineService.updateInventoryLotLineStatus(request) | val result = inventoryLotLineService.updateInventoryLotLineStatus(request) | ||||
| println("✅ Controller: Update successful - $result") | |||||
| println(" Controller: Update successful - $result") | |||||
| return result | return result | ||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("❌ Controller: Update failed - ${e.message}") | println("❌ Controller: Update failed - ${e.message}") | ||||