| @@ -436,7 +436,7 @@ open class DeliveryOrderService( | |||
| ) | |||
| val createdPickOrder = pickOrderService.create(po) | |||
| println("🔍 DEBUG: Created pick order - ID: ${createdPickOrder.id}") | |||
| println(" DEBUG: Created pick order - ID: ${createdPickOrder.id}") | |||
| val consoCode = pickOrderService.assignConsoCode() | |||
| val pickOrderEntity = pickOrderRepository.findById(createdPickOrder.id!!).orElse(null) | |||
| @@ -450,7 +450,7 @@ open class DeliveryOrderService( | |||
| // Debug: Check pick order lines | |||
| println("�� DEBUG: Pick order has ${pickOrderEntity.pickOrderLines?.size ?: 0} pick order lines") | |||
| 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}") | |||
| } | |||
| @@ -460,7 +460,7 @@ open class DeliveryOrderService( | |||
| ) | |||
| val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList) | |||
| println("🔍 DEBUG: Saved ${saveSuggestedPickLots.size} suggested pick lots") | |||
| println(" DEBUG: Saved ${saveSuggestedPickLots.size} suggested pick lots") | |||
| val insufficientCount = suggestions.suggestedList.count { it.suggestedLotLine == null } | |||
| if (insufficientCount > 0) { | |||
| println("⚠️ WARNING: $insufficientCount items have insufficient stock (issues auto-created)") | |||
| @@ -525,11 +525,11 @@ open class DeliveryOrderService( | |||
| val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now() | |||
| val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")) | |||
| println("🔍 DEBUG: Target date: $targetDate, Date prefix: $datePrefix") | |||
| println(" DEBUG: Target date: $targetDate, Date prefix: $datePrefix") | |||
| 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) | |||
| println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") | |||
| println(" DEBUG: Found ${trucks.size} trucks for shop $shopId") | |||
| // 移除提前返回,总是分析 items 分布 | |||
| // 分析 DO order lines 中的 items 分布 | |||
| @@ -553,8 +553,8 @@ open class DeliveryOrderService( | |||
| floorItemCount[row["floor"] as? String ?: "Other"] = (row["item_count"] as? Number)?.toInt() ?: 0 | |||
| } | |||
| println("🔍 DEBUG: Floor item count distribution: $floorItemCount") | |||
| println("🔍 DEBUG: Total items: ${itemIds.size}, Items on 4F: ${floorItemCount["4F"] ?: 0}") | |||
| println(" DEBUG: Floor item count distribution: $floorItemCount") | |||
| println(" DEBUG: Total items: ${itemIds.size}, Items on 4F: ${floorItemCount["4F"] ?: 0}") | |||
| // 新逻辑:只有所有 items 都在 4F,才算 4F,否则算 2F | |||
| val preferredFloor = if ((floorItemCount["4F"] ?: 0) == itemIds.size && (floorItemCount["2F"] ?: 0) == 0) { | |||
| @@ -563,13 +563,13 @@ open class DeliveryOrderService( | |||
| "2F" // 只要有任何 item 不在 4F,就算 2F | |||
| } | |||
| println("🔍 DEBUG: Preferred floor: $preferredFloor (All items on 4F: ${preferredFloor == "4F"})") | |||
| println(" DEBUG: Preferred floor: $preferredFloor (All items on 4F: ${preferredFloor == "4F"})") | |||
| // 查找 truck | |||
| 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) | |||
| println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") | |||
| println(" DEBUG: Found ${trucks.size} trucks for shop $shopId") | |||
| val preferredStoreId = when (preferredFloor) { | |||
| @@ -586,7 +586,7 @@ open class DeliveryOrderService( | |||
| trucks.firstOrNull() | |||
| } | |||
| println("🔍 DEBUG: Selected truck: ID=${selectedTruck?.id}, StoreId=${selectedTruck?.storeId}") | |||
| println(" DEBUG: Selected truck: ID=${selectedTruck?.id}, StoreId=${selectedTruck?.storeId}") | |||
| selectedTruck | |||
| } | |||
| @@ -612,7 +612,7 @@ open class DeliveryOrderService( | |||
| val storeId = "$preferredFloor/F" | |||
| val loadingSequence = truck.loadingSequence ?: 999 | |||
| println("🔍 DEBUG: Creating DoPickOrder - Floor: $preferredFloor, Store: $storeId, Truck: ${truck.id}") | |||
| println(" DEBUG: Creating DoPickOrder - Floor: $preferredFloor, Store: $storeId, Truck: ${truck.id}") | |||
| val doPickOrder = DoPickOrder( | |||
| storeId = storeId, | |||
| @@ -635,7 +635,7 @@ open class DeliveryOrderService( | |||
| ) | |||
| val savedDoPickOrder = doPickOrderService.save(doPickOrder) | |||
| println("🔍 DEBUG: Saved DoPickOrder - ID: ${savedDoPickOrder.id}") | |||
| println(" DEBUG: Saved DoPickOrder - ID: ${savedDoPickOrder.id}") | |||
| truck | |||
| } | |||
| return MessageResponse( | |||
| @@ -1084,7 +1084,7 @@ open class DeliveryOrderService( | |||
| @Transactional(rollbackFor = [Exception::class]) | |||
| open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDoResult { | |||
| println("🔍 DEBUG: Starting releaseDeliveryOrderWithoutTicket for DO ID: ${request.id}") | |||
| println(" DEBUG: Starting releaseDeliveryOrderWithoutTicket for DO ID: ${request.id}") | |||
| val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.id) | |||
| ?: throw NoSuchElementException("Delivery Order not found") | |||
| @@ -1233,7 +1233,7 @@ open class DeliveryOrderService( | |||
| else -> "2F" // 默认 2F | |||
| } | |||
| println("🔍 DEBUG: Floor calculation for DO ${deliveryOrder.id}") | |||
| println(" DEBUG: Floor calculation for DO ${deliveryOrder.id}") | |||
| println(" - Total items: ${itemIds.size}") | |||
| println(" - Items with store_id: $totalItemsWithStoreId") | |||
| println(" - Items without store_id: ${itemIds.size - totalItemsWithStoreId}") | |||
| @@ -1274,15 +1274,15 @@ open class DeliveryOrderService( | |||
| java.time.DayOfWeek.SUNDAY -> "Sun" | |||
| } | |||
| println("🔍 DEBUG: DO ${deliveryOrder.id} - Target date: $targetDate ($dayAbbr), Shop: $shopId") | |||
| println("🔍 DEBUG: Found ${matchedTrucks.size} matched 4F trucks") | |||
| println(" DEBUG: DO ${deliveryOrder.id} - Target date: $targetDate ($dayAbbr), Shop: $shopId") | |||
| println(" DEBUG: Found ${matchedTrucks.size} matched 4F trucks") | |||
| val dayMatchedTrucks = matchedTrucks.filter { | |||
| it.truckLanceCode?.contains(dayAbbr, ignoreCase = true) == true | |||
| } | |||
| println("🔍 DEBUG: Found ${dayMatchedTrucks.size} trucks matching $dayAbbr") | |||
| println(" DEBUG: Found ${dayMatchedTrucks.size} trucks matching $dayAbbr") | |||
| dayMatchedTrucks.forEach { t -> | |||
| println(" - Truck ID=${t.id}, Code=${t.truckLanceCode}, Time=${t.departureTime}") | |||
| } | |||
| @@ -36,22 +36,22 @@ class DoPickOrderAssignmentService( | |||
| "4/F" -> "4/F" | |||
| else -> request.storeId | |||
| } | |||
| println("🔍 DEBUG: assignByLane - Converting storeId from '${request.storeId}' to '$actualStoreId'") | |||
| println(" DEBUG: assignByLane - Converting storeId from '${request.storeId}' to '$actualStoreId'") | |||
| // 获取日期(如果提供) | |||
| val requiredDate = request.requiredDate | |||
| println("🔍 DEBUG: assignByLane - Requested date: $requiredDate") | |||
| println(" DEBUG: assignByLane - Requested date: $requiredDate") | |||
| // 根据是否有日期参数选择不同的查询方法 | |||
| val allCandidates = if (requiredDate != null) { | |||
| println("🔍 DEBUG: Filtering by date: $requiredDate") | |||
| println(" DEBUG: Filtering by date: $requiredDate") | |||
| doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | |||
| actualStoreId, | |||
| requiredDate, | |||
| listOf(DoPickOrderStatus.pending) | |||
| ) | |||
| } else { | |||
| println("🔍 DEBUG: No date filter applied") | |||
| println(" DEBUG: No date filter applied") | |||
| doPickOrderRepository.findByStoreIdAndTicketStatusOrderByTruckDepartureTimeAsc( | |||
| actualStoreId, | |||
| DoPickOrderStatus.pending | |||
| @@ -60,18 +60,18 @@ class DoPickOrderAssignmentService( | |||
| .filter { it.truckLanceCode == request.truckLanceCode } | |||
| .sortedBy { it.truckDepartureTime } | |||
| 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 的记录 | |||
| val filteredCandidates = allCandidates.filter { doPickOrder -> | |||
| val hasNonIssueLines = checkDoPickOrderHasNonIssueLines(doPickOrder.id!!) | |||
| if (!hasNonIssueLines) { | |||
| println("🔍 DEBUG: Filtering out DoPickOrder ${doPickOrder.id} - all lines are issues") | |||
| println(" DEBUG: Filtering out DoPickOrder ${doPickOrder.id} - all lines are issues") | |||
| } | |||
| hasNonIssueLines | |||
| } | |||
| println("🔍 DEBUG: After filtering, ${filteredCandidates.size} do_pick_orders remain") | |||
| println(" DEBUG: After filtering, ${filteredCandidates.size} do_pick_orders remain") | |||
| if (filteredCandidates.isEmpty()) { | |||
| return MessageResponse( | |||
| @@ -92,7 +92,7 @@ class DoPickOrderAssignmentService( | |||
| // 获取这个 do_pick_order 下的所有 pick orders 并分配给用户 | |||
| 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}") | |||
| doPickOrderLines.forEach { line -> | |||
| if (line.pickOrderId != null) { | |||
| @@ -101,7 +101,7 @@ class DoPickOrderAssignmentService( | |||
| pickOrder.assignTo = user | |||
| pickOrder.status = PickOrderStatus.RELEASED | |||
| pickOrderRepository.save(pickOrder) | |||
| println("🔍 DEBUG: Assigned pick order ${line.pickOrderId} to user ${request.userId}") | |||
| println(" DEBUG: Assigned pick order ${line.pickOrderId} to user ${request.userId}") | |||
| } else { | |||
| println("⚠️ WARNING: Pick order ${line.pickOrderId} not found") | |||
| } | |||
| @@ -120,7 +120,7 @@ class DoPickOrderAssignmentService( | |||
| } | |||
| if (records.isNotEmpty()) { | |||
| doPickOrderRecordRepository.saveAll(records) | |||
| println("🔍 DEBUG: Updated ${records.size} do_pick_order_record for pick order ${line.pickOrderId}") | |||
| println(" DEBUG: Updated ${records.size} do_pick_order_record for pick order ${line.pickOrderId}") | |||
| } | |||
| } | |||
| } | |||
| @@ -171,7 +171,7 @@ class DoPickOrderAssignmentService( | |||
| val hasNonIssueLines = nonIssueLines > 0 | |||
| println("🔍 DEBUG: DoPickOrder $doPickOrderId - Total lines: $totalLines, Non-issue lines: $nonIssueLines, Has non-issue lines: $hasNonIssueLines") | |||
| println(" DEBUG: DoPickOrder $doPickOrderId - Total lines: $totalLines, Non-issue lines: $nonIssueLines, Has non-issue lines: $hasNonIssueLines") | |||
| hasNonIssueLines | |||
| @@ -20,7 +20,7 @@ class DoPickOrderQueryService( | |||
| fun getSummaryByStore(storeId: String, requiredDate: LocalDate?, releaseType: String): StoreLaneSummary { | |||
| val targetDate = requiredDate ?: LocalDate.now() | |||
| println("🔍 DEBUG: Getting summary for store=$storeId, date=$targetDate, releaseType=$releaseType") | |||
| println(" DEBUG: Getting summary for store=$storeId, date=$targetDate, releaseType=$releaseType") | |||
| val actualStoreId = when (storeId) { | |||
| "2/F" -> "2/F" | |||
| @@ -56,16 +56,16 @@ class DoPickOrderQueryService( | |||
| else -> completedRecords // "all" 或其他值,不过滤 | |||
| } | |||
| println("🔍 DEBUG: Found ${activeRecords.size} active records for date $targetDate") | |||
| println("🔍 DEBUG: After releaseType filter: ${filteredActiveRecordsByReleaseType.size} active records") | |||
| println("🔍 DEBUG: Found ${completedRecords.size} completed records for date $targetDate") | |||
| println("🔍 DEBUG: After releaseType filter: ${filteredCompletedRecordsByReleaseType.size} completed records") | |||
| println(" DEBUG: Found ${activeRecords.size} active records for date $targetDate") | |||
| println(" DEBUG: After releaseType filter: ${filteredActiveRecordsByReleaseType.size} active records") | |||
| println(" DEBUG: Found ${completedRecords.size} completed records for date $targetDate") | |||
| println(" DEBUG: After releaseType filter: ${filteredCompletedRecordsByReleaseType.size} completed records") | |||
| // Filter active records (check for non-issue lines) | |||
| val filteredActiveRecords = filteredActiveRecordsByReleaseType.filter { doPickOrder -> | |||
| val hasNonIssueLines = checkDoPickOrderHasNonIssueLines(doPickOrder.id!!) | |||
| if (!hasNonIssueLines) { | |||
| println("🔍 DEBUG: Filtering out DoPickOrder ${doPickOrder.id} - all lines are issues") | |||
| println(" DEBUG: Filtering out DoPickOrder ${doPickOrder.id} - all lines are issues") | |||
| } | |||
| hasNonIssueLines | |||
| } | |||
| @@ -74,7 +74,7 @@ class DoPickOrderQueryService( | |||
| val filteredCompletedRecords = filteredCompletedRecordsByReleaseType.filter { record -> | |||
| val hasNonIssueLines = checkDoPickOrderRecordHasNonIssueLines(record.id!!) | |||
| if (!hasNonIssueLines) { | |||
| println("🔍 DEBUG: Filtering out DoPickOrderRecord ${record.id} - all lines are issues") | |||
| println(" DEBUG: Filtering out DoPickOrderRecord ${record.id} - all lines are issues") | |||
| } | |||
| hasNonIssueLines | |||
| } | |||
| @@ -95,7 +95,7 @@ class DoPickOrderQueryService( | |||
| ) | |||
| } | |||
| 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 } | |||
| .mapValues { (_, list) -> | |||
| @@ -264,8 +264,8 @@ class DoReleaseCoordinatorService( | |||
| private fun getOrderedDeliveryOrderIds(ids: List<Long>): List<Long> { | |||
| try { | |||
| println("🔍 DEBUG: Getting ordered IDs for ${ids.size} orders") | |||
| println("🔍 DEBUG: First 5 IDs: ${ids.take(5)}") | |||
| println(" DEBUG: Getting ordered IDs for ${ids.size} orders") | |||
| println(" DEBUG: First 5 IDs: ${ids.take(5)}") | |||
| val dayOfWeekSql = getDayOfWeekAbbrSql("do.estimatedArrivalDate") | |||
| val sql = """ | |||
| WITH DoFloorCounts AS ( | |||
| @@ -470,24 +470,24 @@ class DoReleaseCoordinatorService( | |||
| """.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) | |||
| 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()) { | |||
| 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 -> | |||
| (row["id"] as? Number)?.toLong() | |||
| } | |||
| println("🔍 DEBUG: Query returned ${sortedIds.size} sorted IDs") | |||
| println("🔍 DEBUG: First 10 sorted IDs: ${sortedIds.take(10)}") | |||
| println(" DEBUG: Query returned ${sortedIds.size} sorted IDs") | |||
| println(" DEBUG: First 10 sorted IDs: ${sortedIds.take(10)}") | |||
| return if (sortedIds.isEmpty()) { | |||
| println("⚠️ WARNING: No sorted IDs, using original order") | |||
| @@ -518,7 +518,7 @@ class DoReleaseCoordinatorService( | |||
| try { | |||
| println("📦 Starting batch release for ${ids.size} orders") | |||
| val sortedIds = getOrderedDeliveryOrderIds(ids) | |||
| println("🔍 DEBUG: Got ${sortedIds.size} sorted orders") | |||
| println(" DEBUG: Got ${sortedIds.size} sorted orders") | |||
| val releaseResults = mutableListOf<ReleaseDoResult>() | |||
| @@ -538,7 +538,7 @@ class DoReleaseCoordinatorService( | |||
| ) | |||
| releaseResults.add(result) | |||
| status.success.incrementAndGet() | |||
| println("🔍 DO $id -> Success") | |||
| println(" DO $id -> Success") | |||
| } catch (e: Exception) { | |||
| synchronized(status.failed) { | |||
| status.failed.add(id to (e.message ?: "Exception")) | |||
| @@ -587,13 +587,13 @@ class DoReleaseCoordinatorService( | |||
| Triple(it.estimatedArrivalDate, it.preferredFloor, it.shopId) | |||
| } | |||
| println("🔍 DEBUG: Grouped into ${grouped.size} DoPickOrders") | |||
| println(" DEBUG: Grouped into ${grouped.size} DoPickOrders") | |||
| // 第三步:为每组创建一个 DoPickOrder 和多条 DoPickOrderLine | |||
| grouped.forEach { (key, group) -> | |||
| try { | |||
| createMergedDoPickOrder(group) | |||
| println("🔍 DEBUG: Created DoPickOrder for ${group.size} DOs") | |||
| println(" DEBUG: Created DoPickOrder for ${group.size} DOs") | |||
| } catch (e: Exception) { | |||
| println("❌ Error creating DoPickOrder: ${e.message}") | |||
| e.printStackTrace() | |||
| @@ -653,7 +653,7 @@ class DoReleaseCoordinatorService( | |||
| // 直接使用 doPickOrderRepository.save() 而不是 doPickOrderService.save() | |||
| 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}") | |||
| // 创建多条 DoPickOrderLine(每个 DO 一条) | |||
| @@ -674,7 +674,7 @@ class DoReleaseCoordinatorService( | |||
| status = "pending" // 初始状态 | |||
| } | |||
| doPickOrderLineRepository.save(line) | |||
| println("🔍 DEBUG: Created DoPickOrderLine for pick order ${result.pickOrderId}") | |||
| println(" DEBUG: Created DoPickOrderLine for pick order ${result.pickOrderId}") | |||
| } | |||
| // 现在检查整个 DoPickOrder 是否有库存问题 | |||
| @@ -686,10 +686,10 @@ class DoReleaseCoordinatorService( | |||
| line.status = "issue" | |||
| } | |||
| doPickOrderLineRepository.saveAll(doPickOrderLines) | |||
| println("🔍 DEBUG: Updated ${doPickOrderLines.size} DoPickOrderLine records to 'issue' status") | |||
| println(" DEBUG: Updated ${doPickOrderLines.size} DoPickOrderLine records to 'issue' status") | |||
| } | |||
| println("🔍 DEBUG: Created ${results.size} DoPickOrderLine records") | |||
| println(" DEBUG: Created ${results.size} DoPickOrderLine records") | |||
| } | |||
| private fun checkPickOrderHasStockIssues(doPickOrderId: Long): Boolean { | |||
| try { | |||
| @@ -724,7 +724,7 @@ class DoReleaseCoordinatorService( | |||
| // 3. 只有当所有 pick orders 都有问题时才算 issue | |||
| val hasAllPickOrdersIssues = (totalPickOrders > 0) && (issuePickOrders == totalPickOrders) | |||
| println("🔍 DEBUG: DoPickOrder $doPickOrderId - Total pick orders: $totalPickOrders, Issue pick orders: $issuePickOrders, All pick orders have issues: $hasAllPickOrdersIssues") | |||
| println(" DEBUG: DoPickOrder $doPickOrderId - Total pick orders: $totalPickOrders, Issue pick orders: $issuePickOrders, All pick orders have issues: $hasAllPickOrdersIssues") | |||
| return hasAllPickOrdersIssues | |||
| @@ -811,7 +811,7 @@ class DoReleaseCoordinatorService( | |||
| status = "pending" | |||
| } | |||
| doPickOrderLineRepository.save(line) | |||
| println("🔍 DEBUG: Created DoPickOrderLine for existing DoPickOrder ${existingDoPickOrder.id}") | |||
| println(" DEBUG: Created DoPickOrderLine for existing DoPickOrder ${existingDoPickOrder.id}") | |||
| } | |||
| } else { | |||
| // 如果不存在,创建新的 do_pick_order | |||
| @@ -843,7 +843,7 @@ class DoReleaseCoordinatorService( | |||
| status = "pending" | |||
| } | |||
| doPickOrderLineRepository.save(line) | |||
| println("🔍 DEBUG: Created new DoPickOrder with releaseType=single") | |||
| println(" DEBUG: Created new DoPickOrder with releaseType=single") | |||
| } | |||
| // 更新 ticket numbers(只更新 single 类型的) | |||
| @@ -873,11 +873,11 @@ class DoReleaseCoordinatorService( | |||
| } | |||
| if (tempTickets.isEmpty()) { | |||
| println("🔍 No single release type tickets to update") | |||
| println(" No single release type tickets to update") | |||
| return | |||
| } | |||
| println("🔍 DEBUG: Found ${tempTickets.size} single release type tickets to update") | |||
| println(" DEBUG: Found ${tempTickets.size} single release type tickets to update") | |||
| // 2. 按日期和 storeId 分组 | |||
| val grouped = tempTickets.groupBy { ticket -> | |||
| @@ -918,7 +918,7 @@ class DoReleaseCoordinatorService( | |||
| } else null | |||
| }.toMutableSet() | |||
| println("🔍 DEBUG: Group ($date, $storeId) - Existing ticket numbers: $existingTickets, Existing numbers: $existingNumbers") | |||
| println(" DEBUG: Group ($date, $storeId) - Existing ticket numbers: $existingTickets, Existing numbers: $existingNumbers") | |||
| // 5. 在组内按 truckDepartureTime, truckLanceCode, loadingSequence, doOrderId 排序 | |||
| val sortedTickets = tickets.sortedWith( | |||
| @@ -948,7 +948,7 @@ class DoReleaseCoordinatorService( | |||
| existingNumbers.add(nextNumber) | |||
| nextNumber++ | |||
| println("🔍 DEBUG: Updated ticket ${ticket.id} to $newTicketNo") | |||
| println(" DEBUG: Updated ticket ${ticket.id} to $newTicketNo") | |||
| } | |||
| } | |||
| @@ -260,7 +260,7 @@ open class JoPickOrderService( | |||
| statusList | |||
| ).filter { it.jobOrder != null } // Only pick orders with joId | |||
| println("🔍 DEBUG: Found ${allAssignedPickOrders.size} job order pick orders assigned to user $userId") | |||
| println(" DEBUG: Found ${allAssignedPickOrders.size} job order pick orders assigned to user $userId") | |||
| // Filter based on assignment and status | |||
| val filteredPickOrders = if (allAssignedPickOrders.isNotEmpty()) { | |||
| @@ -271,7 +271,7 @@ open class JoPickOrderService( | |||
| if (assignedReleasedOrders.isNotEmpty()) { | |||
| // If there are assigned RELEASED orders, show only those | |||
| println("🔍 DEBUG: Found ${assignedReleasedOrders.size} assigned RELEASED job orders, showing only those") | |||
| println(" DEBUG: Found ${assignedReleasedOrders.size} assigned RELEASED job orders, showing only those") | |||
| assignedReleasedOrders | |||
| } else { | |||
| // If no assigned RELEASED orders, show only the latest COMPLETED order | |||
| @@ -279,10 +279,10 @@ open class JoPickOrderService( | |||
| if (completedOrders.isNotEmpty()) { | |||
| val latestCompleted = | |||
| completedOrders.maxByOrNull { it.completeDate ?: it.modified ?: LocalDateTime.MIN } | |||
| println("🔍 DEBUG: No assigned RELEASED job orders, showing latest completed order: ${latestCompleted?.code}") | |||
| println(" DEBUG: No assigned RELEASED job orders, showing latest completed order: ${latestCompleted?.code}") | |||
| listOfNotNull(latestCompleted) | |||
| } else { | |||
| println("🔍 DEBUG: No job orders found") | |||
| println(" DEBUG: No job orders found") | |||
| emptyList() | |||
| } | |||
| } | |||
| @@ -569,7 +569,7 @@ open class JoPickOrderService( | |||
| .findAllByAssignToIdAndStatusIn(userId, listOf(PickOrderStatus.COMPLETED)) | |||
| .filter { it.jobOrder != null } | |||
| println("🔍 DEBUG: Found ${completedPickOrders.size} completed job order pick orders assigned to user $userId") | |||
| println(" DEBUG: Found ${completedPickOrders.size} completed job order pick orders assigned to user $userId") | |||
| if (completedPickOrders.isEmpty()) { | |||
| return mapOf("pickOrder" to null, "pickOrderLines" to emptyList<Map<String, Any?>>()) | |||
| @@ -580,7 +580,7 @@ open class JoPickOrderService( | |||
| it.completeDate ?: it.modified ?: LocalDateTime.MIN | |||
| } ?: return mapOf("pickOrder" to null, "pickOrderLines" to emptyList<Map<String, Any?>>()) | |||
| println("🔍 DEBUG: Using latest completed order: ${latestCompletedOrder.code}") | |||
| println(" DEBUG: Using latest completed order: ${latestCompletedOrder.code}") | |||
| val pickOrder = latestCompletedOrder | |||
| val jobOrder = pickOrder.jobOrder!! | |||
| @@ -1524,8 +1524,8 @@ open fun getCompletedJobOrderPickOrderLotDetails(pickOrderId: Long): List<Map<St | |||
| il.lotNo ASC | |||
| """.trimIndent() | |||
| println("🔍 Executing SQL for Job Order pick order lot details: $sql") | |||
| println("🔍 With parameters: pickOrderId = $pickOrderId") | |||
| println(" Executing SQL for Job Order pick order lot details: $sql") | |||
| println(" With parameters: pickOrderId = $pickOrderId") | |||
| val results = jdbcDao.queryForList(sql, mapOf("pickOrderId" to pickOrderId)) | |||
| @@ -1733,8 +1733,8 @@ open fun getCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId: Lo | |||
| il.lotNo ASC | |||
| """.trimIndent() | |||
| println("🔍 Executing SQL for completed pick order lot details: $sql") | |||
| println("🔍 With parameters: pickOrderId = $pickOrderId") | |||
| println(" Executing SQL for completed pick order lot details: $sql") | |||
| println(" With parameters: pickOrderId = $pickOrderId") | |||
| val results = jdbcDao.queryForList(sql, mapOf("pickOrderId" to pickOrderId)) | |||
| @@ -1286,8 +1286,8 @@ open class PickOrderService( | |||
| doPickOrderLineRepository.findByPickOrderIdAndDeletedFalse(pickOrderId) | |||
| val doPickOrderIds = doPickOrderLines.mapNotNull { it.doPickOrderId }.distinct() | |||
| println("🔍 DEBUG: Found ${doPickOrderLines.size} do_pick_order_line records for pick order $pickOrderId") | |||
| println("🔍 DEBUG: Unique do_pick_order IDs: $doPickOrderIds") | |||
| println(" DEBUG: Found ${doPickOrderLines.size} do_pick_order_line records for pick order $pickOrderId") | |||
| println(" DEBUG: Unique do_pick_order IDs: $doPickOrderIds") | |||
| if (doPickOrderIds.isEmpty()) { | |||
| println("ℹ️ INFO: No do_pick_order records found - skipping record copying") | |||
| @@ -1302,13 +1302,13 @@ open class PickOrderService( | |||
| return@forEach | |||
| } | |||
| println("🔍 Processing do_pick_order ID: ${dpo.id}, ticket: ${dpo.ticketNo}") | |||
| println(" Processing do_pick_order ID: ${dpo.id}, ticket: ${dpo.ticketNo}") | |||
| // 新增:检查这个 do_pick_order 下是否还有其他未完成的 pick orders | |||
| val allDoPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(dpo.id!!) | |||
| val allPickOrderIdsInDpo = allDoPickOrderLines.mapNotNull { it.pickOrderId }.distinct() | |||
| println("🔍 DEBUG: This do_pick_order has ${allPickOrderIdsInDpo.size} pick orders") | |||
| println(" DEBUG: This do_pick_order has ${allPickOrderIdsInDpo.size} pick orders") | |||
| // 检查每个 pick order 的状态 | |||
| val pickOrderStatuses = allPickOrderIdsInDpo.map { poId -> | |||
| @@ -1317,7 +1317,7 @@ open class PickOrderService( | |||
| } | |||
| pickOrderStatuses.forEach { (poId, status) -> | |||
| println("🔍 DEBUG: Pick order $poId status: $status") | |||
| println(" DEBUG: Pick order $poId status: $status") | |||
| } | |||
| // 只有当所有 pick orders 都是 COMPLETED 状态时,才移动到 record 表 | |||
| @@ -1327,7 +1327,7 @@ open class PickOrderService( | |||
| if (!allPickOrdersCompleted) { | |||
| println("⏳ Not all pick orders in this do_pick_order are completed, keeping do_pick_order alive") | |||
| println("🔍 DEBUG: Completed pick orders: ${pickOrderStatuses.count { it.second == PickOrderStatus.COMPLETED }}/${pickOrderStatuses.size}") | |||
| println(" DEBUG: Completed pick orders: ${pickOrderStatuses.count { it.second == PickOrderStatus.COMPLETED }}/${pickOrderStatuses.size}") | |||
| return@forEach // 跳过这个 do_pick_order,不删除它 | |||
| } | |||
| @@ -1654,7 +1654,7 @@ open class PickOrderService( | |||
| // 添加检查是否需要 resuggest 的方法 | |||
| private fun checkIfNeedsResuggest(pickOrderLineId: Long): Boolean { | |||
| println("🔍 checkIfNeedsResuggest called with pickOrderLineId: $pickOrderLineId") | |||
| println(" checkIfNeedsResuggest called with pickOrderLineId: $pickOrderLineId") | |||
| // 首先执行一个调试查询来查看实际的数值 | |||
| val debugSql = """ | |||
| @@ -1680,9 +1680,9 @@ open class PickOrderService( | |||
| WHERE spl.pickOrderLineId = :pickOrderLineId | |||
| """.trimIndent() | |||
| println("🔍 Debug SQL: $debugSql") | |||
| println(" Debug SQL: $debugSql") | |||
| val debugResult = jdbcDao.queryForList(debugSql, mapOf("pickOrderLineId" to pickOrderLineId)) | |||
| println("🔍 Debug result:") | |||
| println(" Debug result:") | |||
| debugResult.forEach { row -> | |||
| println(" SuggestedPickLotId: ${row["suggestedPickLotId"]}") | |||
| println(" SuggestedQty: ${row["suggestedQty"]}") | |||
| @@ -1730,12 +1730,12 @@ open class PickOrderService( | |||
| WHERE spl.pickOrderLineId = :pickOrderLineId | |||
| """.trimIndent() | |||
| println("🔍 Executing SQL: $checkSql") | |||
| println("🔍 With parameters: pickOrderLineId = $pickOrderLineId") | |||
| println(" Executing SQL: $checkSql") | |||
| println(" With parameters: pickOrderLineId = $pickOrderLineId") | |||
| val result = jdbcDao.queryForList(checkSql, mapOf("pickOrderLineId" to pickOrderLineId)) | |||
| println("🔍 SQL result size: ${result.size}") | |||
| println(" SQL result size: ${result.size}") | |||
| if (result.isNotEmpty()) { | |||
| val row = result[0] | |||
| @@ -1767,7 +1767,7 @@ open class PickOrderService( | |||
| println("Needs resuggest: $needsResuggest") | |||
| return needsResuggest | |||
| } else { | |||
| println("🔍 No results returned from SQL query") | |||
| println(" No results returned from SQL query") | |||
| } | |||
| return false | |||
| @@ -1839,7 +1839,7 @@ open class PickOrderService( | |||
| ).filter { it.deliveryOrder != null } | |||
| if (userExistingOrders.isNotEmpty()) { | |||
| println("🔍 DEBUG: User $userId already has ${userExistingOrders.size} pick orders in progress") | |||
| println(" DEBUG: User $userId already has ${userExistingOrders.size} pick orders in progress") | |||
| return MessageResponse( | |||
| id = null, | |||
| name = "User already has pick orders", | |||
| @@ -1961,14 +1961,14 @@ open class PickOrderService( | |||
| // Update do_pick_order_record entries | |||
| val existingRecords = doPickOrderRecordRepository.findByPickOrderId(selected.id!!) | |||
| println("🔍 DEBUG: Found ${existingRecords.size} existing DoPickOrderRecord entries for pick order ${selected.id}") | |||
| println(" DEBUG: Found ${existingRecords.size} existing DoPickOrderRecord entries for pick order ${selected.id}") | |||
| if (existingRecords.isNotEmpty()) { | |||
| existingRecords.forEach { record -> | |||
| record.handledBy = user.id | |||
| record.ticketStatus = DoPickOrderStatus.released | |||
| record.ticketReleaseTime = LocalDateTime.now() // 添加这行 | |||
| println("🔍 DEBUG: Updating existing DoPickOrderRecord ID: ${record.id} - handledBy: ${user.id}, status: released") | |||
| println(" DEBUG: Updating existing DoPickOrderRecord ID: ${record.id} - handledBy: ${user.id}, status: released") | |||
| } | |||
| doPickOrderRecordRepository.saveAll(existingRecords) | |||
| println(" Updated ${existingRecords.size} existing DoPickOrderRecord entries") | |||
| @@ -2108,7 +2108,7 @@ open class PickOrderService( | |||
| i.code ASC | |||
| """.trimIndent() | |||
| println("🔍 Executing optimized SQL: $sql") | |||
| println(" Executing optimized SQL: $sql") | |||
| val result = jdbcDao.queryForList(sql, emptyMap<String, Any>()) | |||
| @@ -2234,7 +2234,7 @@ open class PickOrderService( | |||
| // Fix: Store consoCode in local variable to avoid smart cast issues | |||
| val consoCode = pickOrder.consoCode | |||
| if (consoCode == null) { | |||
| println("🔍 Checking pick order ${pickOrder.code} (consoCode: null)") | |||
| println(" Checking pick order ${pickOrder.code} (consoCode: null)") | |||
| println(" ⚠️ No consoCode - considering as active") | |||
| true // No consoCode means not completed | |||
| } else { | |||
| @@ -2501,9 +2501,9 @@ open class PickOrderService( | |||
| ).filter { it.deliveryOrder != null } | |||
| if (userExistingOrders.isNotEmpty()) { | |||
| println("🔍 DEBUG: User $userId already has ${userExistingOrders.size} pick orders in progress:") | |||
| println(" DEBUG: User $userId already has ${userExistingOrders.size} pick orders in progress:") | |||
| userExistingOrders.forEach { po -> | |||
| println("🔍 DEBUG: Existing order ${po.id}: code=${po.code}, status=${po.status}") | |||
| println(" DEBUG: Existing order ${po.id}: code=${po.code}, status=${po.status}") | |||
| } | |||
| return MessageResponse( | |||
| id = null, | |||
| @@ -2624,15 +2624,15 @@ open class PickOrderService( | |||
| val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now() | |||
| val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")) | |||
| println("🔍 DEBUG: Target date: $targetDate, Date prefix: $datePrefix") | |||
| println(" DEBUG: Target date: $targetDate, Date prefix: $datePrefix") | |||
| // Find truck by shop ID with earliest departure time | |||
| val truck = deliveryOrder.shop?.id?.let { shopId -> | |||
| println("🔍 DEBUG: Looking for truck with shop ID: $shopId") | |||
| println(" DEBUG: Looking for truck with shop ID: $shopId") | |||
| val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | |||
| println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") | |||
| println(" DEBUG: Found ${trucks.size} trucks for shop $shopId") | |||
| val selectedTruck = trucks.minByOrNull { it.departureTime ?: LocalTime.MAX } | |||
| println("🔍 DEBUG: Selected truck: ID=${selectedTruck?.id}, DepartureTime=${selectedTruck?.departureTime}") | |||
| println(" DEBUG: Selected truck: ID=${selectedTruck?.id}, DepartureTime=${selectedTruck?.departureTime}") | |||
| selectedTruck | |||
| } | |||
| @@ -2645,13 +2645,13 @@ open class PickOrderService( | |||
| // Get next ticket number for this date and store | |||
| val nextTicketNumber = doPickOrderService.getNextTicketNumber(datePrefix, determinedStoreId) | |||
| println("🔍 DEBUG: Next ticket number: $nextTicketNumber") | |||
| println(" DEBUG: Next ticket number: $nextTicketNumber") | |||
| println("🔍 DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines") | |||
| println(" DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines") | |||
| // UPDATE existing do_pick_order_record entries instead of creating new ones | |||
| val existingRecords = doPickOrderRecordRepository.findByPickOrderId(selected.id!!) | |||
| println("🔍 DEBUG: Found ${existingRecords.size} existing DoPickOrderRecord entries for pick order ${selected.id}") | |||
| println(" DEBUG: Found ${existingRecords.size} existing DoPickOrderRecord entries for pick order ${selected.id}") | |||
| if (existingRecords.isNotEmpty()) { | |||
| // Update existing records | |||
| @@ -2664,7 +2664,7 @@ open class PickOrderService( | |||
| record.shopCode = deliveryOrder.shop?.code | |||
| record.shopName = deliveryOrder.shop?.name | |||
| record.requiredDeliveryDate = targetDate | |||
| println("🔍 DEBUG: Updating existing DoPickOrderRecord ID: ${record.id} - handledBy: ${user.id}, status: released") | |||
| println(" DEBUG: Updating existing DoPickOrderRecord ID: ${record.id} - handledBy: ${user.id}, status: released") | |||
| } | |||
| doPickOrderRecordRepository.saveAll(existingRecords) | |||
| println(" Updated ${existingRecords.size} existing DoPickOrderRecord entries") | |||
| @@ -2672,7 +2672,7 @@ open class PickOrderService( | |||
| // Only create new records if none exist (fallback) | |||
| println("⚠️ No existing DoPickOrderRecord entries found, creating new ones") | |||
| deliveryOrder.deliveryOrderLines.forEach { line -> | |||
| println("🔍 DEBUG: Processing line - Store ID: $determinedStoreId") | |||
| println(" DEBUG: Processing line - Store ID: $determinedStoreId") | |||
| val doPickOrderRecord = DoPickOrderRecord( | |||
| id=dopickorder.id, | |||
| @@ -2691,10 +2691,10 @@ open class PickOrderService( | |||
| requiredDeliveryDate = targetDate | |||
| ) | |||
| println("🔍 DEBUG: Creating new DoPickOrderRecord - Store: $determinedStoreId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") | |||
| println(" DEBUG: Creating new DoPickOrderRecord - Store: $determinedStoreId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") | |||
| val savedDoPickOrderRecord = doPickOrderRecordRepository.save(doPickOrderRecord) | |||
| println("🔍 DEBUG: Saved new DoPickOrderRecord - ID: ${savedDoPickOrderRecord.id}") | |||
| println(" DEBUG: Saved new DoPickOrderRecord - ID: ${savedDoPickOrderRecord.id}") | |||
| } | |||
| } | |||
| } | |||
| @@ -2747,9 +2747,9 @@ open class PickOrderService( | |||
| listOf(PickOrderStatus.PENDING, PickOrderStatus.RELEASED, PickOrderStatus.COMPLETED) | |||
| ).filter { it.deliveryOrder != null } // Only pick orders with doId | |||
| println("🔍 DEBUG: All assigned pick orders: ${allAssignedPickOrders.size}") | |||
| println(" DEBUG: All assigned pick orders: ${allAssignedPickOrders.size}") | |||
| val pickOrderIds = allAssignedPickOrders.map { it.id!! } | |||
| println("🔍 DEBUG: All assigned pick orders: ${pickOrderIds.size}") | |||
| println(" DEBUG: All assigned pick orders: ${pickOrderIds.size}") | |||
| println(" Pick order IDs to fetch: $pickOrderIds") | |||
| if (pickOrderIds.isEmpty()) { | |||
| @@ -2883,7 +2883,7 @@ open class PickOrderService( | |||
| il.lotNo ASC | |||
| """.trimIndent() | |||
| println("🔍 Executing SQL for all pick order lots with details: $sql") | |||
| println(" Executing SQL for all pick order lots with details: $sql") | |||
| println(" With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr") | |||
| val results = jdbcDao.queryForList(sql, mapOf("userId" to userId)) | |||
| @@ -2903,7 +2903,7 @@ open class PickOrderService( | |||
| open fun getFgPickOrders(): List<Map<String, Any?>> { | |||
| try { | |||
| println("🔍 Starting getFgPickOrders method - using repository") | |||
| println(" Starting getFgPickOrders method - using repository") | |||
| // Use repository to find pick orders with type 'do' | |||
| val pickOrders = pickOrderRepository.findAll().filter { | |||
| @@ -2911,7 +2911,7 @@ open class PickOrderService( | |||
| it.status?.value in listOf("assigned", "released", "picking") | |||
| } | |||
| println("🔍 Found ${pickOrders.size} FG pick orders using repository") | |||
| println(" Found ${pickOrders.size} FG pick orders using repository") | |||
| val results = pickOrders.map { po -> | |||
| val deliveryOrder = po.deliveryOrder | |||
| @@ -2968,7 +2968,7 @@ open class PickOrderService( | |||
| open fun getFgPickOrdersByUserId(userId: Long): List<Map<String, Any?>> { | |||
| try { | |||
| println("🔍 Starting getFgPickOrdersByUserId with userId: $userId") | |||
| println(" Starting getFgPickOrdersByUserId with userId: $userId") | |||
| // 修复:从 do_pick_order_line 获取 pick order 信息 | |||
| val sql = """ | |||
| @@ -3018,8 +3018,8 @@ open class PickOrderService( | |||
| ORDER BY MIN(po.targetDate) DESC, MIN(po.code) ASC | |||
| """.trimIndent() | |||
| println("🔍 Executing SQL for FG pick orders by userId: $sql") | |||
| println("🔍 With parameters: userId = $userId") | |||
| println(" Executing SQL for FG pick orders by userId: $sql") | |||
| println(" With parameters: userId = $userId") | |||
| val results = jdbcDao.queryForList(sql, mapOf("userId" to userId)) | |||
| @@ -3028,11 +3028,11 @@ open class PickOrderService( | |||
| return emptyList() | |||
| } | |||
| println("🔍 Found ${results.size} active FG pick orders for user: $userId") | |||
| println(" Found ${results.size} active FG pick orders for user: $userId") | |||
| // 添加调试信息 | |||
| results.forEachIndexed { index, row -> | |||
| println("🔍 DEBUG: Result $index:") | |||
| println(" DEBUG: Result $index:") | |||
| println(" - doPickOrderId: ${row["doPickOrderId"]}") | |||
| println(" - pickOrderIds: ${row["pickOrderIds"]}") | |||
| println(" - numberOfPickOrders: ${row["numberOfPickOrders"]}") | |||
| @@ -3088,7 +3088,7 @@ open class PickOrderService( | |||
| open fun getFgPickOrdersByPickOrderId(pickOrderId: Long): List<Map<String, Any?>> { | |||
| try { | |||
| println("🔍 Starting getFgPickOrdersByPickOrderId method with pickOrderId: $pickOrderId") | |||
| println(" Starting getFgPickOrdersByPickOrderId method with pickOrderId: $pickOrderId") | |||
| val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) | |||
| @@ -3097,7 +3097,7 @@ open class PickOrderService( | |||
| return emptyList() | |||
| } | |||
| println("🔍 Found pick order: ${pickOrder.code}, type: ${pickOrder.type?.value}, status: ${pickOrder.status?.value}") | |||
| println(" Found pick order: ${pickOrder.code}, type: ${pickOrder.type?.value}, status: ${pickOrder.status?.value}") | |||
| if (pickOrder.type?.value != "do") { | |||
| println("❌ Pick order is not of type 'do': ${pickOrder.type?.value}") | |||
| @@ -3117,16 +3117,16 @@ open class PickOrderService( | |||
| val supplier = deliveryOrder?.supplier | |||
| println(" Delivery order: ${deliveryOrder?.code}, Shop: ${shop?.name}, Supplier: ${supplier?.code}") | |||
| println("🔍 Shop ID: ${shop?.id}") | |||
| println(" Shop ID: ${shop?.id}") | |||
| // Get truck information using repository with detailed debugging | |||
| val truck = if (shop?.id != null) { | |||
| try { | |||
| println("🔍 Querying truck repository for shopId: ${shop.id}") | |||
| println(" Querying truck repository for shopId: ${shop.id}") | |||
| // Get all trucks for this shop | |||
| val trucksForShop = truckRepository.findByShopIdAndDeletedFalse(shop.id) | |||
| println("🔍 Trucks for shop ${shop.id}: ${trucksForShop.size}") | |||
| println(" Trucks for shop ${shop.id}: ${trucksForShop.size}") | |||
| trucksForShop.forEach { t -> | |||
| println(" - Truck ID: ${t.id}, truckLanceCode: ${t.truckLanceCode}, ShopId: ${t.shop?.id}, Deleted: ${t.deleted}") | |||
| } | |||
| @@ -3153,7 +3153,7 @@ open class PickOrderService( | |||
| val ticketNo = try { | |||
| val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | |||
| val ticketNo = doPickOrders.firstOrNull()?.ticketNo ?: "" | |||
| println("🔍 Found ticket number: $ticketNo for pick order $pickOrderId") | |||
| println(" Found ticket number: $ticketNo for pick order $pickOrderId") | |||
| ticketNo | |||
| } catch (e: Exception) { | |||
| println("⚠️ Error getting ticket number for pick order $pickOrderId: ${e.message}") | |||
| @@ -3200,7 +3200,7 @@ open class PickOrderService( | |||
| open fun getnewFgPickOrdersByPickOrderId(pickOrderId: Long): List<Map<String, Any?>> { | |||
| try { | |||
| println("🔍 Starting getnewFgPickOrdersByPickOrderId method with pickOrderId: $pickOrderId") | |||
| println(" Starting getnewFgPickOrdersByPickOrderId method with pickOrderId: $pickOrderId") | |||
| val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) | |||
| @@ -3209,7 +3209,7 @@ open class PickOrderService( | |||
| return emptyList() | |||
| } | |||
| println("🔍 Found pick order: ${pickOrder.code}, type: ${pickOrder.type?.value}, status: ${pickOrder.status?.value}") | |||
| println(" Found pick order: ${pickOrder.code}, type: ${pickOrder.type?.value}, status: ${pickOrder.status?.value}") | |||
| if (pickOrder.type?.value != "do") { | |||
| println("❌ Pick order is not of type 'do': ${pickOrder.type?.value}") | |||
| @@ -3229,16 +3229,16 @@ open class PickOrderService( | |||
| val supplier = deliveryOrder?.supplier | |||
| println(" Delivery order: ${deliveryOrder?.code}, Shop: ${shop?.name}, Supplier: ${supplier?.code}") | |||
| println("🔍 Shop ID: ${shop?.id}") | |||
| println(" Shop ID: ${shop?.id}") | |||
| // Get truck information using repository with detailed debugging | |||
| val truck = if (shop?.id != null) { | |||
| try { | |||
| println("🔍 Querying truck repository for shopId: ${shop.id}") | |||
| println(" Querying truck repository for shopId: ${shop.id}") | |||
| // Get all trucks for this shop | |||
| val trucksForShop = truckRepository.findByShopIdAndDeletedFalse(shop.id) | |||
| println("🔍 Trucks for shop ${shop.id}: ${trucksForShop.size}") | |||
| println(" Trucks for shop ${shop.id}: ${trucksForShop.size}") | |||
| trucksForShop.forEach { t -> | |||
| println(" - Truck ID: ${t.id}, TruckLanceCode: ${t.truckLanceCode}, ShopId: ${t.shop?.id}, Deleted: ${t.deleted}") | |||
| } | |||
| @@ -3265,7 +3265,7 @@ open class PickOrderService( | |||
| val ticketNo = try { | |||
| val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | |||
| val ticketNo = doPickOrders.firstOrNull()?.ticketNo ?: "" | |||
| println("🔍 Found ticket number: $ticketNo for pick order $pickOrderId") | |||
| println(" Found ticket number: $ticketNo for pick order $pickOrderId") | |||
| ticketNo | |||
| } catch (e: Exception) { | |||
| println("⚠️ Error getting ticket number for pick order $pickOrderId: ${e.message}") | |||
| @@ -3342,9 +3342,9 @@ open class PickOrderService( | |||
| user, | |||
| listOf(PickOrderStatus.PENDING, PickOrderStatus.RELEASED, PickOrderStatus.COMPLETED) | |||
| ).filter { it.deliveryOrder != null } // Only pick orders with doId | |||
| println("🔍 DEBUG: Found ${allAssignedPickOrders.size} pick orders assigned to user $userId") | |||
| println(" DEBUG: Found ${allAssignedPickOrders.size} pick orders assigned to user $userId") | |||
| allAssignedPickOrders.forEach { po -> | |||
| println("🔍 DEBUG: Pick order ${po.id}: code=${po.code}, status=${po.status}, assignTo=${po.assignTo?.id}, doId=${po.deliveryOrder?.id}") | |||
| println(" DEBUG: Pick order ${po.id}: code=${po.code}, status=${po.status}, assignTo=${po.assignTo?.id}, doId=${po.deliveryOrder?.id}") | |||
| } | |||
| // NEW LOGIC: Filter based on assignment and status | |||
| val filteredPickOrders = if (allAssignedPickOrders.isNotEmpty()) { | |||
| @@ -3355,7 +3355,7 @@ open class PickOrderService( | |||
| if (assignedReleasedOrders.isNotEmpty()) { | |||
| // If there are assigned RELEASED orders, show only those | |||
| println("🔍 DEBUG: Found ${assignedReleasedOrders.size} assigned RELEASED orders, showing only those") | |||
| println(" DEBUG: Found ${assignedReleasedOrders.size} assigned RELEASED orders, showing only those") | |||
| assignedReleasedOrders | |||
| } else { | |||
| // If no assigned RELEASED orders, show only the latest COMPLETED order | |||
| @@ -3366,7 +3366,7 @@ open class PickOrderService( | |||
| println("�� DEBUG: No assigned RELEASED orders, showing latest completed order: ${latestCompleted?.code}") | |||
| listOfNotNull(latestCompleted) | |||
| } else { | |||
| println("🔍 DEBUG: No orders found") | |||
| println(" DEBUG: No orders found") | |||
| emptyList() | |||
| } | |||
| } | |||
| @@ -3523,7 +3523,7 @@ ORDER BY | |||
| il.lotNo ASC | |||
| """.trimIndent() | |||
| println("🔍 Executing SQL for all pick order lots with details (no auto-assign): $sql") | |||
| println(" Executing SQL for all pick order lots with details (no auto-assign): $sql") | |||
| println(" With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr") | |||
| val results = jdbcDao.queryForList(sql, mapOf("userId" to userId)) | |||
| @@ -3630,7 +3630,7 @@ ORDER BY | |||
| ) | |||
| } | |||
| println("🔍 Found ${userPickOrders.size} pick orders assigned to user $userId") | |||
| println(" Found ${userPickOrders.size} pick orders assigned to user $userId") | |||
| val pickOrderIds = userPickOrders.mapNotNull { it.id } | |||
| // Step 2: 通过 do_pick_order_line 找到相关的 do_pick_order | |||
| @@ -3648,7 +3648,7 @@ ORDER BY | |||
| ) | |||
| } | |||
| println("🔍 Found ${doPickOrderIds.size} do_pick_order records") | |||
| println(" Found ${doPickOrderIds.size} do_pick_order records") | |||
| // Step 3: 获取第一个 do_pick_order 的详细信息(用于构建 fgInfo) | |||
| val doPickOrder = doPickOrderRepository.findById(doPickOrderIds.first()).orElse(null) | |||
| @@ -3661,13 +3661,13 @@ ORDER BY | |||
| } | |||
| val doPickOrderId = doPickOrder.id!! | |||
| println("🔍 Using do_pick_order ID: $doPickOrderId") | |||
| println(" Using do_pick_order ID: $doPickOrderId") | |||
| // Step 4: 使用 Repository 加载 pick orders 及其关联数据 | |||
| val pickOrders = pickOrderRepository.findAllById(pickOrderIds) | |||
| .filter { it.deleted == false } | |||
| println("🔍 Loaded ${pickOrders.size} pick orders") | |||
| println(" Loaded ${pickOrders.size} pick orders") | |||
| // 收集所有需要的数据 | |||
| // 收集所有需要的数据 | |||
| @@ -4037,7 +4037,7 @@ open fun getAllPickOrderLotsWithDetailsHierarchicalold(userId: Long): Map<String | |||
| } | |||
| val doPickOrderId = (doPickOrderInfo["do_pick_order_id"] as? Number)?.toLong() | |||
| println("🔍 Found do_pick_order ID: $doPickOrderId") | |||
| println(" Found do_pick_order ID: $doPickOrderId") | |||
| val doTicketStatus = doPickOrderInfo["ticket_status"] | |||
| // Step 2: 获取该 do_pick_order 下的所有 pick orders | |||
| val pickOrdersSql = """ | |||
| @@ -4058,7 +4058,7 @@ open fun getAllPickOrderLotsWithDetailsHierarchicalold(userId: Long): Map<String | |||
| """.trimIndent() | |||
| val pickOrdersInfo = jdbcDao.queryForList(pickOrdersSql, mapOf("doPickOrderId" to doPickOrderId)) | |||
| println("🔍 Found ${pickOrdersInfo.size} pick orders") | |||
| println(" Found ${pickOrdersInfo.size} pick orders") | |||
| // Step 3: 为每个 pick order 获取 lines 和 lots | |||
| val allPickOrderLines = mutableListOf<Map<String, Any?>>() | |||
| @@ -4201,7 +4201,7 @@ open fun getAllPickOrderLotsWithDetailsHierarchicalold(userId: Long): Map<String | |||
| """.trimIndent() | |||
| val linesResults = jdbcDao.queryForList(linesSql, mapOf("pickOrderId" to pickOrderId)) | |||
| println("🔍 Pick order $pickOrderId has ${linesResults.size} line-lot records") | |||
| println(" Pick order $pickOrderId has ${linesResults.size} line-lot records") | |||
| println("DEBUG polIds in linesResults: " + linesResults.map { it["pickOrderLineId"] }.distinct()) | |||
| println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["stockOutLineId"]?.let { _ -> it["pickOrderLineId"] } }.distinct()) | |||
| // 按 pickOrderLineId 分组 | |||
| @@ -4377,8 +4377,8 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto | |||
| ORDER BY orderDate ASC | |||
| """.trimIndent() | |||
| println("🔍 Executing SQL: $sql") | |||
| println("🔍 With parameters: storeId = $storeId") | |||
| println(" Executing SQL: $sql") | |||
| println(" With parameters: storeId = $storeId") | |||
| val results = jdbcDao.queryForList(sql, mapOf("storeId" to storeId)) | |||
| println(" Found ${results.size} records") | |||
| @@ -4743,7 +4743,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri | |||
| """.trimIndent() | |||
| val pickOrdersInfo = jdbcDao.queryForList(pickOrdersSql, mapOf("doPickOrderRecordId" to doPickOrderRecordId)) | |||
| println("🔍 Found ${pickOrdersInfo.size} pick orders in do_pick_order_record") | |||
| println(" Found ${pickOrdersInfo.size} pick orders in do_pick_order_record") | |||
| // Step 3: 为每个 pick order 获取 lines 和 lots | |||
| val pickOrders = pickOrdersInfo.map { poInfo -> | |||
| @@ -5082,7 +5082,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri | |||
| pickOrders = emptyList() | |||
| ) | |||
| println("🔍 Found DoPickOrderRecord: id=${dpor.id}, ticketNo=${dpor.ticketNo}") | |||
| println(" Found DoPickOrderRecord: id=${dpor.id}, ticketNo=${dpor.ticketNo}") | |||
| // 2) 构建 fgInfo | |||
| val fgInfo = FgInfoResponse( | |||
| @@ -5097,7 +5097,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri | |||
| // 3) 通过 DoPickOrderLineRecord 获取 pickOrderIds | |||
| val lineRecords = doPickOrderLineRecordRepository.findByDoPickOrderIdAndDeletedFalse(dpor.recordId!!) | |||
| println("🔍 Found ${lineRecords.size} DoPickOrderLineRecord records") | |||
| println(" Found ${lineRecords.size} DoPickOrderLineRecord records") | |||
| var pickOrderIds = lineRecords.mapNotNull { it.pickOrderId }.distinct() | |||
| @@ -5115,7 +5115,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri | |||
| ) | |||
| } | |||
| println("🔍 Using pickOrderIds: $pickOrderIds") | |||
| println(" Using pickOrderIds: $pickOrderIds") | |||
| // 4) 批量加载所有相关数据 | |||
| val pickOrders = pickOrderRepository.findAllById(pickOrderIds) | |||
| @@ -5129,7 +5129,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri | |||
| ) | |||
| } | |||
| println("🔍 Found ${pickOrders.size} pick orders") | |||
| println(" Found ${pickOrders.size} pick orders") | |||
| // 5) 收集所有 pickOrderLineIds | |||
| val allPickOrderLineIds = pickOrders | |||
| @@ -5137,7 +5137,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri | |||
| .filter { !it.deleted } | |||
| .mapNotNull { it.id } | |||
| println("🔍 Found ${allPickOrderLineIds.size} pick order lines") | |||
| println(" Found ${allPickOrderLineIds.size} pick order lines") | |||
| // 6) 批量加载 suggestions 和 stock out lines | |||
| val allSuggestions = if (allPickOrderLineIds.isNotEmpty()) { | |||
| @@ -5317,7 +5317,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri | |||
| // 新增:从 do_pick_order_record 表查询(用于已完成的 pick orders) | |||
| open fun getFgPickOrdersFromRecordByPickOrderId(pickOrderId: Long): List<Map<String, Any?>> { | |||
| try { | |||
| println("🔍 Starting getFgPickOrdersFromRecordByPickOrderId with pickOrderId: $pickOrderId") | |||
| println(" Starting getFgPickOrdersFromRecordByPickOrderId with pickOrderId: $pickOrderId") | |||
| val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) | |||
| if (pickOrder == null) { | |||
| @@ -83,14 +83,14 @@ open class ProductProcessService( | |||
| } | |||
| open fun findById(id: Long): ProductProcess { | |||
| println("🔍 Service: Finding ProductProcess by ID: $id") | |||
| println(" Service: Finding ProductProcess by ID: $id") | |||
| return productProcessRepository.findById(id) | |||
| .orElseThrow { IllegalArgumentException("ProductProcess not found with id: $id") } | |||
| } | |||
| open fun findByCode(code: String): ProductProcess { | |||
| println("🔍 Service: Finding ProductProcess by code: $code") | |||
| println(" Service: Finding ProductProcess by code: $code") | |||
| return productProcessRepository.findByProductProcessCode(code) | |||
| .orElseThrow { IllegalArgumentException("ProductProcess not found with code: $code") } | |||
| } | |||
| @@ -126,7 +126,7 @@ open class ProductProcessService( | |||
| // 4. 查询 BOM 的所有工序步骤 | |||
| val bomProcesses = bomProcessRepository.findByBomIdOrderBySeqNo(request.bomId) | |||
| println("🔍 Service: Found ${bomProcesses.size} BOM processes") | |||
| println(" Service: Found ${bomProcesses.size} BOM processes") | |||
| // 5. 为每个 BOM Process 创建 ProductProcessLine | |||
| bomProcesses.forEachIndexed { index, bomProcess -> // 修复 forEach | |||
| @@ -274,14 +274,14 @@ open class ProductProcessService( | |||
| return issues | |||
| } | |||
| 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) | |||
| println(" Service: Found ${result.size} processes") | |||
| return result | |||
| } | |||
| open fun findByJobOrderIdWithLines(jobOrderId: Long): List<ProductProcessWithLinesResponse> { | |||
| println("🔍 Service: Finding ProductProcess with lines by jobOrderId: $jobOrderId") | |||
| println(" Service: Finding ProductProcess with lines by jobOrderId: $jobOrderId") | |||
| val processes = productProcessRepository.findByJobOrder_Id(jobOrderId) | |||
| val result = processes.map { process -> | |||
| @@ -338,7 +338,7 @@ open class ProductProcessService( | |||
| } | |||
| open fun findByIdAsDto(id: Long): ProductProcessSimpleResponse { | |||
| println("🔍 Service: Finding ProductProcess by ID: $id as DTO") | |||
| println(" Service: Finding ProductProcess by ID: $id as DTO") | |||
| val entity = productProcessRepository.findById(id) | |||
| .orElseThrow { IllegalArgumentException("ProductProcess not found with id: $id") } | |||
| @@ -480,7 +480,7 @@ open class ProductProcessService( | |||
| return saved | |||
| } | |||
| 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) | |||
| println(" Service: Found ${lines.size} pending lines") | |||
| return lines | |||
| @@ -25,7 +25,7 @@ class ProductProcessController( | |||
| @GetMapping("/{id}") | |||
| 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}") | |||
| return result | |||
| @@ -33,7 +33,7 @@ class ProductProcessController( | |||
| @GetMapping("/code/{code}") | |||
| 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) | |||
| println(" Controller: Found ID: ${result.id}") | |||
| return result | |||
| @@ -128,7 +128,7 @@ class ProductProcessController( | |||
| @GetMapping("/by-job-order/{jobOrderId}") | |||
| 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") | |||
| return result | |||
| @@ -137,7 +137,7 @@ class ProductProcessController( | |||
| // 额外添加:获取带 lines 的完整数据 | |||
| @GetMapping("/by-job-order/{jobOrderId}/with-lines") | |||
| 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) | |||
| println(" Controller: Found ${result.size} processes with lines for Job Order $jobOrderId") | |||
| return result | |||
| @@ -63,4 +63,11 @@ fun findAllByWarehouseIdInAndDeletedIsFalse(@Param("warehouseIds") warehouseIds: | |||
| ORDER BY ill.inventoryLot.item.code, ill.inventoryLot.lotNo | |||
| """) | |||
| fun findAllByWarehouseCodeAndDeletedIsFalse(@Param("warehouseCode") warehouseCode: String): List<InventoryLotLine> | |||
| @Query(""" | |||
| SELECT COUNT(DISTINCT ill.inventoryLot.item.id) | |||
| FROM InventoryLotLine ill | |||
| WHERE ill.warehouse.id IN :warehouseIds | |||
| AND ill.deleted = false | |||
| """) | |||
| fun countDistinctItemsByWarehouseIds(@Param("warehouseIds") warehouseIds: List<Long>): Long | |||
| } | |||
| @@ -108,6 +108,9 @@ open class StockInLine : BaseEntity<Long>() { | |||
| @Column(name = "remarks") | |||
| open var remarks: String? = null | |||
| @Column(name = "type") | |||
| open var type: String? = null | |||
| @JsonManagedReference | |||
| @OneToMany(mappedBy = "stockInLine", cascade = [CascadeType.ALL], orphanRemoval = true) | |||
| open var escalationLog: MutableList<EscalationLog>? = mutableListOf() | |||
| @@ -45,4 +45,7 @@ open class StockOutLine: BaseEntity<Long>() { | |||
| @ManyToOne | |||
| @JoinColumn(name = "pickOrderLineId") | |||
| open var pickOrderLine: PickOrderLine? = null | |||
| @Column(name = "type") | |||
| open var type: String? = null | |||
| } | |||
| @@ -97,9 +97,9 @@ class StockTakeRecordService( | |||
| .filter { it.status == StockTakeStatus.COMPLETED && it.actualEnd != null } | |||
| .sortedByDescending { it.actualEnd } | |||
| val lastStockTakeDate = completedStockTakes.firstOrNull()?.actualEnd?.toLocalDate() | |||
| val lastStockTakeDate = completedStockTakes.firstOrNull()?.actualEnd | |||
| // 7. 获取 status:获取最新的 stock_take 记录(按 actualStart 或 planStart 排序) | |||
| val latestStockTake = stockTakesForSection | |||
| .maxByOrNull { it.actualStart ?: it.planStart ?: LocalDateTime.MIN } | |||
| @@ -108,17 +108,65 @@ class StockTakeRecordService( | |||
| } else { | |||
| "no_cycle" | |||
| } | |||
| val stockTakerName = if (latestStockTake != null) { | |||
| // 从该 stock take 的 records 中获取最新的 stockTakerName | |||
| val recordsForStockTake = allStockTakeRecords | |||
| .filter { | |||
| it.stockTake?.id == latestStockTake.id && | |||
| it.stockTakeSection == stockTakeSection && | |||
| it.stockTakerName != null | |||
| } | |||
| .sortedByDescending { it.stockTakeStartTime ?: LocalDateTime.MIN } | |||
| recordsForStockTake.firstOrNull()?.stockTakerName | |||
| } else { | |||
| null | |||
| } | |||
| val approverName = if (latestStockTake != null) { | |||
| // 从该 stock take 的 records 中获取最新的 approverName | |||
| val recordsForStockTake = allStockTakeRecords | |||
| .filter { | |||
| it.stockTake?.id == latestStockTake.id && | |||
| it.stockTakeSection == stockTakeSection && | |||
| it.approverName != null | |||
| } | |||
| .sortedByDescending { it.stockTakeStartTime ?: LocalDateTime.MIN } | |||
| recordsForStockTake.firstOrNull()?.approverName | |||
| } else { | |||
| null | |||
| } | |||
| // 9. 计算 TotalItemNumber:获取该 section 下所有 InventoryLotLine,按 item 分组,计算不同的 item 数量 | |||
| val totalItemNumber = inventoryLotLineRepository.countDistinctItemsByWarehouseIds(warehouseIds).toInt() | |||
| // 8. 使用 stockTakeSection 作为 stockTakeSession | |||
| val reStockTakeTrueFalse = if (latestStockTake != null) { | |||
| // 检查该 stock take 下该 section 的记录中是否有 notMatch 状态 | |||
| val hasNotMatch = allStockTakeRecords.any { | |||
| !it.deleted && | |||
| it.stockTake?.id == latestStockTake.id && | |||
| it.stockTakeSection == stockTakeSection && | |||
| it.status == "notMatch" | |||
| } | |||
| hasNotMatch | |||
| } else { | |||
| false | |||
| } | |||
| result.add( | |||
| AllPickedStockTakeListReponse( | |||
| id = idCounter++, | |||
| stockTakeSession = stockTakeSection, | |||
| lastStockTakeDate = lastStockTakeDate, | |||
| status = status, | |||
| currentStockTakeItemNumber = 0, // 临时设为 0,测试性能 | |||
| totalInventoryLotNumber = 0, // 临时设为 0,测试性能 | |||
| stockTakeId = latestStockTake?.id ?: 0 | |||
| lastStockTakeDate = latestStockTake?.actualStart?.toLocalDate(), | |||
| status = status?:"", | |||
| currentStockTakeItemNumber = 0, | |||
| totalInventoryLotNumber = 0, | |||
| stockTakeId = latestStockTake?.id ?: 0, | |||
| stockTakerName = stockTakerName, | |||
| TotalItemNumber = totalItemNumber, | |||
| approverName = approverName, | |||
| startTime = latestStockTake?.actualStart, | |||
| endTime = latestStockTake?.actualEnd, | |||
| ReStockTakeTrueFalse = reStockTakeTrueFalse | |||
| ) | |||
| ) | |||
| } | |||
| @@ -196,17 +244,65 @@ class StockTakeRecordService( | |||
| } else { | |||
| null | |||
| } | |||
| val stockTakerName = if (latestStockTake != null) { | |||
| // 从该 stock take 的 records 中获取最新的 stockTakerName | |||
| val recordsForStockTake = allStockTakeRecords | |||
| .filter { | |||
| it.stockTake?.id == latestStockTake.id && | |||
| it.stockTakeSection == stockTakeSection && | |||
| it.stockTakerName != null | |||
| } | |||
| .sortedByDescending { it.stockTakeStartTime ?: LocalDateTime.MIN } | |||
| recordsForStockTake.firstOrNull()?.stockTakerName | |||
| } else { | |||
| null | |||
| } | |||
| val approverName = if (latestStockTake != null) { | |||
| // 从该 stock take 的 records 中获取最新的 approverName | |||
| val recordsForStockTake = allStockTakeRecords | |||
| .filter { | |||
| it.stockTake?.id == latestStockTake.id && | |||
| it.stockTakeSection == stockTakeSection && | |||
| it.approverName != null | |||
| } | |||
| .sortedByDescending { it.stockTakeStartTime ?: LocalDateTime.MIN } | |||
| recordsForStockTake.firstOrNull()?.approverName | |||
| } else { | |||
| null | |||
| } | |||
| val reStockTakeTrueFalse = if (latestStockTake != null) { | |||
| // 检查该 stock take 下该 section 的记录中是否有 notMatch 状态 | |||
| val hasNotMatch = allStockTakeRecords.any { | |||
| !it.deleted && | |||
| it.stockTake?.id == latestStockTake.id && | |||
| it.stockTakeSection == stockTakeSection && | |||
| it.status == "notMatch" | |||
| } | |||
| hasNotMatch | |||
| } else { | |||
| false | |||
| } | |||
| // 9. 计算 TotalItemNumber:获取该 section 下所有 InventoryLotLine,按 item 分组,计算不同的 item 数量 | |||
| val totalItemNumber = inventoryLotLineRepository.countDistinctItemsByWarehouseIds(warehouseIds).toInt() | |||
| // 9. 使用 stockTakeSection 作为 stockTakeSession | |||
| result.add( | |||
| AllPickedStockTakeListReponse( | |||
| id = idCounter++, | |||
| stockTakeSession = stockTakeSection, | |||
| lastStockTakeDate = lastStockTakeDate, | |||
| lastStockTakeDate = latestStockTake?.actualStart?.toLocalDate(), | |||
| status = status?:"", | |||
| currentStockTakeItemNumber = 0, // 临时设为 0,测试性能 | |||
| totalInventoryLotNumber = 0, // 临时设为 0,测试性能 | |||
| stockTakeId = latestStockTake?.id ?: 0 | |||
| stockTakeId = latestStockTake?.id ?: 0, | |||
| stockTakerName = stockTakerName, | |||
| approverName = approverName, | |||
| TotalItemNumber = totalItemNumber, | |||
| startTime = latestStockTake?.actualStart, | |||
| endTime = latestStockTake?.actualEnd, | |||
| ReStockTakeTrueFalse = reStockTakeTrueFalse | |||
| ) | |||
| ) | |||
| } | |||
| @@ -414,11 +510,10 @@ class StockTakeRecordService( | |||
| } | |||
| existingRecord | |||
| } else { | |||
| // 创建新记录(第一次盘点) | |||
| // 第一次盘点不验证 QTY + Bad QTY 是否匹配,允许不匹配的情况 | |||
| // 如果匹配,status = "1",如果不匹配,status = "2" | |||
| val totalInputQty = request.qty.add(request.badQty) | |||
| val isMatched = totalInputQty.compareTo(availableQty) == 0 | |||
| val isCompled=inventoryLotLine.inQty==inventoryLotLine.outQty | |||
| val varianceQty = availableQty-request.qty-request.badQty | |||
| StockTakeRecord().apply { | |||
| this.itemId = item.id | |||
| @@ -435,7 +530,7 @@ class StockTakeRecordService( | |||
| this.varianceQty = varianceQty | |||
| this.uom = inventoryLotLine.stockUom?.uom?.udfudesc | |||
| this.date = java.time.LocalDate.now() | |||
| this.status = if (isMatched) "pass" else "notMatch" | |||
| this.status = if (isCompled) "completed" else "pass" | |||
| this.remarks = request.remark | |||
| this.itemCode = item.code | |||
| this.itemName = item.name | |||
| @@ -447,7 +542,23 @@ class StockTakeRecordService( | |||
| val savedRecord = stockTakeRecordRepository.save(stockTakeRecord) | |||
| if (request.stockTakeRecordId == null) { | |||
| val existingRecordsCount = stockTakeRecordRepository.findAll() | |||
| .filter { | |||
| !it.deleted && | |||
| it.stockTake?.id == stockTakeId && | |||
| it.id != savedRecord.id // 排除刚创建的这条记录 | |||
| } | |||
| .count() | |||
| if (existingRecordsCount == 0 && stockTake.actualStart == null) { | |||
| stockTake.actualStart = java.time.LocalDateTime.now() | |||
| stockTake.status = StockTakeStatus.STOCKTAKING | |||
| stockTakeRepository.save(stockTake) | |||
| println("Stock take $stockTakeId actualStart updated - first record created") | |||
| } | |||
| } | |||
| val stockTakeSection = savedRecord.stockTakeSection | |||
| if (stockTakeSection != null) { | |||
| @@ -541,6 +652,7 @@ open fun batchSaveStockTakeRecords( | |||
| val totalInputQty = qty.add(badQty) | |||
| val isMatched = totalInputQty.compareTo(availableQty) == 0 | |||
| val varianceQty = availableQty-qty-badQty | |||
| val isCompleted = (ill.inQty ?: BigDecimal.ZERO) == (ill.outQty ?: BigDecimal.ZERO) | |||
| // 创建新记录 | |||
| val stockTakeRecord = StockTakeRecord().apply { | |||
| this.itemId = itemEntity.id | |||
| @@ -557,7 +669,7 @@ open fun batchSaveStockTakeRecords( | |||
| this.varianceQty = varianceQty | |||
| this.uom = ill.stockUom?.uom?.udfudesc | |||
| this.date = java.time.LocalDate.now() | |||
| this.status = if (isMatched) "pass" else "notMatch" | |||
| this.status = if (isCompleted) "completed" else "pass" | |||
| this.remarks = null | |||
| this.itemCode = itemEntity.code | |||
| this.itemName = itemEntity.name | |||
| @@ -577,6 +689,12 @@ open fun batchSaveStockTakeRecords( | |||
| } | |||
| } | |||
| if (successCount > 0) { | |||
| val existingRecordsCount = stockTakeRecordRepository.findAll() | |||
| .filter { | |||
| !it.deleted && | |||
| it.stockTake?.id == request.stockTakeId | |||
| } | |||
| .count() | |||
| checkAndUpdateStockTakeStatus(request.stockTakeId, request.stockTakeSection) | |||
| } | |||
| println("batchSaveStockTakeRecords completed: success=$successCount, errors=$errorCount") | |||
| @@ -586,10 +704,7 @@ open fun batchSaveStockTakeRecords( | |||
| errors = errors | |||
| ) | |||
| } | |||
| // ... existing code ... | |||
| // 检查并更新 stock take 状态 | |||
| // 检查并更新 stock take 状态 | |||
| open fun checkAndUpdateStockTakeStatus(stockTakeId: Long, stockTakeSection: String): Map<String, Any> { | |||
| try { | |||
| val stockTake = stockTakeRepository.findByIdAndDeletedIsFalse(stockTakeId) | |||
| @@ -629,20 +744,29 @@ open fun checkAndUpdateStockTakeStatus(stockTakeId: Long, stockTakeSection: Stri | |||
| } | |||
| } | |||
| // 5. 检查是否所有记录的状态都是 "pass" | |||
| val allRecordsPassed = stockTakeRecords.isNotEmpty() && | |||
| stockTakeRecords.all { it.status == "pass" } | |||
| stockTakeRecords.all { it.status == "pass" || it.status == "completed" } | |||
| val allRecordsCompleted = stockTakeRecords.isNotEmpty() && | |||
| stockTakeRecords.all { it.status == "completed" } | |||
| // 6. 如果所有记录都已创建且都是 "pass",更新 stock take 状态为 "approving" | |||
| if (allLinesHaveRecords && allRecordsPassed) { | |||
| stockTake.status = StockTakeStatus.APPROVING | |||
| stockTake.actualEnd = java.time.LocalDateTime.now() | |||
| stockTakeRepository.save(stockTake) | |||
| println("Stock take $stockTakeId status updated to APPROVING - all records are pass") | |||
| return mapOf( | |||
| "success" to true, | |||
| "message" to "Stock take status updated to APPROVING", | |||
| "updated" to true | |||
| ) | |||
| } else if (allLinesHaveRecords && allRecordsCompleted) { | |||
| stockTake.status = StockTakeStatus.COMPLETED | |||
| stockTake.planEnd = java.time.LocalDateTime.now() | |||
| stockTakeRepository.save(stockTake) | |||
| println("Stock take $stockTakeId status updated to COMPLETED - all records are completed") | |||
| return mapOf("success" to true, "message" to "Stock take status updated to COMPLETED", "updated" to true) | |||
| } else { | |||
| return mapOf( | |||
| "success" to true, | |||
| @@ -713,58 +837,60 @@ open fun saveApproverStockTakeRecord( | |||
| ) ?: throw IllegalArgumentException("Inventory lot not found") | |||
| if (varianceQty !=BigDecimal.ZERO ) { | |||
| val stockTakeLine = StockTakeLine().apply { | |||
| this.stockTake = stockTake | |||
| this.inventoryLotLine = inventoryLotLine | |||
| this.initialQty = stockTakeRecord.bookQty | |||
| this.finalQty = finalQty | |||
| this.status = StockTakeLineStatus.COMPLETED | |||
| this.completeDate = java.time.LocalDateTime.now() | |||
| val stockTakeLine = StockTakeLine().apply { | |||
| this.stockTake = stockTake | |||
| this.inventoryLotLine = inventoryLotLine | |||
| this.initialQty = stockTakeRecord.bookQty | |||
| this.finalQty = finalQty | |||
| this.status = StockTakeLineStatus.COMPLETED | |||
| this.completeDate = java.time.LocalDateTime.now() | |||
| } | |||
| stockTakeLineRepository.save(stockTakeLine) | |||
| if (varianceQty < BigDecimal.ZERO ) { | |||
| val stockOut=StockOut().apply { | |||
| this.type="stockTake" | |||
| this.status="completed" | |||
| this.handler=request.approverId | |||
| } | |||
| stockOutRepository.save(stockOut) | |||
| } | |||
| stockTakeLineRepository.save(stockTakeLine) | |||
| val stockOutLine = StockOutLine().apply { | |||
| this.item=inventoryLot.item | |||
| this.qty=(-varianceQty)?.toDouble() | |||
| this.stockOut = stockOut | |||
| this.inventoryLotLine = inventoryLotLine | |||
| this.status = "completed" | |||
| if (varianceQty < BigDecimal.ZERO ) { | |||
| val stockOut=StockOut().apply { | |||
| this.type="stockTake" | |||
| this.status="completed" | |||
| this.handler=request.approverId | |||
| } | |||
| stockOutRepository.save(stockOut) | |||
| } | |||
| stockOutLineRepository.save(stockOutLine) | |||
| val stockOutLine = StockOutLine().apply { | |||
| this.item=inventoryLot.item | |||
| this.qty=(-varianceQty)?.toDouble() | |||
| this.stockOut = stockOut | |||
| this.inventoryLotLine = inventoryLotLine | |||
| this.status = "completed" | |||
| this.type = "ADJ" | |||
| } | |||
| stockOutLineRepository.save(stockOutLine) | |||
| } | |||
| if (varianceQty > BigDecimal.ZERO ) { | |||
| val stockIn=StockIn().apply { | |||
| this.code=stockTake.code | |||
| this.status="completed" | |||
| this.stockTake=stockTake | |||
| } | |||
| stockInRepository.save(stockIn) | |||
| val stockInLine = StockInLine().apply { | |||
| this.stockTakeLine=stockTakeLine | |||
| this.item=inventoryLot.item | |||
| this.itemNo=stockTakeRecord.itemCode | |||
| this.stockIn = stockIn | |||
| this.demandQty=finalQty | |||
| this.acceptedQty=finalQty | |||
| this.expiryDate=inventoryLot.expiryDate | |||
| this.inventoryLot=inventoryLot | |||
| this.inventoryLotLine=inventoryLotLine | |||
| this.lotNo=inventoryLot.lotNo | |||
| this.status = "completed" | |||
| val stockIn=StockIn().apply { | |||
| this.code=stockTake.code | |||
| this.status="completed" | |||
| this.stockTake=stockTake | |||
| } | |||
| stockInRepository.save(stockIn) | |||
| val stockInLine = StockInLine().apply { | |||
| this.stockTakeLine=stockTakeLine | |||
| this.item=inventoryLot.item | |||
| this.itemNo=stockTakeRecord.itemCode | |||
| this.stockIn = stockIn | |||
| this.demandQty=finalQty | |||
| this.acceptedQty=finalQty | |||
| this.expiryDate=inventoryLot.expiryDate | |||
| this.inventoryLot=inventoryLot | |||
| this.inventoryLotLine=inventoryLotLine | |||
| this.lotNo=inventoryLot.lotNo | |||
| this.status = "completed" | |||
| this.type = "ADJ" | |||
| } | |||
| stockInLineRepository.save(stockInLine) | |||
| } | |||
| stockInLineRepository.save(stockInLine) | |||
| } | |||
| // val currentInQty = inventoryLotLine.inQty ?: BigDecimal.ZERO | |||
| // val newInQty = currentInQty.add(variance) | |||
| @@ -782,7 +908,10 @@ open fun saveApproverStockTakeRecord( | |||
| ) | |||
| inventoryLotLineService.saveInventoryLotLine(updateRequest) | |||
| } | |||
| val stockTakeSection = savedRecord.stockTakeSection | |||
| if (stockTakeSection != null) { | |||
| checkAndUpdateStockTakeStatus(stockTakeId, stockTakeSection) | |||
| } | |||
| return savedRecord | |||
| } | |||
| @@ -797,15 +926,15 @@ open fun batchSaveApproverStockTakeRecords( | |||
| ?: throw IllegalArgumentException("Stock take not found: ${request.stockTakeId}") | |||
| // 2. Get all stock take records for this section where: | |||
| // - secondStockTakeQty is null/0 (no second input) | |||
| // - pickerFirstStockTakeQty is not 0 | |||
| // - approverStockTakeQty is null (not yet approved) | |||
| val stockTakeRecords = stockTakeRecordRepository.findAll() | |||
| .filter { | |||
| !it.deleted && | |||
| it.stockTake?.id == request.stockTakeId && | |||
| it.stockTakeSection == request.stockTakeSection && | |||
| (it.pickerSecondStockTakeQty == null || it.pickerSecondStockTakeQty == BigDecimal.ZERO) && | |||
| it.pickerFirstStockTakeQty != null && it.pickerFirstStockTakeQty!! > BigDecimal.ZERO | |||
| it.pickerFirstStockTakeQty != null && it.pickerFirstStockTakeQty!! > BigDecimal.ZERO && | |||
| it.approverStockTakeQty == null // 只处理未审批的记录 | |||
| } | |||
| println("Found ${stockTakeRecords.size} records to process") | |||
| @@ -817,61 +946,47 @@ open fun batchSaveApproverStockTakeRecords( | |||
| // 3. Process each record | |||
| var successCount = 0 | |||
| var errorCount = 0 | |||
| var skippedCount = 0 | |||
| val errors = mutableListOf<String>() | |||
| stockTakeRecords.forEach { record -> | |||
| try { | |||
| // Use first qty/badQty as approver selection | |||
| val qty = record.pickerFirstStockTakeQty ?: BigDecimal.ZERO | |||
| val badQty = record.pickerFirstBadQty ?: BigDecimal.ZERO | |||
| // Update approver fields | |||
| record.apply { | |||
| this.approverId = request.approverId | |||
| this.approverName = user?.name | |||
| this.approverStockTakeQty = qty | |||
| this.approverBadQty = badQty | |||
| val qty: BigDecimal | |||
| val badQty: BigDecimal | |||
| if (record.pickerSecondStockTakeQty != null && record.pickerSecondStockTakeQty!! > BigDecimal.ZERO) { | |||
| qty = record.pickerSecondStockTakeQty!! | |||
| badQty = record.pickerSecondBadQty ?: BigDecimal.ZERO | |||
| } else { | |||
| qty = record.pickerFirstStockTakeQty ?: BigDecimal.ZERO | |||
| badQty = record.pickerFirstBadQty ?: BigDecimal.ZERO | |||
| } | |||
| stockTakeRecordRepository.save(record) | |||
| // Create stock_take_line and update inventoryLotLine | |||
| val inventoryLotLine = record.inventoryLotId?.let { | |||
| inventoryLotLineRepository.findByIdAndDeletedIsFalse(it) | |||
| } ?: throw IllegalArgumentException("Inventory lot line not found") | |||
| val bookQty = record.bookQty ?: BigDecimal.ZERO | |||
| val varianceQty = qty.subtract(bookQty) | |||
| // Create stock take line | |||
| val stockTakeLine = StockTakeLine().apply { | |||
| this.stockTake = stockTake | |||
| this.inventoryLotLine = inventoryLotLine | |||
| this.initialQty = record.bookQty | |||
| this.finalQty = qty.add(badQty) | |||
| this.status = StockTakeLineStatus.COMPLETED | |||
| this.completeDate = java.time.LocalDateTime.now() | |||
| this.remarks = "Batch approved by approver" | |||
| if (varianceQty.compareTo(BigDecimal.ZERO) != 0) { | |||
| skippedCount++ | |||
| println("Skipping record ${record.id}: variance = $varianceQty (qty=$qty, bookQty=$bookQty)") | |||
| return@forEach | |||
| } | |||
| stockTakeLineRepository.save(stockTakeLine) | |||
| // Update inventoryLotLine | |||
| val variance = qty.add(badQty).subtract(record.bookQty ?: BigDecimal.ZERO) | |||
| if (variance.compareTo(BigDecimal.ZERO) != 0) { | |||
| val currentInQty = inventoryLotLine.inQty ?: BigDecimal.ZERO | |||
| val newInQty = currentInQty.add(variance) | |||
| val updateRequest = SaveInventoryLotLineRequest( | |||
| id = inventoryLotLine.id, | |||
| inventoryLotId = inventoryLotLine.inventoryLot?.id, | |||
| warehouseId = inventoryLotLine.warehouse?.id, | |||
| stockUomId = inventoryLotLine.stockUom?.id, | |||
| inQty = newInQty, | |||
| outQty = inventoryLotLine.outQty, | |||
| holdQty = inventoryLotLine.holdQty, | |||
| status = inventoryLotLine.status?.value, | |||
| remarks = inventoryLotLine.remarks | |||
| ) | |||
| inventoryLotLineService.saveInventoryLotLine(updateRequest) | |||
| // variance = 0,批量保存并设置为 completed | |||
| record.apply { | |||
| this.approverId = request.approverId | |||
| this.approverName = user?.name | |||
| this.approverStockTakeQty = qty | |||
| this.approverBadQty = badQty | |||
| this.varianceQty = varianceQty // 应该是 0 | |||
| this.status = "completed" | |||
| } | |||
| stockTakeRecordRepository.save(record) | |||
| successCount++ | |||
| } catch (e: Exception) { | |||
| errorCount++ | |||
| @@ -881,14 +996,19 @@ open fun batchSaveApproverStockTakeRecords( | |||
| } | |||
| } | |||
| println("batchSaveApproverStockTakeRecords completed: success=$successCount, errors=$errorCount") | |||
| // 只在最后调用一次 checkAndUpdateStockTakeStatus | |||
| if (successCount > 0) { | |||
| checkAndUpdateStockTakeStatus(request.stockTakeId, request.stockTakeSection) | |||
| } | |||
| println("batchSaveApproverStockTakeRecords completed: success=$successCount, skipped=$skippedCount, errors=$errorCount") | |||
| return BatchSaveApproverStockTakeRecordResponse( | |||
| successCount = successCount, | |||
| errorCount = errorCount, | |||
| errors = errors | |||
| ) | |||
| } | |||
| // 在文件末尾,batchSaveApproverStockTakeRecords 方法之后添加 | |||
| open fun updateStockTakeRecordStatusToNotMatch(stockTakeRecordId: Long): StockTakeRecord { | |||
| println("updateStockTakeRecordStatusToNotMatch called with stockTakeRecordId: $stockTakeRecordId") | |||
| @@ -1,16 +1,27 @@ | |||
| // StockTakeRecordReponse.kt | |||
| package com.ffii.fpsms.modules.stock.web.model | |||
| import com.fasterxml.jackson.annotation.JsonFormat | |||
| import java.time.LocalDate | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDateTime | |||
| data class AllPickedStockTakeListReponse( | |||
| val id: Long, | |||
| val stockTakeSession: String, | |||
| @JsonFormat(pattern = "yyyy-MM-dd") | |||
| val lastStockTakeDate: LocalDate?, | |||
| val status: String, | |||
| val currentStockTakeItemNumber: Int, | |||
| val totalInventoryLotNumber: Int, | |||
| @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") | |||
| val startTime: LocalDateTime?, | |||
| @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") | |||
| val endTime: LocalDateTime?, | |||
| val stockTakeId: Long, | |||
| val stockTakerName: String?, | |||
| val approverName: String?, | |||
| val TotalItemNumber: Int, | |||
| val ReStockTakeTrueFalse: Boolean, | |||
| ) | |||
| data class InventoryLotDetailResponse( | |||
| val id: Long, | |||
| @@ -0,0 +1,8 @@ | |||
| --liquibase formatted sql | |||
| --changeset author:add_time_fields_to_productprocessline | |||
| ALTER TABLE `stock_in_line` | |||
| ADD COLUMN `type` VARCHAR(255) NULL AFTER `remarks`; | |||
| ALTER TABLE `stock_out_line` | |||
| ADD COLUMN `type` VARCHAR(255) NULL AFTER `pickerId`; | |||