| @@ -100,7 +100,7 @@ class DoPickOrder { | |||||
| // Constructor for creating new instances | // Constructor for creating new instances | ||||
| constructor( | constructor( | ||||
| storeId: String, | |||||
| storeId: String?, | |||||
| ticketNo: String, | ticketNo: String, | ||||
| ticketStatus: DoPickOrderStatus, | ticketStatus: DoPickOrderStatus, | ||||
| truckId: Long? = null, | truckId: Long? = null, | ||||
| @@ -41,7 +41,7 @@ fun findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | |||||
| ): List<DoPickOrder> | ): List<DoPickOrder> | ||||
| fun findByShopIdAndStoreIdAndReleaseTypeAndTicketStatusAndDeletedFalse( | fun findByShopIdAndStoreIdAndReleaseTypeAndTicketStatusAndDeletedFalse( | ||||
| shopId: Long?, | shopId: Long?, | ||||
| storeId: String, | |||||
| storeId: String?, | |||||
| releaseType: String, | releaseType: String, | ||||
| ticketStatus: DoPickOrderStatus | ticketStatus: DoPickOrderStatus | ||||
| ): List<DoPickOrder> | ): List<DoPickOrder> | ||||
| @@ -50,4 +50,8 @@ fun findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | |||||
| handledBy: Long, | handledBy: Long, | ||||
| ticketStatus: List<String> | ticketStatus: List<String> | ||||
| ): DoPickOrder? | ): DoPickOrder? | ||||
| fun findByRequiredDeliveryDateAndTicketStatusIn( | |||||
| requiredDeliveryDate: LocalDate, | |||||
| ticketStatus: List<DoPickOrderStatus> | |||||
| ): List<DoPickOrder> | |||||
| } | } | ||||
| @@ -899,26 +899,27 @@ open class DeliveryOrderService( | |||||
| else -> "2F" | else -> "2F" | ||||
| } | } | ||||
| if (truck == null || truckStoreId != expectedStoreId) { | |||||
| val errorMsg = | |||||
| "Items preferredFloor ($preferredFloor) does not match available truck (Truck Store: $truckStoreId). Skipping DO ${deliveryOrder.id}." | |||||
| val defaultTruckId = 4658L | |||||
| val effectiveTruck = truck ?: truckRepository.findById(defaultTruckId).orElse(null) | |||||
| if (effectiveTruck == null) { | |||||
| val errorMsg = "No matching truck for preferredFloor ($preferredFloor) and default truck $defaultTruckId not found. Skipping DO ${deliveryOrder.id}." | |||||
| println("⚠️ $errorMsg") | println("⚠️ $errorMsg") | ||||
| 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: ${effectiveTruck.storeId}, Preferred: $preferredFloor") | |||||
| // storeId 基于 preferredFloor | |||||
| val storeId = "$preferredFloor/F" | |||||
| val loadingSequence = truck.loadingSequence ?: 999 | |||||
| val usedDefaultTruck = (truck == null) | |||||
| val storeId: String? = if (usedDefaultTruck) null else "$preferredFloor/F" | |||||
| val loadingSequence = effectiveTruck.loadingSequence ?: 999 | |||||
| println(" DEBUG: Creating DoPickOrder - Floor: $preferredFloor, Store: $storeId, Truck: ${truck.id}") | |||||
| println(" DEBUG: Creating DoPickOrder - Floor: $preferredFloor, Store: $storeId, Truck: ${effectiveTruck.id}") | |||||
| val doPickOrder = DoPickOrder( | val doPickOrder = DoPickOrder( | ||||
| storeId = storeId, | storeId = storeId, | ||||
| ticketNo = "TEMP-${System.currentTimeMillis()}", | ticketNo = "TEMP-${System.currentTimeMillis()}", | ||||
| ticketStatus = DoPickOrderStatus.pending, | ticketStatus = DoPickOrderStatus.pending, | ||||
| truckId = truck.id, | |||||
| truckId = effectiveTruck.id, | |||||
| pickOrderId = createdPickOrder.id, | pickOrderId = createdPickOrder.id, | ||||
| doOrderId = deliveryOrder.id, | doOrderId = deliveryOrder.id, | ||||
| ticketReleaseTime = null, | ticketReleaseTime = null, | ||||
| @@ -926,8 +927,8 @@ open class DeliveryOrderService( | |||||
| handlerName = null, | handlerName = null, | ||||
| handledBy = null, | handledBy = null, | ||||
| ticketCompleteDateTime = null, | ticketCompleteDateTime = null, | ||||
| truckDepartureTime = truck.departureTime, | |||||
| truckLanceCode = truck.truckLanceCode, | |||||
| truckDepartureTime = effectiveTruck.departureTime, | |||||
| truckLanceCode = effectiveTruck.truckLanceCode, | |||||
| shopCode = deliveryOrder.shop?.code, | shopCode = deliveryOrder.shop?.code, | ||||
| shopName = deliveryOrder.shop?.name, | shopName = deliveryOrder.shop?.name, | ||||
| requiredDeliveryDate = targetDate, | requiredDeliveryDate = targetDate, | ||||
| @@ -1602,17 +1603,18 @@ open class DeliveryOrderService( | |||||
| matchedTrucks.minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } | matchedTrucks.minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| // 如果没有匹配的 truck,抛出异常跳过 | |||||
| if (truck == null) { | |||||
| val errorMsg = | |||||
| "No matching truck found for preferredFloor ($preferredFloor). Skipping DO ${deliveryOrder.id}." | |||||
| } | |||||
| val defaultTruckId = 4658L | |||||
| val effectiveTruck = truck ?: truckRepository.findById(defaultTruckId).orElse(null) | |||||
| val usedDefaultTruck = (truck == null) | |||||
| if (effectiveTruck == null) { | |||||
| val errorMsg = "No matching truck for preferredFloor ($preferredFloor) and default truck $defaultTruckId not found. 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=${effectiveTruck.id}, Store=${effectiveTruck.storeId}, Floor=$preferredFloor") | |||||
| return ReleaseDoResult( | return ReleaseDoResult( | ||||
| deliveryOrderId = deliveryOrder.id!!, | deliveryOrderId = deliveryOrder.id!!, | ||||
| @@ -1624,10 +1626,12 @@ open class DeliveryOrderService( | |||||
| shopName = deliveryOrder.shop?.name, | shopName = deliveryOrder.shop?.name, | ||||
| estimatedArrivalDate = targetDate, | estimatedArrivalDate = targetDate, | ||||
| preferredFloor = preferredFloor, | preferredFloor = preferredFloor, | ||||
| truckId = truck.id, | |||||
| truckDepartureTime = truck.departureTime, | |||||
| truckLanceCode = truck.truckLanceCode, | |||||
| loadingSequence = truck.loadingSequence | |||||
| truckId = effectiveTruck.id, | |||||
| truckDepartureTime = effectiveTruck.departureTime, | |||||
| truckLanceCode = effectiveTruck.truckLanceCode, | |||||
| loadingSequence = effectiveTruck.loadingSequence, | |||||
| usedDefaultTruck = usedDefaultTruck | |||||
| ) | ) | ||||
| } | } | ||||
| @@ -10,12 +10,13 @@ import com.ffii.fpsms.modules.deliveryOrder.web.models.DoPickOrderSummaryItem | |||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import java.time.LocalDate | import java.time.LocalDate | ||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository | import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository | |||||
| @Service | @Service | ||||
| class DoPickOrderQueryService( | class DoPickOrderQueryService( | ||||
| private val doPickOrderRepository: DoPickOrderRepository, | private val doPickOrderRepository: DoPickOrderRepository, | ||||
| private val jdbcDao: JdbcDao, | private val jdbcDao: JdbcDao, | ||||
| private val doPickOrderRecordRepository: DoPickOrderRecordRepository | |||||
| private val doPickOrderRecordRepository: DoPickOrderRecordRepository, | |||||
| private val truckRepository: TruckRepository | |||||
| ) { | ) { | ||||
| fun getSummaryByStore(storeId: String, requiredDate: LocalDate?, releaseType: String): StoreLaneSummary { | fun getSummaryByStore(storeId: String, requiredDate: LocalDate?, releaseType: String): StoreLaneSummary { | ||||
| @@ -94,7 +95,9 @@ class DoPickOrderQueryService( | |||||
| handledBy = it.handledBy | handledBy = it.handledBy | ||||
| ) | ) | ||||
| } | } | ||||
| val defaultTruckId = 4658L | |||||
| val defaultTruck = truckRepository.findById(defaultTruckId).orElse(null) | |||||
| val defaultTruckLaneCode = defaultTruck?.truckLanceCode ?: "" | |||||
| println(" DEBUG: After filtering, ${allRecords.size} records remain (${filteredActiveRecords.size} active + ${filteredCompletedRecords.size} completed)") | println(" DEBUG: After filtering, ${allRecords.size} records remain (${filteredActiveRecords.size} active + ${filteredCompletedRecords.size} completed)") | ||||
| val grouped = allRecords.groupBy { it.truckDepartureTime to it.truckLanceCode } | val grouped = allRecords.groupBy { it.truckDepartureTime to it.truckLanceCode } | ||||
| @@ -105,8 +108,11 @@ class DoPickOrderQueryService( | |||||
| total = list.size | total = list.size | ||||
| ) | ) | ||||
| } | } | ||||
| val timeGroups = grouped.entries | |||||
| val filteredGrouped = grouped | |||||
| .filter { (_, laneBtn) -> | |||||
| laneBtn.truckLanceCode != defaultTruckLaneCode | |||||
| } | |||||
| val timeGroups = filteredGrouped.entries | |||||
| .groupBy { it.key.first } | .groupBy { it.key.first } | ||||
| .mapValues { (_, entries) -> | .mapValues { (_, entries) -> | ||||
| entries.map { it.value } | entries.map { it.value } | ||||
| @@ -123,8 +129,22 @@ class DoPickOrderQueryService( | |||||
| lanes = lanes | lanes = lanes | ||||
| ) | ) | ||||
| } | } | ||||
| val defaultTruckCandidates = doPickOrderRepository.findByRequiredDeliveryDateAndTicketStatusIn( | |||||
| targetDate, | |||||
| listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released) | |||||
| ) | |||||
| // 只算:storeId = null 且 truckLanceCode = '車線-X' | |||||
| val defaultTruckCount = defaultTruckCandidates.count { | |||||
| it.storeId == null && it.truckLanceCode == "車線-X" | |||||
| } | |||||
| return StoreLaneSummary(storeId = storeId, rows = timeGroups) | |||||
| // 9) 回傳 summary,附帶 defaultTruckCount | |||||
| return StoreLaneSummary( | |||||
| storeId = storeId, | |||||
| rows = timeGroups, | |||||
| defaultTruckCount = defaultTruckCount | |||||
| ) | |||||
| } | } | ||||
| @@ -378,8 +378,10 @@ open class DoPickOrderService( | |||||
| lanes = lanes | lanes = lanes | ||||
| ) | ) | ||||
| } | } | ||||
| return StoreLaneSummary(storeId = storeId, rows = timeGroups) | |||||
| val defaultTruckCount = allRecords.count { | |||||
| it.truckLanceCode == "車線-X" && it.requiredDeliveryDate == targetDate | |||||
| } | |||||
| return StoreLaneSummary(storeId = storeId, rows = timeGroups, defaultTruckCount = defaultTruckCount) | |||||
| } | } | ||||
| private fun checkDoPickOrderHasNonIssueLines(doPickOrderId: Long): Boolean { | private fun checkDoPickOrderHasNonIssueLines(doPickOrderId: Long): Boolean { | ||||
| try { | try { | ||||
| @@ -553,9 +555,9 @@ open class DoPickOrderService( | |||||
| "3/F" -> "3F" | "3/F" -> "3F" | ||||
| else -> "2F" | else -> "2F" | ||||
| } | } | ||||
| val defaultTruckId = 4658L | |||||
| val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | ||||
| return trucks.find { it.storeId == storeId } ?: trucks.firstOrNull() | |||||
| return trucks.find { it.storeId == storeId } ?: truckRepository.findById(defaultTruckId).orElse(null) | |||||
| } | } | ||||
| open fun finishDoPickOrder(doPickOrderId: Long): MessageResponse { | open fun finishDoPickOrder(doPickOrderId: Long): MessageResponse { | ||||
| @@ -979,5 +981,60 @@ open class DoPickOrderService( | |||||
| .thenBy { it.truckLanceCode } | .thenBy { it.truckLanceCode } | ||||
| ) | ) | ||||
| } | } | ||||
| open fun findReleasedDoPickOrdersForSelectionToday( | |||||
| shopName: String?, | |||||
| storeId: String?, | |||||
| truck: String? | |||||
| ): List<ReleasedDoPickOrderListItem> { | |||||
| val today = LocalDate.now() | |||||
| var filtered = doPickOrderRepository.findByTicketStatusIn( | |||||
| listOf(DoPickOrderStatus.released, DoPickOrderStatus.pending) | |||||
| ) | |||||
| if (!storeId.isNullOrBlank()) { | |||||
| filtered = filtered.filter { it.storeId == storeId } | |||||
| } | |||||
| if (!shopName.isNullOrBlank()) { | |||||
| filtered = filtered.filter { | |||||
| it.shopName?.contains(shopName, ignoreCase = true) == true || | |||||
| it.shopCode?.contains(shopName, ignoreCase = true) == true | |||||
| } | |||||
| } | |||||
| if (!truck.isNullOrBlank()) { | |||||
| filtered = filtered.filter { it.truckLanceCode == truck } | |||||
| } | |||||
| // 注意:这里是「早于今天」 | |||||
| filtered = filtered.filter { dpo -> | |||||
| dpo.requiredDeliveryDate | |||||
| ?.isEqual(today) | |||||
| ?: false | |||||
| } | |||||
| return filtered.map { dpo -> | |||||
| val lines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(dpo.id!!) | |||||
| val deliveryOrderCodes = lines | |||||
| .mapNotNull { it.deliveryOrderCode } | |||||
| .distinct() | |||||
| ReleasedDoPickOrderListItem( | |||||
| id = dpo.id!!, | |||||
| requiredDeliveryDate = dpo.requiredDeliveryDate, | |||||
| shopCode = dpo.shopCode, | |||||
| shopName = dpo.shopName, | |||||
| storeId = dpo.storeId, | |||||
| truckLanceCode = dpo.truckLanceCode, | |||||
| truckDepartureTime = dpo.truckDepartureTime, | |||||
| deliveryOrderCodes = deliveryOrderCodes.ifEmpty { | |||||
| listOfNotNull(dpo.deliveryOrderCode) | |||||
| } | |||||
| ) | |||||
| }.sortedWith( | |||||
| compareBy<ReleasedDoPickOrderListItem> { it.requiredDeliveryDate } | |||||
| .thenBy { it.truckDepartureTime } | |||||
| .thenBy { it.truckLanceCode } | |||||
| ) | |||||
| } | |||||
| }// 类结束 | }// 类结束 | ||||
| @@ -213,22 +213,23 @@ class DoReleaseCoordinatorService( | |||||
| ) | ) | ||||
| SELECT | SELECT | ||||
| dpo2.id, | dpo2.id, | ||||
| CONCAT('TI-B-', | |||||
| DATE_FORMAT(dpo2.RequiredDeliveryDate, '%Y%m%d'), | |||||
| '-', | |||||
| REPLACE(COALESCE(dpo2.store_id, ts.preferred_floor, '2F'), '/', ''), | |||||
| '-', | |||||
| LPAD( | |||||
| ROW_NUMBER() OVER ( | |||||
| PARTITION BY DATE(dpo2.RequiredDeliveryDate), COALESCE(ts.preferred_floor, '2F') | |||||
| ORDER BY COALESCE(ts.selected_departure_time, '23:59:59'), | |||||
| COALESCE(ts.selected_truck_lance, 'ZZ'), | |||||
| COALESCE(ts.loading_sequence, 999), | |||||
| dpo2.do_order_id | |||||
| ), | |||||
| 3, '0' | |||||
| ) | |||||
| ) AS new_ticket_no | |||||
| CONCAT('TI-B-', | |||||
| DATE_FORMAT(dpo2.RequiredDeliveryDate, '%Y%m%d'), | |||||
| '-', | |||||
| IF(dpo2.store_id IS NULL OR TRIM(dpo2.store_id) = '', | |||||
| 'XF', | |||||
| REPLACE(TRIM(dpo2.store_id), '/', '')), | |||||
| '-', | |||||
| LPAD( | |||||
| ROW_NUMBER() OVER ( | |||||
| PARTITION BY DATE(dpo2.RequiredDeliveryDate), | |||||
| IF(dpo2.store_id IS NULL OR TRIM(dpo2.store_id) = '', 'XF', COALESCE(ts.preferred_floor, '2F')) | |||||
| ORDER BY COALESCE(ts.selected_departure_time, '23:59:59'), | |||||
| COALESCE(ts.selected_truck_lance, 'ZZ'), | |||||
| COALESCE(ts.loading_sequence, 999), | |||||
| dpo2.do_order_id | |||||
| ), 3, '0') | |||||
| ) AS new_ticket_no | |||||
| FROM fpsmsdb.do_pick_order dpo2 | FROM fpsmsdb.do_pick_order dpo2 | ||||
| LEFT JOIN TruckSelection ts ON ts.delivery_order_id = dpo2.do_order_id | LEFT JOIN TruckSelection ts ON ts.delivery_order_id = dpo2.do_order_id | ||||
| WHERE dpo2.ticket_no LIKE 'TEMP-%' | WHERE dpo2.ticket_no LIKE 'TEMP-%' | ||||
| @@ -572,12 +573,14 @@ class DoReleaseCoordinatorService( | |||||
| private fun createMergedDoPickOrder(results: List<ReleaseDoResult>) { | private fun createMergedDoPickOrder(results: List<ReleaseDoResult>) { | ||||
| val first = results.first() | val first = results.first() | ||||
| val storeId = when (first.preferredFloor) { | |||||
| "2F" -> "2/F" | |||||
| "4F" -> "4/F" | |||||
| else -> "2/F" | |||||
| val storeId: String? = when { | |||||
| first.usedDefaultTruck!=false -> null | |||||
| else -> when (first.preferredFloor) { | |||||
| "2F" -> "2/F" | |||||
| "4F" -> "4/F" | |||||
| else -> "2/F" | |||||
| } | |||||
| } | } | ||||
| val doPickOrder = DoPickOrder( | val doPickOrder = DoPickOrder( | ||||
| storeId = storeId, | storeId = storeId, | ||||
| ticketNo = "TEMP-${System.currentTimeMillis()}", | ticketNo = "TEMP-${System.currentTimeMillis()}", | ||||
| @@ -727,10 +730,13 @@ class DoReleaseCoordinatorService( | |||||
| ) | ) | ||||
| // 确定 storeId | // 确定 storeId | ||||
| val storeId = when (result.preferredFloor) { | |||||
| "2F" -> "2/F" | |||||
| "4F" -> "4/F" | |||||
| else -> "2/F" | |||||
| val storeId: String? = when { | |||||
| result.usedDefaultTruck!=false -> null | |||||
| else -> when (result.preferredFloor) { | |||||
| "2F" -> "2/F" | |||||
| "4F" -> "4/F" | |||||
| else -> "2/F" | |||||
| } | |||||
| } | } | ||||
| // 查找是否已有相同 shop、storeId 和 releaseType='single' 的 do_pick_order | // 查找是否已有相同 shop、storeId 和 releaseType='single' 的 do_pick_order | ||||
| @@ -76,6 +76,14 @@ class DoPickOrderController( | |||||
| ): List<ReleasedDoPickOrderListItem> { | ): List<ReleasedDoPickOrderListItem> { | ||||
| return doPickOrderService.findReleasedDoPickOrdersForSelection(shopName, storeId, truck) | return doPickOrderService.findReleasedDoPickOrdersForSelection(shopName, storeId, truck) | ||||
| } | } | ||||
| @GetMapping("/released-today") | |||||
| fun getReleasedDoPickOrdersToday( | |||||
| @RequestParam(required = false) shopName: String?, | |||||
| @RequestParam(required = false) storeId: String?, | |||||
| @RequestParam(required = false) truck: String? | |||||
| ): List<ReleasedDoPickOrderListItem> { | |||||
| return doPickOrderService.findReleasedDoPickOrdersForSelectionToday(shopName, storeId, truck) | |||||
| } | |||||
| @PostMapping("/assign-by-id") | @PostMapping("/assign-by-id") | ||||
| fun assignByDoPickOrderId(@RequestBody request: AssignByDoPickOrderIdRequest): MessageResponse { | fun assignByDoPickOrderId(@RequestBody request: AssignByDoPickOrderIdRequest): MessageResponse { | ||||
| @@ -34,7 +34,8 @@ data class DoDetailLineResponse( | |||||
| ) | ) | ||||
| data class StoreLaneSummary( | data class StoreLaneSummary( | ||||
| val storeId: String, | val storeId: String, | ||||
| val rows: List<LaneRow> | |||||
| val rows: List<LaneRow>, | |||||
| val defaultTruckCount: Int? = null | |||||
| ) | ) | ||||
| data class LaneRow( | data class LaneRow( | ||||
| @@ -14,9 +14,11 @@ data class ReleaseDoResult( | |||||
| val shopId: Long?, | val shopId: Long?, | ||||
| val shopCode: String?, | val shopCode: String?, | ||||
| val shopName: String?, | val shopName: String?, | ||||
| val usedDefaultTruck: Boolean?, | |||||
| val estimatedArrivalDate: LocalDate?, | val estimatedArrivalDate: LocalDate?, | ||||
| val preferredFloor: String, | val preferredFloor: String, | ||||
| val truckId: Long?, | val truckId: Long?, | ||||
| val truckDepartureTime: LocalTime?, | val truckDepartureTime: LocalTime?, | ||||
| val truckLanceCode: String?, | val truckLanceCode: String?, | ||||
| val loadingSequence: Int? | val loadingSequence: Int? | ||||
| @@ -193,7 +193,124 @@ class StockTakeRecordService( | |||||
| return result.sortedBy { it.stockTakeSession } | return result.sortedBy { it.stockTakeSession } | ||||
| } | } | ||||
| open fun getApproverInventoryLotDetailsAll( | |||||
| stockTakeId: Long? = null, | |||||
| pageNum: Int = 0, | |||||
| pageSize: Int = 100 | |||||
| ): RecordsRes<InventoryLotDetailResponse> { | |||||
| println("getApproverInventoryLotDetailsAll called with stockTakeId: $stockTakeId, pageNum: $pageNum, pageSize: $pageSize") | |||||
| // 1. 不分 section,直接拿所有未删除的 warehouse | |||||
| val warehouses = warehouseRepository.findAllByDeletedIsFalse() | |||||
| if (warehouses.isEmpty()) { | |||||
| logger.warn("No warehouses found for approverInventoryLotDetailsAll") | |||||
| return RecordsRes(emptyList(), 0) | |||||
| } | |||||
| val warehouseIds = warehouses.mapNotNull { it.id } | |||||
| println("Found ${warehouses.size} warehouses for ALL sections") | |||||
| // 2. 拿所有这些仓库下面的 lot line | |||||
| val inventoryLotLines = inventoryLotLineRepository.findAllByWarehouseIdInAndDeletedIsFalse(warehouseIds) | |||||
| println("Found ${inventoryLotLines.size} inventory lot lines for ALL sections") | |||||
| // 3. 如果传了 stockTakeId,就把对应的 stockTakeRecord 预先查出来建 map(跟 section 版一样) | |||||
| val stockTakeRecordsMap = if (stockTakeId != null) { | |||||
| val allStockTakeRecords = stockTakeRecordRepository.findAll() | |||||
| .filter { | |||||
| !it.deleted && | |||||
| it.stockTake?.id == stockTakeId && | |||||
| it.warehouse?.id in warehouseIds | |||||
| } | |||||
| allStockTakeRecords.associateBy { | |||||
| Pair(it.lotId ?: 0L, it.warehouse?.id ?: 0L) | |||||
| } | |||||
| } else { | |||||
| emptyMap() | |||||
| } | |||||
| // 4. 组装 InventoryLotDetailResponse(基本复制 section 版里的 map 那段) | |||||
| val allResults = inventoryLotLines.map { ill -> | |||||
| val inventoryLot = ill.inventoryLot | |||||
| val item = inventoryLot?.item | |||||
| val warehouse = ill.warehouse | |||||
| val availableQty = (ill.inQty ?: BigDecimal.ZERO) | |||||
| .subtract(ill.outQty ?: BigDecimal.ZERO) | |||||
| .subtract(ill.holdQty ?: BigDecimal.ZERO) | |||||
| val stockTakeRecord = if (stockTakeId != null && inventoryLot?.id != null && warehouse?.id != null) { | |||||
| stockTakeRecordsMap[Pair(inventoryLot.id, warehouse.id)] | |||||
| } else { | |||||
| null | |||||
| } | |||||
| val inventoryLotLineId = ill.id | |||||
| val stockTakeLine = if (stockTakeId != null && inventoryLotLineId != null) { | |||||
| stockTakeLineRepository.findByInventoryLotLineIdAndStockTakeIdAndDeletedIsFalse( | |||||
| inventoryLotLineId, | |||||
| stockTakeId | |||||
| ) | |||||
| } else { | |||||
| null | |||||
| } | |||||
| InventoryLotDetailResponse( | |||||
| id = ill.id ?: 0L, | |||||
| inventoryLotId = inventoryLot?.id ?: 0L, | |||||
| itemId = item?.id ?: 0L, | |||||
| itemCode = item?.code, | |||||
| itemName = item?.name, | |||||
| lotNo = inventoryLot?.lotNo, | |||||
| expiryDate = inventoryLot?.expiryDate, | |||||
| productionDate = inventoryLot?.productionDate, | |||||
| stockInDate = inventoryLot?.stockInDate, | |||||
| inQty = ill.inQty, | |||||
| remarks = stockTakeRecord?.remarks, | |||||
| outQty = ill.outQty, | |||||
| holdQty = ill.holdQty, | |||||
| availableQty = availableQty, | |||||
| uom = ill.stockUom?.uom?.udfudesc, | |||||
| warehouseCode = warehouse?.code, | |||||
| warehouseName = warehouse?.name, | |||||
| status = ill.status?.name, | |||||
| warehouseSlot = warehouse?.slot, | |||||
| warehouseArea = warehouse?.area, | |||||
| warehouse = warehouse?.warehouse, | |||||
| varianceQty = stockTakeRecord?.varianceQty, | |||||
| stockTakeRecordId = stockTakeRecord?.id, | |||||
| stockTakeRecordStatus = stockTakeRecord?.status, | |||||
| firstStockTakeQty = stockTakeRecord?.pickerFirstStockTakeQty, | |||||
| secondStockTakeQty = stockTakeRecord?.pickerSecondStockTakeQty, | |||||
| firstBadQty = stockTakeRecord?.pickerFirstBadQty, | |||||
| secondBadQty = stockTakeRecord?.pickerSecondBadQty, | |||||
| approverQty = stockTakeRecord?.approverStockTakeQty, | |||||
| approverBadQty = stockTakeRecord?.approverBadQty, | |||||
| finalQty = stockTakeLine?.finalQty, | |||||
| bookQty = stockTakeRecord?.bookQty, | |||||
| ) | |||||
| } | |||||
| // 5. 可选过滤:比如只保留 availableQty > 0 或有盘点记录的(跟 section 版一样) | |||||
| val filteredResults = allResults.filter { response -> | |||||
| val av = response.availableQty ?: BigDecimal.ZERO | |||||
| av.compareTo(BigDecimal.ZERO) > 0 || response.stockTakeRecordId != null | |||||
| } | |||||
| // 6. 分页(和 section 版一模一样) | |||||
| val pageable = PageRequest.of(pageNum, pageSize) | |||||
| val startIndex = pageable.offset.toInt() | |||||
| val endIndex = minOf(startIndex + pageSize, filteredResults.size) | |||||
| val paginatedResult = if (startIndex < filteredResults.size) { | |||||
| filteredResults.subList(startIndex, endIndex) | |||||
| } else { | |||||
| emptyList() | |||||
| } | |||||
| return RecordsRes(paginatedResult, filteredResults.size) | |||||
| } | |||||
| open fun AllApproverStockTakeList(): List<AllPickedStockTakeListReponse> { | open fun AllApproverStockTakeList(): List<AllPickedStockTakeListReponse> { | ||||
| // 1. 获取所有不同的 stockTakeSection(从 warehouse 表) | // 1. 获取所有不同的 stockTakeSection(从 warehouse 表) | ||||
| val allWarehouses = warehouseRepository.findAllByDeletedIsFalse() | val allWarehouses = warehouseRepository.findAllByDeletedIsFalse() | ||||
| @@ -18,11 +18,42 @@ class StockTakeRecordController( | |||||
| private val stockOutRecordService: StockTakeRecordService | private val stockOutRecordService: StockTakeRecordService | ||||
| ) { | ) { | ||||
| private val logger = LoggerFactory.getLogger(StockTakeRecordController::class.java) | private val logger = LoggerFactory.getLogger(StockTakeRecordController::class.java) | ||||
| /* | |||||
| @GetMapping("/AllPickedStockOutRecordList") | @GetMapping("/AllPickedStockOutRecordList") | ||||
| fun AllPickedStockOutRecordList(): List<AllPickedStockTakeListReponse> { | fun AllPickedStockOutRecordList(): List<AllPickedStockTakeListReponse> { | ||||
| return stockOutRecordService.AllPickedStockTakeList() | return stockOutRecordService.AllPickedStockTakeList() | ||||
| } | } | ||||
| */ | |||||
| @GetMapping("/AllPickedStockOutRecordList") | |||||
| fun allPickedStockOutRecordList( | |||||
| @RequestParam(required = false, defaultValue = "0") pageNum: Int, | |||||
| @RequestParam(required = false, defaultValue = "6") pageSize: Int | |||||
| ): RecordsRes<AllPickedStockTakeListReponse> { | |||||
| val all = stockOutRecordService.AllPickedStockTakeList() | |||||
| val total = all.size | |||||
| val fromIndex = pageNum * pageSize | |||||
| val toIndex = kotlin.math.min(fromIndex + pageSize, total) | |||||
| val pageList = | |||||
| if (fromIndex >= total) emptyList() | |||||
| else all.subList(fromIndex, toIndex) | |||||
| return RecordsRes<AllPickedStockTakeListReponse>( | |||||
| pageList, | |||||
| total | |||||
| ) | |||||
| } | |||||
| @GetMapping("/approverInventoryLotDetailsAll") | |||||
| fun getApproverInventoryLotDetailsAll( | |||||
| @RequestParam(required = false) stockTakeId: Long?, // 可选:限定某个 stockTake | |||||
| @RequestParam(required = false, defaultValue = "0") pageNum: Int, // 分页页码(从 0 开始) | |||||
| @RequestParam(required = false, defaultValue = "100") pageSize: Int // 每页多少条,测试时可调大 | |||||
| ): RecordsRes<InventoryLotDetailResponse> { | |||||
| return stockOutRecordService.getApproverInventoryLotDetailsAll( | |||||
| stockTakeId = stockTakeId, | |||||
| pageNum = pageNum, | |||||
| pageSize = pageSize | |||||
| ) | |||||
| } | |||||
| @GetMapping("/AllApproverStockTakeList") | @GetMapping("/AllApproverStockTakeList") | ||||
| fun AllApproverStockTakeList(): List<AllPickedStockTakeListReponse> { | fun AllApproverStockTakeList(): List<AllPickedStockTakeListReponse> { | ||||
| return stockOutRecordService.AllApproverStockTakeList() | return stockOutRecordService.AllApproverStockTakeList() | ||||