From 4ee5babc8d1e3fe0ffd1b9863ac8986d840ed58e Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Thu, 16 Apr 2026 21:35:13 +0800 Subject: [PATCH] update do ticket ui --- .../service/DeliveryOrderService.kt | 43 +++++++++++++------ .../service/DoPickOrderAssignmentService.kt | 20 +++++++++ .../service/DoPickOrderQueryService.kt | 32 +++++++++----- .../web/models/DoDetailResponse.kt | 3 ++ 4 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt index c34ec08..7fe6a6d 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt @@ -1806,16 +1806,33 @@ val inventoryLotLine = illId?.let { inventoryLotLineMap[it] } } /** - * Workbench NO-HOLD release: - * - Create pick_order + stock_out + * Workbench NO-HOLD release (V1): + * - Create pick_order + stock_out header * - Do NOT create suggested_pick_lot / stock_out_line * - Do NOT update inventory_lot_line.holdQty * - * Downstream should be handled by workbench services (no-hold suggestion + stock_out_line creation). + * Downstream should be handled by workbench batch release (no-hold suggestion + stock_out_line creation), + * or on ticket assign when using [releaseDeliveryOrderWithoutTicketNoHoldV2]. */ @Transactional(rollbackFor = [Exception::class]) - open fun releaseDeliveryOrderWithoutTicketNoHold(request: ReleaseDoRequest): ReleaseDoResult { - println(" DEBUG: Starting releaseDeliveryOrderWithoutTicketNoHold for DO ID: ${request.id}") + open fun releaseDeliveryOrderWithoutTicketNoHold(request: ReleaseDoRequest): ReleaseDoResult = + releaseDeliveryOrderWithoutTicketNoHoldInternal(request, createStockOutHeader = true) + + /** + * Workbench NO-HOLD release (V2): same as [releaseDeliveryOrderWithoutTicketNoHold] but **does not** create a + * [StockOut] row. Suggested pick lots, stock out header, and stock out lines are created when the workbench + * assigns the [delivery_order_pick_order] ([com.ffii.fpsms.modules.deliveryOrder.service.DoWorkbenchDopoAssignmentService]). + */ + @Transactional(rollbackFor = [Exception::class]) + open fun releaseDeliveryOrderWithoutTicketNoHoldV2(request: ReleaseDoRequest): ReleaseDoResult = + releaseDeliveryOrderWithoutTicketNoHoldInternal(request, createStockOutHeader = false) + + private fun releaseDeliveryOrderWithoutTicketNoHoldInternal( + request: ReleaseDoRequest, + createStockOutHeader: Boolean, + ): ReleaseDoResult { + val tag = if (createStockOutHeader) "V1" else "V2" + println(" DEBUG: Starting releaseDeliveryOrderWithoutTicketNoHold ($tag) for DO ID: ${request.id}") val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.id) ?: throw NoSuchElementException("Delivery Order not found") @@ -1854,14 +1871,16 @@ val inventoryLotLine = illId?.let { inventoryLotLineMap[it] } pickOrderEntity.status = com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED pickOrderRepository.saveAndFlush(pickOrderEntity) - // Create stock out header only; stock_out_line is created by workbench service - val stockOut = StockOut().apply { - this.type = "do" - this.consoPickOrderCode = consoCode - this.status = StockOutStatus.PENDING.status - this.handler = request.userId + if (createStockOutHeader) { + // Create stock out header only; stock_out_line is created by workbench service (V1 batch) or on assign (V2) + val stockOut = StockOut().apply { + this.type = "do" + this.consoPickOrderCode = consoCode + this.status = StockOutStatus.PENDING.status + this.handler = request.userId + } + stockOutRepository.saveAndFlush(stockOut) } - stockOutRepository.saveAndFlush(stockOut) } // Truck selection (reuse normal logic) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderAssignmentService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderAssignmentService.kt index 6a6d212..c5ea5f5 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderAssignmentService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderAssignmentService.kt @@ -13,6 +13,7 @@ import com.ffii.fpsms.modules.user.entity.UserRepository import org.springframework.stereotype.Service import java.time.LocalDateTime import java.time.LocalDate +import java.time.LocalTime @Service class DoPickOrderAssignmentService( private val doPickOrderRepository: DoPickOrderRepository, @@ -41,6 +42,15 @@ class DoPickOrderAssignmentService( // 获取日期(如果提供) val requiredDate = request.requiredDate println(" DEBUG: assignByLane - Requested date: $requiredDate") + val requestedDepartureTime: LocalTime? = request.truckDepartureTime + ?.takeIf { it.isNotBlank() } + ?.let { + try { + LocalTime.parse(it) + } catch (e: Exception) { + null + } + } // 根据是否有日期参数选择不同的查询方法 val allCandidates = if (requiredDate != null) { @@ -58,6 +68,16 @@ class DoPickOrderAssignmentService( ) } .filter { it.truckLanceCode == request.truckLanceCode } + .let { candidates -> + requestedDepartureTime?.let { dep -> + candidates.filter { it.truckDepartureTime == dep } + } ?: candidates + } + .let { candidates -> + request.loadingSequence?.let { seq -> + candidates.filter { it.loadingSequence == seq } + } ?: candidates + } .sortedBy { it.truckDepartureTime } println(" DEBUG: Found ${allCandidates.size} candidate do_pick_orders for lane ${request.truckLanceCode}") diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderQueryService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderQueryService.kt index 2efd0b9..94a349a 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderQueryService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderQueryService.kt @@ -86,12 +86,15 @@ class DoPickOrderQueryService( DoPickOrderSummaryItem( truckDepartureTime = it.truckDepartureTime, truckLanceCode = it.truckLanceCode, + // 只對 4/F 顯示/分組 loadingSequence;2/F 維持舊邏輯避免同車線被拆成多序 + loadingSequence = if (actualStoreId == "4/F") it.loadingSequence else null, handledBy = it.handledBy ) } + filteredCompletedRecords.map { DoPickOrderSummaryItem( truckDepartureTime = it.truckDepartureTime, truckLanceCode = it.truckLanceCode, + loadingSequence = if (actualStoreId == "4/F") it.loadingSequence else null, handledBy = it.handledBy ) } @@ -100,24 +103,33 @@ class DoPickOrderQueryService( val defaultTruckLaneCode = defaultTruck?.truckLanceCode ?: "" //println(" DEBUG: After filtering, ${allRecords.size} records remain (${filteredActiveRecords.size} active + ${filteredCompletedRecords.size} completed)") - val grouped = allRecords.groupBy { it.truckDepartureTime to it.truckLanceCode } - .mapValues { (_, list) -> - LaneBtn( - truckLanceCode = list.first().truckLanceCode ?: "", - unassigned = list.count { it.handledBy == null }, - total = list.size - ) - } + val grouped = if (actualStoreId == "4/F") { + allRecords.groupBy { Triple(it.truckDepartureTime, it.truckLanceCode, it.loadingSequence) } + } else { + // 2/F:回到舊分組(truckDepartureTime + truckLanceCode) + allRecords.groupBy { Pair(it.truckDepartureTime, it.truckLanceCode) } + }.mapValues { (_, list) -> + LaneBtn( + truckLanceCode = list.first().truckLanceCode ?: "", + loadingSequence = if (actualStoreId == "4/F") list.first().loadingSequence else null, + unassigned = list.count { it.handledBy == null }, + total = list.size + ) + } val filteredGrouped = grouped .filter { (_, laneBtn) -> laneBtn.truckLanceCode != defaultTruckLaneCode } val timeGroups = filteredGrouped.entries - .groupBy { it.key.first } + .groupBy { (it.key as? Triple<*, *, *>)?.first as? java.time.LocalTime ?: (it.key as Pair<*, *>).first as java.time.LocalTime? } .mapValues { (_, entries) -> entries.map { it.value } .filter { it.unassigned > 0 } // filter out lanes with no unassigned orders - .sortedByDescending { it.unassigned } + .sortedWith( + compareByDescending { it.unassigned } + .thenBy { it.truckLanceCode } + .thenBy { it.loadingSequence ?: 999 } + ) } .filterValues { lanes -> lanes.isNotEmpty() } .toSortedMap(compareBy { it }) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt index eaa58d8..f52517d 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt @@ -45,6 +45,7 @@ data class LaneRow( data class LaneBtn( val truckLanceCode: String, + val loadingSequence: Int? = null, val unassigned: Int, val total: Int ) @@ -53,11 +54,13 @@ data class AssignByLaneRequest( val storeId: String, val truckDepartureTime: String?, // 可选:限定出车时间 val truckLanceCode: String , + val loadingSequence: Int? = null, val requiredDate: LocalDate? // 必填:车道编号 ) data class DoPickOrderSummaryItem( val truckDepartureTime: java.time.LocalTime?, val truckLanceCode: String?, + val loadingSequence: Int?, val handledBy: Long? ) data class DoSearchRow(