Browse Source

update

master
CANCERYS\kw093 1 month ago
parent
commit
446fcca384
32 changed files with 652 additions and 634 deletions
  1. +1
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt
  2. +3
    -3
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderLineRecord.kt
  3. +1
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecord.kt
  4. +2
    -0
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecordRepository.kt
  5. +25
    -25
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt
  6. +9
    -9
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderAssignmentService.kt
  7. +10
    -10
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderCompletionService.kt
  8. +61
    -50
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt
  9. +16
    -16
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt
  10. +1
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt
  11. +1
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoPickOrderController.kt
  12. +66
    -66
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  13. +4
    -4
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt
  14. +5
    -5
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt
  15. +2
    -2
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanIssueRequest.kt
  16. +2
    -2
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanSubmitRequest.kt
  17. +1
    -1
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt
  18. +3
    -3
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt
  19. +50
    -50
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt
  20. +198
    -198
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  21. +1
    -1
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickExecutionIssueController.kt
  22. +1
    -1
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcess.kt
  23. +3
    -3
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt
  24. +2
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLineRepository.kt
  25. +2
    -2
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssue.kt
  26. +58
    -55
      src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt
  27. +34
    -34
      src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt
  28. +8
    -8
      src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt
  29. +2
    -2
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt
  30. +7
    -7
      src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt
  31. +72
    -72
      src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt
  32. +1
    -1
      src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt

+ 1
- 1
src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt View File

@@ -46,7 +46,7 @@ class DoPickOrder {
@Column(name = "ticket_release_time")
var ticketReleaseTime: LocalDateTime? = null
// 新增字段声明
// 新增字段声明
@Column(name = "ticketCompleteDateTime")
var ticketCompleteDateTime: LocalDateTime? = null


+ 3
- 3
src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderLineRecord.kt View File

@@ -23,13 +23,13 @@ open class DoPickOrderLineRecord: BaseEntity<Long>() {
@Column(name = "do_pick_order_id", length = 100)
open var doPickOrderId: Long? = null

@Column(name = "pick_order_id") // 正确:普通列
@Column(name = "pick_order_id") // 正确:普通列
open var pickOrderId: Long? = null
@Column(name = "do_order_id") // 正确:普通列
@Column(name = "do_order_id") // 正确:普通列
open var doOrderId: Long? = null
@Column(name = "pick_order_code") // 正确:普通列
@Column(name = "pick_order_code") // 正确:普通列
open var pickOrderCode: String? = null

@Column(name = "delivery_order_code")


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecord.kt View File

@@ -54,7 +54,7 @@ class DoPickOrderRecord {
@Column(name = "handled_by")
var handledBy: Long? = null
// 新增字段声明
// 新增字段声明
@Column(name = "ticketCompleteDateTime")
var ticketCompleteDateTime: LocalDateTime? = null


+ 2
- 0
src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecordRepository.kt View File

@@ -10,9 +10,11 @@ import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository
import java.io.Serializable
import java.time.LocalDateTime
import java.time.LocalDate

@Repository
interface DoPickOrderRecordRepository : JpaRepository<DoPickOrderRecord, Long> {
fun findByPickOrderId(pickOrderId: Long): List<DoPickOrderRecord>
fun findByTicketNoStartingWith(ticketPrefix: String): List<DoPickOrderRecord>
fun findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn(storeId: String, requiredDeliveryDate: LocalDate, ticketStatus: List<DoPickOrderStatus>): List<DoPickOrderRecord>
}

+ 25
- 25
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt View File

@@ -71,11 +71,11 @@ import com.ffii.fpsms.modules.stock.service.InventoryLotService
import net.sf.jasperreports.engine.JasperPrintManager
import net.sf.jasperreports.engine.JRPrintPage
import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService // 添加这行
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService // 添加这行
import com.ffii.fpsms.modules.deliveryOrder.web.models.*
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // 添加
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository // 添加
import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // 添加
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // 添加
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository // 添加
import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // 添加
import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus
@Service
open class DeliveryOrderService(
@@ -310,7 +310,7 @@ open class DeliveryOrderService(
return null
}

// 新增方法2:获取 warehouse 的 code 字段(用于显示路由)
// 新增方法2:获取 warehouse 的 code 字段(用于显示路由)
open fun getWarehouseCodeByItemId(itemId: Long): String? {
val inventoryLots = inventoryLotService.findByItemId(itemId)
if (inventoryLots.isNotEmpty()) {
@@ -451,7 +451,7 @@ open class DeliveryOrderService(
pickOrderRepository.saveAndFlush(pickOrderEntity)
println("�� DEBUG: Assigned consoCode $consoCode to pick order ${createdPickOrder.id}")

// Debug: Check pick order lines
// Debug: Check pick order lines
println("�� DEBUG: Pick order has ${pickOrderEntity.pickOrderLines?.size ?: 0} pick order lines")
pickOrderEntity.pickOrderLines?.forEach { line ->
println("🔍 DEBUG: Pick order line - Item ID: ${line.item?.id}, Qty: ${line.qty}")
@@ -470,7 +470,7 @@ open class DeliveryOrderService(
println("⚠️ WARNING: $insufficientCount items have insufficient stock (issues auto-created)")
}

// Hold inventory quantities
// Hold inventory quantities
val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn(
saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id }
)
@@ -486,7 +486,7 @@ open class DeliveryOrderService(
}
inventoryLotLineRepository.saveAll(inventoryLotLines)

// Create stock out record and pre-create stock out lines
// Create stock out record and pre-create stock out lines
val stockOut = StockOut().apply {
this.type = "do"
this.consoPickOrderCode = consoCode
@@ -495,7 +495,7 @@ open class DeliveryOrderService(
}
val savedStockOut = stockOutRepository.saveAndFlush(stockOut)

// Pre-create stock out lines for suggested lots
// Pre-create stock out lines for suggested lots
saveSuggestedPickLots.forEach { lot ->
val polId = lot.pickOrderLine?.id
val illId = lot.suggestedLotLine?.id
@@ -522,10 +522,10 @@ open class DeliveryOrderService(
}
}

// CREATE do_pick_order_record entries
// CREATE do_pick_order_record entries
// 第 471-555 行附近 - 修复创建逻辑

// CREATE do_pick_order_record entries
// CREATE do_pick_order_record entries
val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now()
val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"))

@@ -535,9 +535,9 @@ open class DeliveryOrderService(
val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId)
println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId")

// 移除提前返回,总是分析 items 分布
// 移除提前返回,总是分析 items 分布
// 分析 DO order lines 中的 items 分布
// 分析 items 来确定 storeId
// 分析 items 来确定 storeId
val itemIds = deliveryOrder.deliveryOrderLines.mapNotNull { it.item?.id }.distinct()
val inventoryQuery = """
SELECT
@@ -560,7 +560,7 @@ open class DeliveryOrderService(
println("🔍 DEBUG: Floor item count distribution: $floorItemCount")
println("🔍 DEBUG: Total items: ${itemIds.size}, Items on 4F: ${floorItemCount["4F"] ?: 0}")

// 新逻辑:只有所有 items 都在 4F,才算 4F,否则算 2F
// 新逻辑:只有所有 items 都在 4F,才算 4F,否则算 2F
val preferredFloor = if ((floorItemCount["4F"] ?: 0) == itemIds.size && (floorItemCount["2F"] ?: 0) == 0) {
"4F" // 所有 items 都在 4F
} else {
@@ -569,7 +569,7 @@ open class DeliveryOrderService(

println("🔍 DEBUG: Preferred floor: $preferredFloor (All items on 4F: ${preferredFloor == "4F"})")

// 查找 truck
// 查找 truck
val truck = deliveryOrder.shop?.id?.let { shopId ->
println("🔍 DEBUG: Looking for truck with shop ID: $shopId")
val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId)
@@ -593,7 +593,7 @@ open class DeliveryOrderService(
selectedTruck
}

// 检查 truck 和 preferredFloor 是否匹配
// 检查 truck 和 preferredFloor 是否匹配
val truckStoreId = truck?.storeId
val expectedStoreId = when (preferredFloor) {
"2F" -> 2
@@ -608,9 +608,9 @@ open class DeliveryOrderService(
throw IllegalStateException(errorMsg) // 或返回错误响应
}

println(" DEBUG: Truck matches preferred floor - Truck Store: $truckStoreId, Preferred: $preferredFloor")
println(" DEBUG: Truck matches preferred floor - Truck Store: $truckStoreId, Preferred: $preferredFloor")

// storeId 基于 items 的 preferredFloor
// storeId 基于 items 的 preferredFloor
val storeId = "$preferredFloor/F"
val loadingSequence = truck.loadingSequence ?: 999

@@ -922,12 +922,12 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo
val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.id)
?: throw NoSuchElementException("Delivery Order not found")

// 检查状态,跳过已完成或已发布的DO
// 检查状态,跳过已完成或已发布的DO
if (deliveryOrder.status == DeliveryOrderStatus.COMPLETED || deliveryOrder.status == DeliveryOrderStatus.RECEIVING) {
throw IllegalStateException("Delivery Order ${deliveryOrder.id} is already ${deliveryOrder.status?.value}, skipping release")
}

// 更新状态为released (使用RECEIVING表示已发布)
// 更新状态为released (使用RECEIVING表示已发布)
deliveryOrder.apply {
status = DeliveryOrderStatus.RECEIVING // 使用RECEIVING表示已发布状态
}
@@ -1004,7 +1004,7 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo
this.pickOrderLine = pickOrderLine
this.inventoryLotLine = inventoryLotLine // 可能为 null
this.item = pickOrderLine.item
// 修复:根据是否有 inventoryLotLine 设置状态
// 修复:根据是否有 inventoryLotLine 设置状态
this.status = if (inventoryLotLine == null) {
StockOutLineStatus.PARTIALLY_COMPLETE.status // 没有库存批次时使用 PARTIALLY_COMPLETE
} else {
@@ -1046,7 +1046,7 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo
"2F"
}

// 查找匹配 preferred floor 的 truck
// 查找匹配 preferred floor 的 truck
val truck = deliveryOrder.shop?.id?.let { shopId ->
val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId)
val preferredStoreId = when (preferredFloor) {
@@ -1065,14 +1065,14 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo
}
}

// 如果没有匹配的 truck,抛出异常跳过
// 如果没有匹配的 truck,抛出异常跳过
if (truck == null) {
val errorMsg = "No matching truck found for preferredFloor ($preferredFloor). Skipping DO ${deliveryOrder.id}."
println("⚠️ $errorMsg")
throw IllegalStateException(errorMsg)
}

println(" DEBUG: Matched truck - ID=${truck.id}, Store=${truck.storeId}, Floor=$preferredFloor")
println(" DEBUG: Matched truck - ID=${truck.id}, Store=${truck.storeId}, Floor=$preferredFloor")

return ReleaseDoResult(
deliveryOrderId = deliveryOrder.id!!,
@@ -1087,7 +1087,7 @@ open fun releaseDeliveryOrderWithoutTicket(request: ReleaseDoRequest): ReleaseDo
truckId = truck.id,
truckDepartureTime = truck.departureTime,
truckLanceCode = truck.truckLanceCode,
loadingSequence = truck.loadingSequence // 直接使用 truck 的值
loadingSequence = truck.loadingSequence // 直接使用 truck 的值
)
}



+ 9
- 9
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderAssignmentService.kt View File

@@ -20,7 +20,7 @@ class DoPickOrderAssignmentService(
private val doPickOrderRecordRepository: DoPickOrderRecordRepository,
private val pickOrderRepository: PickOrderRepository,
private val userRepository: UserRepository,
private val jdbcDao: JdbcDao // 添加 JdbcDao
private val jdbcDao: JdbcDao // 添加 JdbcDao
) {
fun assignByLane(request: AssignByLaneRequest): MessageResponse {
@@ -30,7 +30,7 @@ class DoPickOrderAssignmentService(
message = "User not found", errorPosition = null, entity = null
)
// 转换 storeId 格式
// 转换 storeId 格式
val actualStoreId = when (request.storeId) {
"2/F" -> "2/F"
"4/F" -> "4/F"
@@ -38,11 +38,11 @@ class DoPickOrderAssignmentService(
}
println("🔍 DEBUG: assignByLane - Converting storeId from '${request.storeId}' to '$actualStoreId'")
// 获取日期(如果提供)
// 获取日期(如果提供)
val requiredDate = request.requiredDate
println("🔍 DEBUG: assignByLane - Requested date: $requiredDate")
// 根据是否有日期参数选择不同的查询方法
// 根据是否有日期参数选择不同的查询方法
val allCandidates = if (requiredDate != null) {
println("🔍 DEBUG: Filtering by date: $requiredDate")
doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn(
@@ -62,7 +62,7 @@ class DoPickOrderAssignmentService(
println("🔍 DEBUG: Found ${allCandidates.size} candidate do_pick_orders for lane ${request.truckLanceCode}")
// 过滤掉所有 pick orders 都是 issue 的记录
// 过滤掉所有 pick orders 都是 issue 的记录
val filteredCandidates = allCandidates.filter { doPickOrder ->
val hasNonIssueLines = checkDoPickOrderHasNonIssueLines(doPickOrder.id!!)
if (!hasNonIssueLines) {
@@ -83,14 +83,14 @@ class DoPickOrderAssignmentService(
val firstOrder = filteredCandidates.first()
// 更新 do_pick_order
// 更新 do_pick_order
firstOrder.handledBy = request.userId
firstOrder.handlerName = user.name
firstOrder.ticketStatus = DoPickOrderStatus.released
firstOrder.ticketReleaseTime = LocalDateTime.now()
doPickOrderRepository.save(firstOrder)
// 获取这个 do_pick_order 下的所有 pick orders 并分配给用户
// 获取这个 do_pick_order 下的所有 pick orders 并分配给用户
val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(firstOrder.id!!)
println("🔍 DEBUG: Found ${doPickOrderLines.size} pick orders in do_pick_order ${firstOrder.id}")
@@ -108,7 +108,7 @@ class DoPickOrderAssignmentService(
}
}
// 同步更新 do_pick_order_record(如果有的话)
// 同步更新 do_pick_order_record(如果有的话)
doPickOrderLines.forEach { line ->
if (line.pickOrderId != null) {
val records = doPickOrderRecordRepository.findByPickOrderId(line.pickOrderId!!)
@@ -141,7 +141,7 @@ class DoPickOrderAssignmentService(
)
}
// 添加过滤方法(和 DoPickOrderQueryService 中相同的逻辑)
// 添加过滤方法(和 DoPickOrderQueryService 中相同的逻辑)
private fun checkDoPickOrderHasNonIssueLines(doPickOrderId: Long): Boolean {
return try {
val totalLinesSql = """


+ 10
- 10
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderCompletionService.kt View File

@@ -25,14 +25,14 @@ open class DoPickOrderCompletionService(
}
val savedDoPickOrders = doPickOrderRepository.saveAll(doPickOrders)
// 同步更新相关的delivery_order状态
// 同步更新相关的delivery_order状态
savedDoPickOrders.forEach { doPickOrder ->
if (doPickOrder.doOrderId != null) {
val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doPickOrder.doOrderId!!)
if (deliveryOrder != null && deliveryOrder.status != DeliveryOrderStatus.COMPLETED) {
deliveryOrder.status = DeliveryOrderStatus.COMPLETED
deliveryOrderRepository.save(deliveryOrder)
println(" Updated delivery order ${doPickOrder.doOrderId} status to completed")
println(" Updated delivery order ${doPickOrder.doOrderId} status to completed")
}
}
}
@@ -46,7 +46,7 @@ open class DoPickOrderCompletionService(
var deletedCount = 0
doPickOrders.forEach { doPickOrder ->
// 第一步:复制 do_pick_order 到 do_pick_order_record
// 第一步:复制 do_pick_order 到 do_pick_order_record
val doPickOrderRecord = DoPickOrderRecord(
storeId = doPickOrder.storeId ?: "",
@@ -70,9 +70,9 @@ open class DoPickOrderCompletionService(
requiredDeliveryDate = doPickOrder.requiredDeliveryDate
)
val savedRecord = doPickOrderRecordRepository.save(doPickOrderRecord)
println(" Copied do_pick_order ${doPickOrder.id} to do_pick_order_record")
println(" Copied do_pick_order ${doPickOrder.id} to do_pick_order_record")
// 第二步:复制 do_pick_order_line 到 do_pick_order_line_record
// 第二步:复制 do_pick_order_line 到 do_pick_order_line_record
val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrder.id!!)
doPickOrderLines.forEach { line ->
val doPickOrderLineRecord = DoPickOrderLineRecord()
@@ -83,19 +83,19 @@ open class DoPickOrderCompletionService(
doPickOrderLineRecord.deliveryOrderCode = line.deliveryOrderCode
doPickOrderLineRecord.status = line.status
doPickOrderLineRecordRepository.save(doPickOrderLineRecord)
println(" Copied do_pick_order_line ${line.id} to do_pick_order_line_record")
println(" Copied do_pick_order_line ${line.id} to do_pick_order_line_record")
}
// 第三步:删除原始的 do_pick_order_line 记录
// 第三步:删除原始的 do_pick_order_line 记录
if (doPickOrderLines.isNotEmpty()) {
doPickOrderLineRepository.deleteAll(doPickOrderLines)
println(" Deleted ${doPickOrderLines.size} do_pick_order_line records")
println(" Deleted ${doPickOrderLines.size} do_pick_order_line records")
}
// 第四步:删除原始的 do_pick_order 记录
// 第四步:删除原始的 do_pick_order 记录
doPickOrderRepository.delete(doPickOrder)
deletedCount++
println(" Deleted do_pick_order ${doPickOrder.id}")
println(" Deleted do_pick_order ${doPickOrder.id}")
}
return deletedCount


+ 61
- 50
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt View File

@@ -33,7 +33,7 @@ import java.time.LocalDateTime
import com.ffii.fpsms.modules.deliveryOrder.web.models.AssignByStoreRequest
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecord
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository
import com.ffii.fpsms.modules.deliveryOrder.web.models.* // 导入
import com.ffii.fpsms.modules.deliveryOrder.web.models.* // 导入
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus
import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository
@@ -52,7 +52,7 @@ open class DoPickOrderService(
private val doPickOrderRecordRepository: DoPickOrderRecordRepository,
private val userRepository: UserRepository,
private val pickOrderRepository: PickOrderRepository,
private val jdbcDao: JdbcDao, // 添加这行
private val jdbcDao: JdbcDao, // 添加这行
private val truckRepository: TruckRepository,
private val doPickOrderLineRepository: DoPickOrderLineRepository,
@Lazy private val deliveryOrderRepository: DeliveryOrderRepository,
@@ -73,7 +73,7 @@ open class DoPickOrderService(
} else {
datePrefix
}
// 修改搜索模式为新格式
// 修改搜索模式为新格式
val searchPattern = "TI-${shortDatePrefix}-${sanitizedStoreId}-" // T-20250915-4F-
val todayTickets = doPickOrderRepository.findByTicketNoStartingWith(searchPattern)
println("🔍 DEBUG: Found ${todayTickets.size} existing tickets with prefix $searchPattern")
@@ -81,7 +81,7 @@ open class DoPickOrderService(
println("🔍 DEBUG: Existing ticket: ${ticket.ticketNo}, Status: ${ticket.ticketStatus}")
}
val nextNumber = (todayTickets.size + 1).toString().padStart(3, '0')
// 修改生成格式
// 修改生成格式
val ticketNumber = "TI-${datePrefix}-${sanitizedStoreId}-${nextNumber}" // T-20250915-4F-001
println("🔍 DEBUG: Generated ticket number: $ticketNumber")
return ticketNumber
@@ -129,10 +129,10 @@ open class DoPickOrderService(
)
}

// Updated method to set ticketReleaseTime when assigning order to user
// Updated method to set ticketReleaseTime when assigning order to user
open fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrder> {
val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId)
val user = userRepository.findById(userId).orElse(null) // 改用 orElse(null)
val user = userRepository.findById(userId).orElse(null) // 改用 orElse(null)
val handlerName = user?.name ?: "Unknown"
doPickOrders.forEach {
it.handledBy = userId
@@ -151,14 +151,14 @@ open class DoPickOrderService(
}
val savedDoPickOrders = doPickOrderRepository.saveAll(doPickOrders)
// 同步更新相关的delivery_order状态
// 同步更新相关的delivery_order状态
savedDoPickOrders.forEach { doPickOrder ->
if (doPickOrder.doOrderId != null) {
val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doPickOrder.doOrderId!!)
if (deliveryOrder != null && deliveryOrder.status != DeliveryOrderStatus.COMPLETED) {
deliveryOrder.status = DeliveryOrderStatus.COMPLETED
deliveryOrderRepository.save(deliveryOrder)
println(" Updated delivery order ${doPickOrder.doOrderId} status to completed")
println(" Updated delivery order ${doPickOrder.doOrderId} status to completed")
}
}
}
@@ -166,15 +166,15 @@ open class DoPickOrderService(
return savedDoPickOrders
}

// New method to remove do_pick_order records when auto-assigning by store
// 修改方法:先复制记录到record表,再删除原记录
// New method to remove do_pick_order records when auto-assigning by store
// 修改方法:先复制记录到record表,再删除原记录
@Transactional
open fun removeDoPickOrdersForPickOrder(pickOrderId: Long): Int {
val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId)
var deletedCount = 0
doPickOrders.forEach { doPickOrder ->
// 第一步:复制 do_pick_order 到 do_pick_order_record
// 第一步:复制 do_pick_order 到 do_pick_order_record
val doPickOrderRecord = DoPickOrderRecord(
recordId = doPickOrder.id,
storeId = doPickOrder.storeId?: "",
@@ -198,34 +198,34 @@ open class DoPickOrderService(
requiredDeliveryDate = doPickOrder.requiredDeliveryDate
)
val savedRecord = doPickOrderRecordRepository.save(doPickOrderRecord)
println(" Copied do_pick_order ${doPickOrder.id} to do_pick_order_record")
println(" Copied do_pick_order ${doPickOrder.id} to do_pick_order_record")
// 第二步:复制 do_pick_order_line 到 do_pick_order_line_record
// 第二步:复制 do_pick_order_line 到 do_pick_order_line_record
// 第二步:复制 do_pick_order_line 到 do_pick_order_line_record
// 第二步:复制 do_pick_order_line 到 do_pick_order_line_record
val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrder.id!!)
doPickOrderLines.forEach { line ->
val doPickOrderLineRecord = DoPickOrderLineRecord()
doPickOrderLineRecord.recordId = line.id // 使用默认构造函数
doPickOrderLineRecord.doPickOrderId = savedRecord.id // 设置属性
doPickOrderLineRecord.recordId = line.id // 使用默认构造函数
doPickOrderLineRecord.doPickOrderId = savedRecord.id // 设置属性
doPickOrderLineRecord.pickOrderId = line.pickOrderId
doPickOrderLineRecord.doOrderId = line.doOrderId
doPickOrderLineRecord.pickOrderCode = line.pickOrderCode
doPickOrderLineRecord.deliveryOrderCode = line.deliveryOrderCode
doPickOrderLineRecord.status = line.status
doPickOrderLineRecordRepository.save(doPickOrderLineRecord)
println(" Copied do_pick_order_line ${line.id} to do_pick_order_line_record")
println(" Copied do_pick_order_line ${line.id} to do_pick_order_line_record")
}
// 第三步:删除原始的 do_pick_order_line 记录
// 第三步:删除原始的 do_pick_order_line 记录
if (doPickOrderLines.isNotEmpty()) {
doPickOrderLineRepository.deleteAll(doPickOrderLines)
println(" Deleted ${doPickOrderLines.size} do_pick_order_line records")
println(" Deleted ${doPickOrderLines.size} do_pick_order_line records")
}
// 第四步:删除原始的 do_pick_order 记录
// 第四步:删除原始的 do_pick_order 记录
doPickOrderRepository.delete(doPickOrder)
deletedCount++
println(" Deleted do_pick_order ${doPickOrder.id}")
println(" Deleted do_pick_order ${doPickOrder.id}")
}
return deletedCount
@@ -235,10 +235,10 @@ open class DoPickOrderService(
return doPickOrderRecordRepository.save(record)
}

// Add method to update DoPickOrderRecord status
// Add method to update DoPickOrderRecord status
open fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrderRecord> {
val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId)
val user = userRepository.findById(userId).orElse(null) // 改用 orElse(null)
val user = userRepository.findById(userId).orElse(null) // 改用 orElse(null)
val handlerName = user?.name ?: "Unknown"
doPickOrderRecords.forEach {
it.handledBy = userId
@@ -249,12 +249,12 @@ open class DoPickOrderService(
return doPickOrderRecordRepository.saveAll(doPickOrderRecords)
}

// Add method to complete DoPickOrderRecord
// Add method to complete DoPickOrderRecord
open fun completeDoPickOrderRecordsForPickOrder(pickOrderId: Long): List<DoPickOrderRecord> {
val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId)
doPickOrderRecords.forEach {
it.ticketStatus = DoPickOrderStatus.completed
it.ticketCompleteDateTime = LocalDateTime.now() // 设置完成时间
it.ticketCompleteDateTime = LocalDateTime.now() // 设置完成时间
}
return doPickOrderRecordRepository.saveAll(doPickOrderRecords)
}
@@ -271,29 +271,32 @@ open class DoPickOrderService(
}
return doPickOrderRepository.saveAll(doPickOrders)
}
open fun getSummaryByStore(storeId: String, requiredDate: LocalDate?): StoreLaneSummary {
fun getSummaryByStore(storeId: String, requiredDate: LocalDate?): StoreLaneSummary {
val targetDate = requiredDate ?: LocalDate.now()
println("🔍 DEBUG: Getting summary for store=$storeId, date=$targetDate")
// ✅ 修复格式转换:保持原始格式
val actualStoreId = when (storeId) {
"2/F" -> "2/F" // ✅ 保持原格式
"4/F" -> "4/F" // ✅ 保持原格式
"2/F" -> "2/F"
"4/F" -> "4/F"
else -> storeId
}
println("🔍 DEBUG: Using storeId: '$actualStoreId'")
// ✅ 直接查询 do_pick_order 表
val allRecords = doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn(
actualStoreId,
targetDate,
listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed)
)

// 添加 finishedRecords 查询
val finishedRecords = doPickOrderRecordRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn(
actualStoreId,
targetDate,
listOf(DoPickOrderStatus.completed)
)
println("🔍 DEBUG: Found ${allRecords.size} records for date $targetDate")
println("🔍 DEBUG: Found ${finishedRecords.size} finished records for date $targetDate")
// ✅ 过滤掉所有 do_pick_order_line 都是 "issue" 状态的 shop
/*
val filteredRecords = allRecords.filter { doPickOrder ->
val hasNonIssueLines = checkDoPickOrderHasNonIssueLines(doPickOrder.id!!)
if (!hasNonIssueLines) {
@@ -301,19 +304,27 @@ open class DoPickOrderService(
}
hasNonIssueLines
}
*/
// println("🔍 DEBUG: After filtering, ${filteredRecords.size} records remain")
// ✅ 使用过滤后的记录
val grouped = allRecords.groupBy { it.truckDepartureTime to it.truckLanceCode }
.mapValues { (_, list) ->
println("🔍 DEBUG: After filtering, ${filteredRecords.size} records remain")
val grouped = filteredRecords.groupBy { it.truckDepartureTime to it.truckLanceCode }
.mapValues { (key, list) ->
val (truckDepartureTime, truckLanceCode) = key
// 计算匹配的 finishedRecords 数量
val matchingFinishedCount = finishedRecords.count { record ->
(record.truckDepartureTime == truckDepartureTime) &&
(record.truckLanceCode == truckLanceCode)
}
println("🔍 DEBUG: Group key - truckDepartureTime: $truckDepartureTime, truckLanceCode: $truckLanceCode")
println("🔍 DEBUG: Found ${list.size} active records in this group")
println("🔍 DEBUG: Found $matchingFinishedCount finished records matching this group")
LaneBtn(
truckLanceCode = list.first().truckLanceCode ?: "",
unassigned = list.count { it.handledBy == null },
total = list.size
total = list.size + matchingFinishedCount // 添加 finishedRecords 计数
)
}
val timeGroups = grouped.entries
.groupBy { it.key.first }
.mapValues { (_, entries) ->
@@ -330,7 +341,7 @@ open class DoPickOrderService(
lanes = lanes
)
}
return StoreLaneSummary(storeId = storeId, rows = timeGroups)
}
private fun checkDoPickOrderHasNonIssueLines(doPickOrderId: Long): Boolean {
@@ -374,7 +385,7 @@ open class DoPickOrderService(
return true // 出错时不过滤
}
}
// 修复:把 assignByLane 移到类里面
// 修复:把 assignByLane 移到类里面
open fun assignByLane(request: AssignByLaneRequest): MessageResponse {
val user = userRepository.findById(request.userId).orElse(null)
?: return MessageResponse(
@@ -382,10 +393,10 @@ open class DoPickOrderService(
message = "User not found", errorPosition = null, entity = null
)
// 转换 storeId 格式:'2/F' -> '2F/F', '4/F' -> '4F/F'
// 转换 storeId 格式:'2/F' -> '2F/F', '4/F' -> '4F/F'
val actualStoreId = when (request.storeId) {
"2/F" -> "2/F" // 保持原格式
"4/F" -> "4/F" // 保持原格式
"2/F" -> "2/F" // 保持原格式
"4/F" -> "4/F" // 保持原格式
else -> request.storeId
}
println("🔍 DEBUG: assignByLane - Converting storeId from '${request.storeId}' to '$actualStoreId'")
@@ -406,14 +417,14 @@ open class DoPickOrderService(
val firstOrder = candidates.first()
// 更新 do_pick_order
// 更新 do_pick_order
firstOrder.handledBy = request.userId
firstOrder.handlerName = user.name
firstOrder.ticketStatus = DoPickOrderStatus.released
firstOrder.ticketReleaseTime = LocalDateTime.now()
doPickOrderRepository.save(firstOrder)
// 关键修改:获取这个 do_pick_order 下的所有 pick orders 并分配给用户
// 关键修改:获取这个 do_pick_order 下的所有 pick orders 并分配给用户
val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(firstOrder.id!!)
println("🔍 DEBUG: Found ${doPickOrderLines.size} pick orders in do_pick_order ${firstOrder.id}")
@@ -431,7 +442,7 @@ open class DoPickOrderService(
}
}
// 同步更新 do_pick_order_record(如果有的话)
// 同步更新 do_pick_order_record(如果有的话)
doPickOrderLines.forEach { line ->
if (line.pickOrderId != null) {
val records = doPickOrderRecordRepository.findByPickOrderId(line.pickOrderId!!)
@@ -588,4 +599,4 @@ open class DoPickOrderService(
open fun getTicketReleaseTable(): List<DoPickOrder>{
return doPickOrderRepository.findAllByDeletedFalseOrderByTicketReleaseTimeDesc()
}
}// 类结束
}// 类结束

+ 16
- 16
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt View File

@@ -183,7 +183,7 @@ class DoReleaseCoordinatorService(
""".trimIndent()
val rowsUpdated = jdbcDao.executeUpdate(updateSql, emptyMap<String, Any>())
println(" Updated $rowsUpdated ticket numbers")
println(" Updated $rowsUpdated ticket numbers")
} catch (e: Exception) {
println("❌ Error updating ticket numbers: ${e.message}")
e.printStackTrace()
@@ -332,16 +332,16 @@ 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 ->
@@ -359,7 +359,7 @@ class DoReleaseCoordinatorService(
}
} catch (e: Exception) {
println("❌ ERROR: ${e.message}")
println("❌ ERROR Stack Trace:") // 添加这行
println("❌ ERROR Stack Trace:") // 添加这行
e.printStackTrace()
return ids
}
@@ -407,7 +407,7 @@ class DoReleaseCoordinatorService(
}
println("❌ DO $id skipped: ${e.message}")

// 调用 PickExecutionIssueService 创建 issue 记录
// 调用 PickExecutionIssueService 创建 issue 记录
try {
val issueCategory = when {
e.message?.contains("Unable to find") == true &&
@@ -430,9 +430,9 @@ class DoReleaseCoordinatorService(
println("⚠️ Failed to create issue for DO $id: ${issueException.message}")
}
}
} // forEach 循环在这里结束
} // forEach 循环在这里结束

// 第二步:按日期、楼层、店铺分组(在 forEach 之后)
// 第二步:按日期、楼层、店铺分组(在 forEach 之后)
val sortedResults = releaseResults.sortedWith(
compareBy(
{ it.estimatedArrivalDate },
@@ -444,7 +444,7 @@ class DoReleaseCoordinatorService(
)
)

// 然后按正确的顺序分组(保持排序后的顺序)
// 然后按正确的顺序分组(保持排序后的顺序)
val grouped = sortedResults.groupBy {
Triple(it.estimatedArrivalDate, it.preferredFloor, it.shopId)
}
@@ -468,7 +468,7 @@ class DoReleaseCoordinatorService(
updateBatchTicketNumbers()
}

println(" Batch completed: ${status.success.get()} success, ${status.failed.size} failed")
println(" Batch completed: ${status.success.get()} success, ${status.failed.size} failed")

} catch (e: Exception) {
println("❌ Batch release exception: ${e.javaClass.simpleName} - ${e.message}")
@@ -511,7 +511,7 @@ class DoReleaseCoordinatorService(
requiredDeliveryDate = first.estimatedArrivalDate
)
// 直接使用 doPickOrderRepository.save() 而不是 doPickOrderService.save()
// 直接使用 doPickOrderRepository.save() 而不是 doPickOrderService.save()
val saved = doPickOrderRepository.save(doPickOrder)
println("🔍 DEBUG: Saved DoPickOrder - ID: ${saved.id}, Ticket: ${saved.ticketNo}")
@@ -524,7 +524,7 @@ class DoReleaseCoordinatorService(
return@forEach // 跳过这个
}
// 先创建 DoPickOrderLine,然后检查库存问题
// 先创建 DoPickOrderLine,然后检查库存问题
val line = DoPickOrderLine().apply {
doPickOrderId = saved.id
pickOrderId = result.pickOrderId
@@ -537,7 +537,7 @@ class DoReleaseCoordinatorService(
println("🔍 DEBUG: Created DoPickOrderLine for pick order ${result.pickOrderId}")
}
// 现在检查整个 DoPickOrder 是否有库存问题
// 现在检查整个 DoPickOrder 是否有库存问题
val hasStockIssues = checkPickOrderHasStockIssues(saved.id!!)
if (hasStockIssues) {
// 更新所有相关的 DoPickOrderLine 状态为 "issue"


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt View File

@@ -180,7 +180,7 @@ class DeliveryOrderController(

@PostMapping("/release")
fun releaseDeliveryOrder(@RequestBody request: ReleaseDoRequest): MessageResponse {
// Simply pass the request directly - userId comes from frontend session
// Simply pass the request directly - userId comes from frontend session
return deliveryOrderService.releaseDeliveryOrder(request)
}



+ 1
- 1
src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoPickOrderController.kt View File

@@ -75,7 +75,7 @@ class DoPickOrderController(
}
@PostMapping("/assign-by-lane")
fun assignByLane(@RequestBody request: AssignByLaneRequest): MessageResponse {
return doPickOrderAssignmentService.assignByLane(request) // 使用新的 Service
return doPickOrderAssignmentService.assignByLane(request) // 使用新的 Service
}
@PostMapping("/batch-release/async")


+ 66
- 66
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt View File

@@ -44,7 +44,7 @@ open class JoPickOrderService(
return joPickOrderRecordRepository.save(record)
}
// Update JoPickOrder status to released
// Update JoPickOrder status to released
open fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<JoPickOrder> {
val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId)
@@ -68,7 +68,7 @@ open class JoPickOrderService(
}
return joPickOrderRepository.saveAll(joPickOrders)
}
// Complete JoPickOrder
// Complete JoPickOrder
open fun completeJoPickOrdersForPickOrder(pickOrderId: Long): List<JoPickOrder> {
val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId)
joPickOrders.forEach {
@@ -78,7 +78,7 @@ open class JoPickOrderService(
return joPickOrderRepository.saveAll(joPickOrders)
}
// Update JoPickOrderRecord status to released
// Update JoPickOrderRecord status to released
open fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List<JoPickOrderRecord> {
val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId)
@@ -103,7 +103,7 @@ open class JoPickOrderService(
return joPickOrderRecordRepository.saveAll(joPickOrderRecords)
}
// Complete JoPickOrderRecord
// Complete JoPickOrderRecord
open fun completeJoPickOrderRecordsForPickOrder(pickOrderId: Long): List<JoPickOrderRecord> {
val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId)
joPickOrderRecords.forEach {
@@ -113,17 +113,17 @@ open class JoPickOrderService(
return joPickOrderRecordRepository.saveAll(joPickOrderRecords)
}
// Find JoPickOrder records by pick order ID
// Find JoPickOrder records by pick order ID
open fun findByPickOrderId(pickOrderId: Long): List<JoPickOrder> {
return joPickOrderRepository.findByPickOrderId(pickOrderId)
}
// Find JoPickOrderRecord records by pick order ID
// Find JoPickOrderRecord records by pick order ID
open fun findRecordsByPickOrderId(pickOrderId: Long): List<JoPickOrderRecord> {
return joPickOrderRecordRepository.findByPickOrderId(pickOrderId)
}
// Create JoPickOrder records for a pick order
// Create JoPickOrder records for a pick order
@Transactional
open fun createJoPickOrdersForPickOrder(pickOrderId: Long, itemIds: List<Long>): List<JoPickOrder> {
// Get pick order and item details
@@ -155,7 +155,7 @@ open class JoPickOrderService(
}
return joPickOrderRepository.saveAll(joPickOrders)
}
// Create JoPickOrderRecord records for a pick order
// Create JoPickOrderRecord records for a pick order
@Transactional
open fun createJoPickOrderRecordsForPickOrder(pickOrderId: Long, itemIds: List<Long>): List<JoPickOrderRecord> {
// Get pick order and item details
@@ -248,7 +248,7 @@ open class JoPickOrderService(
)
}
// 修复:简化 SQL 查询,移除不存在的字段
// 修复:简化 SQL 查询,移除不存在的字段
val pickOrderIdsStr = pickOrderIds.joinToString(",")
val sql = """
@@ -286,7 +286,7 @@ open class JoPickOrderService(
w.name as location,
COALESCE(uc.udfudesc, 'N/A') as stockUnit,
-- Simplified Router Information using warehouse
-- Simplified Router Information using warehouse
w.`order` as routerIndex,
w.code as routerRoute,
w.code as routerArea,
@@ -363,7 +363,7 @@ open class JoPickOrderService(
LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN fpsmsdb.inventory_lot_line ill ON spl.suggestedLotLineId = ill.id
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId -- 直接关联 warehouse
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId -- 直接关联 warehouse
LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ill.id AND sol.deleted = false
LEFT JOIN fpsmsdb.jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId
WHERE po.deleted = false
@@ -376,7 +376,7 @@ open class JoPickOrderService(
AND (spl.pickOrderLineId IS NOT NULL OR sol.pickOrderLineId IS NOT NULL)
ORDER BY
CASE WHEN sol.status = 'rejected' THEN 0 ELSE 1 END,
COALESCE(w.`order`, 999999) ASC, -- 使用 warehouse.order 排序
COALESCE(w.`order`, 999999) ASC, -- 使用 warehouse.order 排序
po.code ASC,
i.code ASC,
il.expiryDate ASC,
@@ -386,7 +386,7 @@ open class JoPickOrderService(
println("🔍 Executing SQL for job order hierarchical structure: $sql")
println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr")
// 修复:直接执行 SQL 查询并构建分层结构
// 修复:直接执行 SQL 查询并构建分层结构
try {
val results = jdbcDao.queryForList(sql, mapOf("userId" to userId))
@@ -410,7 +410,7 @@ open class JoPickOrderService(
"jobOrder" to mapOf(
"id" to firstRow["jobOrderId"],
"code" to firstRow["jobOrderCode"],
"name" to "Job Order ${firstRow["jobOrderCode"]}" // 使用生成的名称
"name" to "Job Order ${firstRow["jobOrderCode"]}" // 使用生成的名称
)
)
}
@@ -474,7 +474,7 @@ open class JoPickOrderService(
}

// Get completed job order pick orders (for second tab)
// Fix the getCompletedJobOrderLotsHierarchical method
// Fix the getCompletedJobOrderLotsHierarchical method
open fun getCompletedJobOrderLotsHierarchical(userId: Long): Map<String, Any?> {
println("=== Debug: getCompletedJobOrderLotsHierarchical ===")
println("today: ${LocalDate.now()}")
@@ -520,7 +520,7 @@ open fun getCompletedJobOrderLotsHierarchical(userId: Long): Map<String, Any?> {
println("🔍 DEBUG: Using latest completed order: ${latestCompletedOrder.code}")
// Use the same SQL query approach as getAllJobOrderLotsWithDetailsHierarchical
// Use the same SQL query approach as getAllJobOrderLotsWithDetailsHierarchical
val pickOrderIds = listOf(latestCompletedOrder.id!!)
val pickOrderIdsStr = pickOrderIds.joinToString(",")
@@ -657,7 +657,7 @@ open fun getCompletedJobOrderLotsHierarchical(userId: Long): Map<String, Any?> {
println("🔍 Executing SQL for completed job order hierarchical structure: $sql")
println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr")
// Execute SQL query and build hierarchical structure
// Execute SQL query and build hierarchical structure
try {
val results = jdbcDao.queryForList(sql, mapOf("userId" to userId))
@@ -799,7 +799,7 @@ open fun getUnassignedJobOrderPickOrders(): List<Map<String, Any?>> {
println("=== getUnassignedJobOrderPickOrders ===")
return try {
// 修复:使用正确的 repository 方法
// 修复:使用正确的 repository 方法
val unassignedPickOrders = pickOrderRepository.findAllByStatusAndAssignToIsNullAndDeletedFalse(
com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED
).filter { pickOrder ->
@@ -840,7 +840,7 @@ open fun getUnassignedJobOrderPickOrders(): List<Map<String, Any?>> {
}
}

// 修复:assignJobOrderPickOrderToUser 方法中的自引用问题
// 修复:assignJobOrderPickOrderToUser 方法中的自引用问题
@Transactional
open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): MessageResponse {
println("=== assignJobOrderPickOrderToUser ===")
@@ -874,7 +874,7 @@ open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): Message
pickOrder.assignTo = userService.find(userId).orElse(null)
pickOrderRepository.save(pickOrder)
// 修复:使用 this 而不是 joPickOrderService
// 修复:使用 this 而不是 joPickOrderService
this.updateHandledByForPickOrder(pickOrderId, userId)
this.updateRecordHandledByForPickOrder(pickOrderId, userId)
MessageResponse(
@@ -898,7 +898,7 @@ open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): Message
)
}
}
// Fix the updateMatchStatus method
// Fix the updateMatchStatus method
open fun updateMatchStatus(pickOrderId: Long, itemId: Long, userId: Long, qty: Int): MessageResponse {
try {
println("=== Debug: updateMatchStatus ===")
@@ -919,23 +919,23 @@ open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): Message
val joPickOrderEntity = joPickOrder.get()
// 设置扫描状态和相关字段
// 设置扫描状态和相关字段
joPickOrderEntity.matchStatus = JoPickOrderStatus.scanned
joPickOrderEntity.matchBy = userId
joPickOrderEntity.matchQty = qty // 使用传递的 qty
joPickOrderEntity.matchQty = qty // 使用传递的 qty
joPickOrderRepository.save(joPickOrderEntity)
// 同时更新 jo_pick_order_record
// 同时更新 jo_pick_order_record
val joPickOrderRecord = joPickOrderRecordRepository.findByPickOrderIdAndItemId(pickOrderId, itemId)
if (joPickOrderRecord.isPresent) {
val recordEntity = joPickOrderRecord.get()
recordEntity.matchStatus = JoPickOrderStatus.scanned
recordEntity.matchBy = userId
recordEntity.matchQty = qty // 使用相同的 qty
recordEntity.matchQty = qty // 使用相同的 qty
joPickOrderRecordRepository.save(recordEntity)
}
println(" Updated match status: pickOrderId=$pickOrderId, itemId=$itemId, matchQty=$qty")
println(" Updated match status: pickOrderId=$pickOrderId, itemId=$itemId, matchQty=$qty")
return MessageResponse(
id = null,
@@ -979,17 +979,17 @@ open fun submitSecondScanQty(request: SecondScanSubmitRequest): MessageResponse
val joPickOrderEntity = joPickOrder.get()
joPickOrderEntity.matchQty = request.qty.toInt()
// Set status to completed when submitting quantity
// Set status to completed when submitting quantity
joPickOrderEntity.matchStatus = JoPickOrderStatus.completed
// 添加:如果 ticketCompleteTime 还没设置,现在设置(通常已经在拣货完成时设置了)
// 添加:如果 ticketCompleteTime 还没设置,现在设置(通常已经在拣货完成时设置了)
if (joPickOrderEntity.ticketCompleteTime == null) {
joPickOrderEntity.ticketCompleteTime = LocalDateTime.now()
}
joPickOrderRepository.save(joPickOrderEntity)
println(" Updated jo_pick_order: status=${joPickOrderEntity.matchStatus}, qty=${joPickOrderEntity.matchQty}, completeTime=${joPickOrderEntity.ticketCompleteTime}")
println(" Updated jo_pick_order: status=${joPickOrderEntity.matchStatus}, qty=${joPickOrderEntity.matchQty}, completeTime=${joPickOrderEntity.ticketCompleteTime}")
return MessageResponse(
id = null,
@@ -1014,7 +1014,7 @@ open fun submitSecondScanQty(request: SecondScanSubmitRequest): MessageResponse
}


// Fix the recordSecondScanIssue method
// Fix the recordSecondScanIssue method
open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse {
try {
println("=== Debug: recordSecondScanIssue ===")
@@ -1033,7 +1033,7 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse
)
}
// Get pick order and pick order line details with item and lot information
// Get pick order and pick order line details with item and lot information
val pickOrderDetails = jdbcDao.queryForList(
"""
SELECT
@@ -1060,7 +1060,7 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse
)
)
// 修复:使用正确的变量名 pickOrderDetails
// 修复:使用正确的变量名 pickOrderDetails
if (pickOrderDetails.isEmpty()) {
return MessageResponse(
id = null,
@@ -1072,7 +1072,7 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse
)
}
// 修复:使用正确的变量名 pickOrderDetails
// 修复:使用正确的变量名 pickOrderDetails
val orderData = pickOrderDetails.first()
val pickOrderCode = orderData["pickOrderCode"] as String?
val pickOrderCreateDate = orderData["pickOrderCreateDate"]?.let {
@@ -1086,7 +1086,7 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse
BigDecimal(it.toString())
}
// 获取 suggested lot 信息(用于 lot_id, lot_no, store_location)
// 获取 suggested lot 信息(用于 lot_id, lot_no, store_location)
val lotResults = jdbcDao.queryForList(
"""
SELECT
@@ -1127,41 +1127,41 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse

joPickOrderRepository.save(joPickOrderEntity)
// 生成 issueNo
// 生成 issueNo
val issueNo = generateIssueNoForJo()
// Create pick execution issue with complete data
// Create pick execution issue with complete data
// Create pick execution issue with complete data
// Create pick execution issue with complete data
val pickExecutionIssue = PickExecutionIssue(
id = null, // 1
pickOrderId = request.pickOrderId, // 2
pickOrderCode = pickOrderCode ?: "PI-${request.pickOrderId}", // 3
pickOrderCreateDate = pickOrderCreateDate, // 4
pickExecutionDate = LocalDate.now(), // 5
pickOrderLineId = pickOrderLineId, // 6
issueNo = issueNo, // 7
issueCategory = IssueCategory.match_issue, // 8 - 因为这是 JO 的 match 问题
itemId = request.itemId, // 9
itemCode = itemCode, // 10
itemDescription = itemName, // 11
lotId = lotId, // 12
lotNo = lotNo, // 13
storeLocation = storeLocation, // 14
requiredQty = requiredQty, // 15
actualPickQty = request.qty.toBigDecimal(), // 16
missQty = request.missQty.toBigDecimal(), // 17
badItemQty = request.badItemQty.toBigDecimal(), // 18
issueRemark = request.reason, // 19
pickerName = pickerName, // 20
handleStatus = HandleStatus.pending, // 21
handleDate = null, // 22
handledBy = null, // 23
created = LocalDateTime.now(), // 24
createdBy = request.createdBy.toString(), // 25
version = 0, // 26
modified = LocalDateTime.now(), // 27
modifiedBy = null, // 28
deleted = false // 29
id = null, // 1
pickOrderId = request.pickOrderId, // 2
pickOrderCode = pickOrderCode ?: "PI-${request.pickOrderId}", // 3
pickOrderCreateDate = pickOrderCreateDate, // 4
pickExecutionDate = LocalDate.now(), // 5
pickOrderLineId = pickOrderLineId, // 6
issueNo = issueNo, // 7
issueCategory = IssueCategory.match_issue, // 8 - 因为这是 JO 的 match 问题
itemId = request.itemId, // 9
itemCode = itemCode, // 10
itemDescription = itemName, // 11
lotId = lotId, // 12
lotNo = lotNo, // 13
storeLocation = storeLocation, // 14
requiredQty = requiredQty, // 15
actualPickQty = request.qty.toBigDecimal(), // 16
missQty = request.missQty.toBigDecimal(), // 17
badItemQty = request.badItemQty.toBigDecimal(), // 18
issueRemark = request.reason, // 19
pickerName = pickerName, // 20
handleStatus = HandleStatus.pending, // 21
handleDate = null, // 22
handledBy = null, // 23
created = LocalDateTime.now(), // 24
createdBy = request.createdBy.toString(), // 25
version = 0, // 26
modified = LocalDateTime.now(), // 27
modifiedBy = null, // 28
deleted = false // 29
)
pickExecutionIssueRepository.save(pickExecutionIssue)


+ 4
- 4
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt View File

@@ -244,7 +244,7 @@ open class JobOrderService(
pickOrderEntity.status = com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED
pickOrderRepository.saveAndFlush(pickOrderEntity)

// 添加 suggested pick lots 创建逻辑
// 添加 suggested pick lots 创建逻辑
val lines = pickOrderLineRepository.findAllByPickOrderId(pickOrderEntity.id!!)
if (lines.isNotEmpty()) {
val suggestions = suggestedPickLotService.suggestionForPickOrderLines(
@@ -253,7 +253,7 @@ open class JobOrderService(

val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList)

// Hold inventory quantities
// Hold inventory quantities
val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn(
saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id }
)
@@ -269,7 +269,7 @@ open class JobOrderService(
}
inventoryLotLineRepository.saveAll(inventoryLotLines)

// Create stock out record and pre-create stock out lines
// Create stock out record and pre-create stock out lines
val stockOut = StockOut().apply {
this.type = "job"
this.consoPickOrderCode = consoCode
@@ -278,7 +278,7 @@ open class JobOrderService(
}
val savedStockOut = stockOutRepository.saveAndFlush(stockOut)

// Pre-create stock out lines for suggested lots
// Pre-create stock out lines for suggested lots
saveSuggestedPickLots.forEach { lot ->
val polId = lot.pickOrderLine?.id
val illId = lot.suggestedLotLine?.id


+ 5
- 5
src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt View File

@@ -134,8 +134,8 @@ class JobOrderController(
@RequestBody data: Map<String, Any>
): MessageResponse {
val request = SecondScanSubmitRequest(
pickOrderId = pickOrderId, // Use path variable
itemId = itemId, // Use path variable
pickOrderId = pickOrderId, // Use path variable
itemId = itemId, // Use path variable
qty = (data["qty"] as Number).toDouble(),
isMissing = data["isMissing"] as? Boolean ?: false,
isBad = data["isBad"] as? Boolean ?: false
@@ -150,10 +150,10 @@ fun recordSecondScanIssue(
@RequestBody data: Map<String, Any>
): MessageResponse {
val request = SecondScanIssueRequest(
pickOrderId = pickOrderId, // path 变量
itemId = itemId, // path 变量
pickOrderId = pickOrderId, // path 变量
itemId = itemId, // path 变量
qty = (data["qty"] as Number).toDouble(),
// 新增:安全读取 missQty/badItemQty/type,默认 0/"match"
// 新增:安全读取 missQty/badItemQty/type,默认 0/"match"
missQty = (data["missQty"] as? Number)?.toDouble() ?: 0.0,
badItemQty = (data["badItemQty"] as? Number)?.toDouble() ?: 0.0,
isMissing = data["isMissing"] as? Boolean ?: false,


+ 2
- 2
src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanIssueRequest.kt View File

@@ -4,8 +4,8 @@ data class SecondScanIssueRequest(
val pickOrderId: Long,
val itemId: Long,
val qty: Double, // 这是 actual pick qty (verified qty)
val missQty: Double = 0.0, // 添加:单独的 miss qty
val badItemQty: Double = 0.0, // 添加:单独的 bad item qty
val missQty: Double = 0.0, // 添加:单独的 miss qty
val badItemQty: Double = 0.0, // 添加:单独的 bad item qty
val isMissing: Boolean,
val isBad: Boolean,
val reason: String,


+ 2
- 2
src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanSubmitRequest.kt View File

@@ -4,9 +4,9 @@ import jakarta.validation.constraints.NotNull

data class SecondScanSubmitRequest(
@NotNull
val pickOrderId: Long, // Add missing property
val pickOrderId: Long, // Add missing property
@NotNull
val itemId: Long, // Add missing property
val itemId: Long, // Add missing property
@NotNull
val qty: Double,
val isMissing: Boolean = false,


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt View File

@@ -103,7 +103,7 @@ val doPickOrderId: Long? = null,
@Column(name = "deleted", nullable = false)
val deleted: Boolean = false
) {
// 添加默认构造函数
// 添加默认构造函数
constructor() : this(
pickOrderId = null,
pickOrderCode = "",


+ 3
- 3
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt View File

@@ -13,15 +13,15 @@ interface TruckRepository : AbstractRepository<Truck, Long> {
@Query("SELECT t FROM Truck t WHERE t.shop.id = :shopId AND t.deleted = false ORDER BY t.id ASC")
fun findFirstByShopIdAndDeletedFalse(@Param("shopId") shopId: Long): List<Truck>
// 使用新的 TruckLanceCode 字段名
// 使用新的 TruckLanceCode 字段名
@Query("SELECT t FROM Truck t WHERE t.truckLanceCode = :truckLanceCode AND t.deleted = false")
fun findByTruckLanceCodeAndDeletedFalse(@Param("truckLanceCode") truckLanceCode: String): Truck?

// 按 ShopCode 查询
// 按 ShopCode 查询
@Query("SELECT t FROM Truck t WHERE t.shopCode = :shopCode AND t.deleted = false")
fun findByShopCodeAndDeletedFalse(@Param("shopCode") shopCode: String): List<Truck>
// 按 Store_id 查询
// 按 Store_id 查询
fun findByStoreIdAndDeletedFalse(storeId: Int): List<Truck>
}

+ 50
- 50
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt View File

@@ -5,7 +5,7 @@ import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository
import com.ffii.fpsms.modules.stock.entity.StockOutLine
import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // 添加这行
import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // 添加这行
import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus
import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
@@ -70,15 +70,15 @@ open class PickExecutionIssueService(
val pickOrder = pickOrderRepository.findById(request.pickOrderId).orElse(null)
// 2. 创建 pick execution issue 记录
val pickExecutionIssue = PickExecutionIssue(
id = null, // 添加 id
id = null, // 添加 id
pickOrderId = request.pickOrderId,
pickOrderCode = request.pickOrderCode,
pickOrderCreateDate = request.pickOrderCreateDate,
pickExecutionDate = request.pickExecutionDate ?: LocalDate.now(),
pickOrderLineId = request.pickOrderLineId,
issueNo = generateIssueNo(),
joPickOrderId = pickOrder?.jobOrder?.id, // 添加
doPickOrderId = if (pickOrder?.type?.value == "do") pickOrder.id else null, // 添加
joPickOrderId = pickOrder?.jobOrder?.id, // 添加
doPickOrderId = if (pickOrder?.type?.value == "do") pickOrder.id else null, // 添加
issueCategory = IssueCategory.valueOf(
request.issueCategory ?: "lot_issue"
),
@@ -93,16 +93,16 @@ open class PickExecutionIssueService(
missQty = request.missQty,
badItemQty = request.badItemQty,
issueRemark = request.issueRemark,
pickerName = request.pickerName, // 确保从 request 中获取
handleStatus = HandleStatus.pending, // 添加
handleDate = null, // 添加
pickerName = request.pickerName, // 确保从 request 中获取
handleStatus = HandleStatus.pending, // 添加
handleDate = null, // 添加
handledBy = request.handledBy,
created = LocalDateTime.now(),
createdBy = "system",
version = 0, // 添加
version = 0, // 添加
modified = LocalDateTime.now(),
modifiedBy = "system",
deleted = false // 添加
deleted = false // 添加
)

val savedIssue = pickExecutionIssueRepository.save(pickExecutionIssue)
@@ -139,7 +139,7 @@ open class PickExecutionIssueService(
handleBothMissAndBadItem(request, missQty, badItemQty)
}
// 修复:情况4: 有 miss item 的情况(无论 actualPickQty 是多少)
// 修复:情况4: 有 miss item 的情况(无论 actualPickQty 是多少)
missQty > BigDecimal.ZERO -> {
handleMissItemWithPartialPick(request, actualPickQty, missQty)
}
@@ -264,7 +264,7 @@ private fun checkAndCompletePickOrder(consoCode: String) {
// 4. 如果所有行都完成或被拒绝,则完成 pick order
if (unfinishedLines.isEmpty()) {
println(" All stock out lines completed or rejected, completing pick order...")
println(" All stock out lines completed or rejected, completing pick order...")
// 4.1 更新 StockOut 状态
stockOut.status = StockOutStatus.COMPLETE.status
@@ -281,7 +281,7 @@ private fun checkAndCompletePickOrder(consoCode: String) {
println("Updated pick order line ${line.id} to COMPLETED")
}
pickOrderLineRepository.saveAll(pickOrderLines)
println(" Updated ${pickOrderLines.size} pick order lines to COMPLETED status")
println(" Updated ${pickOrderLines.size} pick order lines to COMPLETED status")
// 4.3 获取所有受影响的 pick orders
val pickOrderIds = pickOrderLines.mapNotNull { it.pickOrder?.id }.distinct()
@@ -302,15 +302,15 @@ private fun checkAndCompletePickOrder(consoCode: String) {
pickOrder.status = PickOrderStatus.COMPLETED
pickOrder.completeDate = LocalDateTime.now()
pickOrderRepository.save(pickOrder)
println(" Updated pick order ${pickOrder.code} to COMPLETED status")
println(" Updated pick order ${pickOrder.code} to COMPLETED status")
// 4.5 处理 DO pick order 相关记录
try {
val removedCount = doPickOrderService.removeDoPickOrdersForPickOrder(pickOrderId)
println(" Removed $removedCount do_pick_order records for completed pick order ${pickOrderId}")
println(" Removed $removedCount do_pick_order records for completed pick order ${pickOrderId}")
doPickOrderService.completeDoPickOrderRecordsForPickOrder(pickOrderId)
println(" Updated do_pick_order_record status to COMPLETED for pick order ${pickOrderId}")
println(" Updated do_pick_order_record status to COMPLETED for pick order ${pickOrderId}")
} catch (e: Exception) {
println("⚠️ Error updating DO pick order records: ${e.message}")
}
@@ -330,7 +330,7 @@ private fun checkAndCompletePickOrder(consoCode: String) {
}
joPickOrderRecordRepository.saveAll(joPickOrderRecords)
println(" Set jo_pick_order ticketCompleteTime for pick order ${pickOrderId}")
println(" Set jo_pick_order ticketCompleteTime for pick order ${pickOrderId}")
} catch (e: Exception) {
println("⚠️ Error updating JO pick order records: ${e.message}")
}
@@ -346,7 +346,7 @@ private fun checkAndCompletePickOrder(consoCode: String) {
}
}
// FPSMS-backend/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt
// 修复:处理有部分拣货但有 miss item 的情况
// 修复:处理有部分拣货但有 miss item 的情况
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, actualPickQty: BigDecimal, missQty: BigDecimal) {
println("=== HANDLING MISS ITEM WITH PARTIAL PICK (FIXED LOGIC) ===")
@@ -358,16 +358,16 @@ private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, ac
val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null)
if (inventoryLotLine != null) {
// 修复1:只处理已拣货的部分:更新 outQty
// 修复1:只处理已拣货的部分:更新 outQty
val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO
val newOutQty = currentOutQty.add(actualPickQty)
inventoryLotLine.outQty = newOutQty
// 修复2:Miss item 不减少 inQty,而是标记为 unavailable
// 修复2:Miss item 不减少 inQty,而是标记为 unavailable
// 因为 miss item 意味着这些物品实际上不存在或找不到
// 所以应该标记整个批次为 unavailable,而不是减少 inQty
// 修复3:如果 missQty > 0,标记批次为 unavailable
// 修复3:如果 missQty > 0,标记批次为 unavailable
if (missQty > BigDecimal.ZERO) {
inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE
}
@@ -381,11 +381,11 @@ private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, ac
println(" - Set status to UNAVAILABLE due to missQty: ${missQty}")
}
// 修复4:更新 inventory 表的 unavailableQty
// 修复4:更新 inventory 表的 unavailableQty
// 对于 miss item,应该将 missQty 计入 unavailableQty
updateInventoryUnavailableQty(itemId, missQty)
// 修复5:更新 stock_out_line 状态为 rejected(因为还有 miss item)
// 修复5:更新 stock_out_line 状态为 rejected(因为还有 miss item)
updateStockOutLineStatus(request, "rejected")
// 重新建议拣货批次(针对 miss 的数量)
@@ -397,7 +397,7 @@ private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, ac
}
}

// 修复:Miss item 处理逻辑
// 修复:Miss item 处理逻辑
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigDecimal) {
println("=== HANDLING MISS ITEM ONLY (FIXED LOGIC) ===")
@@ -408,7 +408,7 @@ private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigD
val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null)
if (inventoryLotLine != null) {
// 修复:Miss item 意味着剩余的所有物品都找不到
// 修复:Miss item 意味着剩余的所有物品都找不到
val currentInQty = inventoryLotLine.inQty ?: BigDecimal.ZERO
val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO
val remainingQty = currentInQty.minus(currentOutQty)
@@ -425,16 +425,16 @@ private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigD
println(" - Unavailable qty (should be remaining qty): ${remainingQty}")
}
// 修复:更新 inventory 表的 unavailableQty
// 修复:更新 inventory 表的 unavailableQty
// 应该是剩余的数量,而不是 missQty
val currentInQty = inventoryLotLine?.inQty ?: BigDecimal.ZERO
val currentOutQty = inventoryLotLine?.outQty ?: BigDecimal.ZERO
val remainingQty = currentInQty.minus(currentOutQty)
// 修复:只增加剩余数量,不要重复计算 missQty
// 修复:只增加剩余数量,不要重复计算 missQty
updateInventoryUnavailableQty(itemId, remainingQty)
// 修复:更新 stock_out_line 状态为 rejected
// 修复:更新 stock_out_line 状态为 rejected
updateStockOutLineStatus(request, "rejected")
// 重新建议拣货批次
@@ -446,7 +446,7 @@ private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigD
}
}

// 修复:Bad item 处理逻辑
// 修复:Bad item 处理逻辑
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: BigDecimal) {
println("=== HANDLING BAD ITEM ONLY (FIXED LOGIC) ===")
@@ -457,7 +457,7 @@ private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: Bi
val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null)
if (inventoryLotLine != null) {
// 修复:Bad item 不减少 inQty,而是标记为 unavailable
// 修复:Bad item 不减少 inQty,而是标记为 unavailable
// 因为 bad item 意味着这些物品质量有问题,不能使用
inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE
inventoryLotLine.modified = LocalDateTime.now()
@@ -467,10 +467,10 @@ private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: Bi
println("Bad item only: Set lot ${lotId} status to UNAVAILABLE")
}
// 修复:更新 inventory 表的 unavailableQty
// 修复:更新 inventory 表的 unavailableQty
updateInventoryUnavailableQty(itemId, badItemQty)
// 修复:更新 stock_out_line 状态为 rejected
// 修复:更新 stock_out_line 状态为 rejected
updateStockOutLineStatus(request, "rejected")
// 重新建议拣货批次
@@ -482,7 +482,7 @@ private fun handleBadItemOnly(request: PickExecutionIssueRequest, badItemQty: Bi
}
}

// 修复:Both miss and bad item 处理逻辑
// 修复:Both miss and bad item 处理逻辑
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty: BigDecimal, badItemQty: BigDecimal) {
println("=== HANDLING BOTH MISS AND BAD ITEM (FIXED LOGIC) ===")
@@ -494,7 +494,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
val totalUnavailableQty = missQty.add(badItemQty)
if (inventoryLotLine != null) {
// 修复:Miss + Bad item 不减少 inQty,而是标记为 unavailable
// 修复:Miss + Bad item 不减少 inQty,而是标记为 unavailable
inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE
inventoryLotLine.modified = LocalDateTime.now()
inventoryLotLine.modifiedBy = "system"
@@ -506,10 +506,10 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
println(" - Total Unavailable Qty: ${totalUnavailableQty}")
}
// 修复:更新 inventory 表的 unavailableQty
// 修复:更新 inventory 表的 unavailableQty
updateInventoryUnavailableQty(itemId, totalUnavailableQty)
// 修复:更新 stock_out_line 状态为 rejected
// 修复:更新 stock_out_line 状态为 rejected
updateStockOutLineStatus(request, "rejected")
// 重新建议拣货批次
@@ -521,19 +521,19 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
}
}

// 修复:正常拣货处理逻辑
// 修复:正常拣货处理逻辑
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun handleNormalPick(request: PickExecutionIssueRequest, actualPickQty: BigDecimal) {
println("=== HANDLING NORMAL PICK ===")
// 修复:更新 stock_out_line,但不要累积 qty
// 修复:更新 stock_out_line,但不要累积 qty
val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
request.pickOrderLineId,
request.lotId ?: 0L
)
stockOutLines.forEach { stockOutLine ->
// 修复:直接设置 qty 为 actualPickQty,不要累积
// 修复:直接设置 qty 为 actualPickQty,不要累积
val requiredQty = request.requiredQty?.toDouble() ?: 0.0
val actualPickQtyDouble = actualPickQty.toDouble()
val newStatus = if (actualPickQtyDouble >= requiredQty) {
@@ -543,7 +543,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
}
stockOutLine.status = newStatus
stockOutLine.qty = actualPickQtyDouble // 直接设置,不累积
stockOutLine.qty = actualPickQtyDouble // 直接设置,不累积
stockOutLine.modified = LocalDateTime.now()
stockOutLine.modifiedBy = "system"
stockOutLineRepository.save(stockOutLine)
@@ -551,11 +551,11 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
println("Updated stock out line ${stockOutLine.id}: status=${newStatus}, qty=${actualPickQtyDouble}")
}

// 修复:更新 inventory_lot_line 的 outQty
// 修复:更新 inventory_lot_line 的 outQty
val lotId = request.lotId ?: return
val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null)
if (inventoryLotLine != null) {
// 修复:计算新的 outQty,考虑之前的 outQty
// 修复:计算新的 outQty,考虑之前的 outQty
val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO
val previousPickedQty = currentOutQty.minus(actualPickQty) // 计算之前已拣的数量
val newOutQty = previousPickedQty.add(actualPickQty) // 更新为新的总拣货数量
@@ -569,7 +569,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
}
}

// 新方法:统一更新 inventory 表的 unavailableQty
// 新方法:统一更新 inventory 表的 unavailableQty
private fun updateInventoryUnavailableQty(itemId: Long, unavailableQty: BigDecimal) {
try {
println("=== INVENTORY UNAVAILABLE QTY UPDATE (TRIGGER HANDLED) ===")
@@ -582,7 +582,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
}
}

// 修复:更新 stock_out_line 状态
// 修复:更新 stock_out_line 状态
private fun updateStockOutLineStatus(request: PickExecutionIssueRequest, status: String) {
val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
request.pickOrderLineId,
@@ -592,7 +592,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
stockOutLines.forEach { stockOutLine ->
stockOutLine.status = status
// FIX: Update qty to actualPickQty before setting status to rejected
// FIX: Update qty to actualPickQty before setting status to rejected
if (status == "rejected" && request.actualPickQty != null) {
stockOutLine.qty = request.actualPickQty.toDouble()
println("Updated stock out line ${stockOutLine.id} qty to: ${request.actualPickQty}")
@@ -605,7 +605,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
println("Updated stock out line ${stockOutLine.id} status to: ${status}")
}
}
// 修复:使用 REQUIRES_NEW 传播级别,避免事务冲突
// 修复:使用 REQUIRES_NEW 传播级别,避免事务冲突
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun resuggestPickOrder(pickOrderId: Long?) {
if (pickOrderId != null) {
@@ -639,7 +639,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
"jo" -> {
// Case 1: 只返回 Job Order 的 issues
val joIssues = pickExecutionIssueRepository.findJoIssues()
println(" Finding JO issues: ${joIssues.size} records")
println(" Finding JO issues: ${joIssues.size} records")
joIssues.forEach {
println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}")
}
@@ -648,7 +648,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
"do" -> {
// Case 2: 只返回 Delivery Order 的 issues
val doIssues = pickExecutionIssueRepository.findDoIssues()
println(" Finding DO issues: ${doIssues.size} records")
println(" Finding DO issues: ${doIssues.size} records")
doIssues.forEach {
println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}")
}
@@ -657,7 +657,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
"material" -> {
// Case 3: 只返回 Material 的 issues (普通 pick order)
val materialIssues = pickExecutionIssueRepository.findMaterialIssues()
println(" Finding Material issues: ${materialIssues.size} records")
println(" Finding Material issues: ${materialIssues.size} records")
materialIssues.forEach {
println(" - ID=${it.id}, joPickOrderId=${it.joPickOrderId}, doPickOrderId=${it.doPickOrderId}")
}
@@ -666,7 +666,7 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
else -> {
// Case 4: 返回所有 issues
val allIssues = pickExecutionIssueRepository.findByDeletedFalse()
println(" Finding ALL issues: ${allIssues.size} records")
println(" Finding ALL issues: ${allIssues.size} records")
allIssues
}
}
@@ -772,7 +772,7 @@ open fun createBatchReleaseIssue(
createdCount++
}
println(" Created $createdCount ${issueCategory.name} issues for DO $deliveryOrderId")
println(" Created $createdCount ${issueCategory.name} issues for DO $deliveryOrderId")
return createdCount
} catch (e: Exception) {


+ 198
- 198
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
File diff suppressed because it is too large
View File


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickExecutionIssueController.kt View File

@@ -3,7 +3,7 @@ package com.ffii.fpsms.modules.pickOrder.web

import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue
import com.ffii.fpsms.modules.pickOrder.service.PickExecutionIssueService // 修复导入路径
import com.ffii.fpsms.modules.pickOrder.service.PickExecutionIssueService // 修复导入路径
import com.ffii.fpsms.modules.stock.web.model.PickExecutionIssueRequest
import org.springframework.web.bind.annotation.*



+ 1
- 1
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcess.kt View File

@@ -14,7 +14,7 @@ import com.ffii.fpsms.modules.master.entity.Bom
@Entity
@Table(name = "productprocess")
open class ProductProcess : BaseEntity<Long>() {
@Size(max = 50)
@NotNull
@Column(name = "productprocesscode", nullable = false, unique = true, length = 50)


+ 3
- 3
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt View File

@@ -14,12 +14,12 @@ import com.ffii.fpsms.modules.master.entity.BomProcess
open class ProductProcessLine : BaseEntity<Long>() {

// 添加 @ManyToOne
// 添加 @ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "operatorId")
open var operator: User? = null
// 添加 @ManyToOne
// 添加 @ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "equipmentId")
open var equipment: Equipment? = null
@@ -35,7 +35,7 @@ open class ProductProcessLine : BaseEntity<Long>() {
@Column(name = "equipment_name", length = 100)
open var equipmentType: String? = null
// 添加 @ManyToOne
// 添加 @ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "byproductId")
open var byproduct: Items? = null


+ 2
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLineRepository.kt View File

@@ -8,4 +8,6 @@ interface ProductProcessLineRepository : JpaRepository<ProductProcessLine, Long>
fun findByProductProcess_Id(productProcessId: Long): List<ProductProcessLine>
fun findByProductProcess_IdAndHandler_Id(productProcessId: Long, handlerId: Long): List<ProductProcessLine>
fun findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId: Long): List<ProductProcessLine>
fun findByProductProcess_IdIn(ids: List<Long>): List<ProductProcessLine>

}

+ 2
- 2
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssue.kt View File

@@ -8,8 +8,8 @@ import jakarta.validation.constraints.Size
import java.time.LocalDateTime

@Entity
@Table(name = "productionprocessissue") // 修复:改为正确的表名
open class ProductionProcessIssue : BaseEntity<Long>() { // 修复:改为正确的类名
@Table(name = "productionprocessissue") // 修复:改为正确的表名
open class ProductionProcessIssue : BaseEntity<Long>() { // 修复:改为正确的类名
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "productprocessid", nullable = false)


+ 58
- 55
src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt View File

@@ -45,7 +45,7 @@ open class ProductProcessService(
open fun findAll(pageable: Pageable): Page<ProductProcess> {
println("📋 Service: Finding all ProductProcess with page: ${pageable.pageNumber}, size: ${pageable.pageSize}")
val result = productProcessRepository.findAll(pageable)
println(" Service: Found ${result.totalElements} records")
println(" Service: Found ${result.totalElements} records")
return result
}
@@ -89,14 +89,14 @@ open class ProductProcessService(
}
val savedProcess = productProcessRepository.save(productProcess)
println(" Service: ProductProcess created with ID: ${savedProcess.id}")
println(" Service: ProductProcess created with ID: ${savedProcess.id}")
// 4. 查询 BOM 的所有工序步骤
val bomProcesses = bomProcessRepository.findByBomIdOrderBySeqNo(request.bomId)
println("🔍 Service: Found ${bomProcesses.size} BOM processes")
// 5. 为每个 BOM Process 创建 ProductProcessLine
bomProcesses.forEachIndexed { index, bomProcess -> // 修复 forEach
bomProcesses.forEachIndexed { index, bomProcess -> // 修复 forEach
val line = ProductProcessLine().apply {
this.productProcess = savedProcess
this.bomProcess = bomProcess
@@ -109,7 +109,7 @@ open class ProductProcessService(
println("➕ Service: Created line ${index + 1} - seq: ${line.seqNo}, name: ${line.name}")
}
println(" Service: All ${bomProcesses.size} lines created automatically")
println(" Service: All ${bomProcesses.size} lines created automatically")
return SaveProductProcessResponse(
id = savedProcess.id!!,
@@ -126,11 +126,11 @@ open class ProductProcessService(
}

// 添加:查询工序的所有步骤
// 添加:查询工序的所有步骤
open fun getLines(productProcessId: Long): List<ProductProcessLine> {
println("📋 Service: Getting lines for process ID: $productProcessId")
val lines = productProcessLineRepository.findByProductProcess_Id(productProcessId)
println(" Service: Found ${lines.size} lines")
println(" Service: Found ${lines.size} lines")
return lines
}
@@ -140,7 +140,7 @@ open class ProductProcessService(
productProcess.status = ProductProcessStatus.IN_PROGRESS
productProcess.startTime = LocalDateTime.now()
val saved = productProcessRepository.save(productProcess)
println(" Service: Process started, status: ${saved.status}")
println(" Service: Process started, status: ${saved.status}")
return saved
}
@@ -160,7 +160,7 @@ open class ProductProcessService(
productProcessRepository.save(productProcess)
val savedIssue = productionProcessIssueRepository.save(issue)
println(" Service: Process stopped, issue ID: ${savedIssue.id}")
println(" Service: Process stopped, issue ID: ${savedIssue.id}")
return savedIssue
}
@@ -178,7 +178,7 @@ open class ProductProcessService(
productProcessRepository.save(productProcess)
val savedIssue = productionProcessIssueRepository.save(issue)
println(" Service: Process resumed, total stop time: ${savedIssue.totalTime} minutes")
println(" Service: Process resumed, total stop time: ${savedIssue.totalTime} minutes")
return savedIssue
}
@@ -188,7 +188,7 @@ open class ProductProcessService(
productProcess.status = ProductProcessStatus.COMPLETED
productProcess.endTime = LocalDateTime.now()
val saved = productProcessRepository.save(productProcess)
println(" Service: Process completed")
println(" Service: Process completed")
return saved
}
@@ -206,7 +206,7 @@ open class ProductProcessService(
}
val saved = productProcessLineRepository.save(line)
println(" Service: Line added with ID: ${saved.id}")
println(" Service: Line added with ID: ${saved.id}")
return SaveProductProcessLineResponse(saved.id!!)
}
@@ -230,20 +230,20 @@ open class ProductProcessService(
}
val saved = productProcessLineRepository.save(line)
println(" Service: Line output updated")
println(" Service: Line output updated")
return saved
}
open fun getIssues(productProcessId: Long): List<ProductionProcessIssue> {
println("📋 Service: Getting issues for ProductProcess ID: $productProcessId")
val issues = productionProcessIssueRepository.findByProductProcess_Id(productProcessId)
println(" Service: Found ${issues.size} issues")
println(" Service: Found ${issues.size} issues")
return issues
}
open fun findByJobOrderId(jobOrderId: Long): List<ProductProcess> {
println("🔍 Service: Finding ProductProcess by jobOrderId: $jobOrderId")
val result = productProcessRepository.findByJobOrder_Id(jobOrderId)
println(" Service: Found ${result.size} processes")
println(" Service: Found ${result.size} processes")
return result
}
@@ -280,7 +280,7 @@ open class ProductProcessService(
)
}
println(" Service: Returning ${result.size} processes with ${result.sumOf { it.lines.size }} total lines")
println(" Service: Returning ${result.size} processes with ${result.sumOf { it.lines.size }} total lines")
return result
}
open fun findAllAsDto(pageable: Pageable): Page<ProductProcessSimpleResponse> {
@@ -300,7 +300,7 @@ open class ProductProcessService(
)
}
println(" Service: Converted ${dtoList.size} entities to DTOs")
println(" Service: Converted ${dtoList.size} entities to DTOs")
return org.springframework.data.domain.PageImpl(dtoList, pageable, entityPage.totalElements)
}
@@ -435,7 +435,7 @@ open class ProductProcessService(
val line = productProcessLineRepository.findById(lineId)
.orElseThrow { IllegalArgumentException("Line not found") }
// 修复:使用 UserRepository 获取 User 对象
// 修复:使用 UserRepository 获取 User 对象
val user = userRepository.findById(userId)
.orElseThrow { IllegalArgumentException("User not found with id: $userId") }
@@ -443,13 +443,13 @@ open class ProductProcessService(
line.startTime = LocalDateTime.now()
val saved = productProcessLineRepository.save(line)
println(" Service: Line started, handlerId: ${saved.handler?.id}")
println(" Service: Line started, handlerId: ${saved.handler?.id}")
return saved
}
open fun findPendingLinesByHandlerId(handlerId: Long): List<ProductProcessLine> {
println("🔍 Service: Finding pending lines for handlerId: $handlerId")
val lines = productProcessLineRepository.findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId)
println(" Service: Found ${lines.size} pending lines")
println(" Service: Found ${lines.size} pending lines")
return lines
}
@@ -753,42 +753,45 @@ open fun updateProductProcessLineQty(request: UpdateProductProcessLineQtyRequest
errorPosition = null,
)
}
/*
open fun getAllJoborderProductProcessInfo(): List<AllJoborderProductProcessInfoResponse> {
val productProcesses = productProcessRepository.findAllByDeletedIsFalse()
val productProcessId=productProcesses.map { it.id }
val productProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcessId.map { it.id })
return productProcesses.map { process ->
AllJoborderProductProcessInfoResponse(
id = productProcesses.id,
productProcessCode = productProcesses.productProcessCode,
status = productProcesses.status,
startTime = productProcesses.startTime,
endTime = productProcesses.endTime,
date = productProcesses.date,
bomId = productProcesses.bom.id,
bomName = productProcesses.bom.name,
jobOrderId = productProcesses.jobOrder.id,
lines = productProcessLines.map { line ->
ProductProcessInfoResponse(
id = line.id?:0,
productProcessCode = process.productProcessCode?:"",
operatorId = process.operator?.id?:0,
operatorName = process.operator?.name?:"",
equipmentId = line.equipment?.id?:0,
equipmentName = process.equipment?.name?:"",
startTime = line.startTime?:LocalDateTime.now(),
endTime = line.endTime?:LocalDateTime.now(),
date = line.date?:LocalDate.now(),
status = line.status?:"",
bomId = process.bom?.id?:0,
bomName = process.bom?.name?:"",
jobOrderId = process.jobOrder?.id?:0
)
}
)

open fun getAllJoborderProductProcessInfo(): List<AllJoborderProductProcessInfoResponse> {
val productProcesses = productProcessRepository.findAllByDeletedIsFalse()
val productProcessIds = productProcesses.map { it.id }

return productProcesses.map { productProcesses ->
val productProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcesses.id?:0L)
val jobOrder = jobOrderRepository.findById(productProcesses.jobOrder?.id?:0L).orElse(null)
val FinishedProductProcessLineCount = productProcessLineRepository.findByProductProcess_Id(productProcesses.id?:0L).count { it.status == "Completed" }
AllJoborderProductProcessInfoResponse(
id = productProcesses.id ?: 0L,
productProcessCode = productProcesses.productProcessCode,
status = productProcesses.status.value,
startTime = productProcesses.startTime,
endTime = productProcesses.endTime,
date = productProcesses.date,
bomId = productProcesses.bom?.id,
itemName = productProcesses.item?.name,
jobOrderId = productProcesses.jobOrder?.id,
jobOrderCode = jobOrder?.code,
productProcessLineCount = productProcessLines.size,
FinishedProductProcessLineCount = FinishedProductProcessLineCount,
lines = productProcessLines
.map { line ->
ProductProcessInfoResponse(
id = line.id ?: 0,
operatorId = line.operator?.id,
operatorName = line.operator?.name ?: "",
equipmentId = line.equipment?.id,
equipmentName = line.equipmentType?: "",
startTime = line.startTime,
endTime = line.endTime,
status = line.status ?: ""
)
}
)
}
}
}

*/
}

+ 34
- 34
src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt View File

@@ -16,18 +16,18 @@ class ProductProcessController(
) {

@GetMapping
fun findAll(pageable: Pageable): Page<ProductProcessSimpleResponse> { // 改为返回 DTO
fun findAll(pageable: Pageable): Page<ProductProcessSimpleResponse> { // 改为返回 DTO
println("📋 Controller: GET /product-process - Finding all")
val result = productProcessService.findAllAsDto(pageable) // 使用新方法
println(" Controller: Returning ${result.totalElements} records")
val result = productProcessService.findAllAsDto(pageable) // 使用新方法
println(" Controller: Returning ${result.totalElements} records")
return result
}

@GetMapping("/{id}")
fun findById(@PathVariable id: Long): ProductProcessSimpleResponse { // 改为返回 DTO
fun findById(@PathVariable id: Long): ProductProcessSimpleResponse { // 改为返回 DTO
println("🔍 Controller: GET /product-process/$id")
val result = productProcessService.findByIdAsDto(id) // 使用新方法
println(" Controller: Found: ${result.productProcessCode}")
val result = productProcessService.findByIdAsDto(id) // 使用新方法
println(" Controller: Found: ${result.productProcessCode}")
return result
}

@@ -35,24 +35,24 @@ class ProductProcessController(
fun findByCode(@PathVariable code: String): ProductProcess {
println("🔍 Controller: GET /product-process/code/$code")
val result = productProcessService.findByCode(code)
println(" Controller: Found ID: ${result.id}")
println(" Controller: Found ID: ${result.id}")
return result
}

@PostMapping
fun create(@RequestBody request: SaveProductProcessRequest): SaveProductProcessResponse {
println("💾 Controller: POST /product-process - Creating ProductProcess")
//println("📦 Controller: Request - bomId: ${request.bomId}, jobOrderId: ${request.jobOrderId}, date: ${request.date}") // 修复
//println("📦 Controller: Request - bomId: ${request.bomId}, jobOrderId: ${request.jobOrderId}, date: ${request.date}") // 修复
val result = productProcessService.create(request)
println(" Controller: Created ProductProcess - ID: ${result.id}, Code: ${result.productProcessCode}, Lines: ${result.linesCreated}") // 改进日志
println(" Controller: Created ProductProcess - ID: ${result.id}, Code: ${result.productProcessCode}, Lines: ${result.linesCreated}") // 改进日志
return result
}

@PostMapping("/{id}/start")
fun startProcess(@PathVariable id: Long, @RequestParam operatorId: Long): ProductProcessSimpleResponse {
println("▶️ Controller: POST /product-process/$id/start - operatorId: $operatorId")
val result = productProcessService.startProcessAsDto(id, operatorId) // 改用 AsDto 方法
println(" Controller: Process started")
val result = productProcessService.startProcessAsDto(id, operatorId) // 改用 AsDto 方法
println(" Controller: Process started")
return result
}

@@ -60,11 +60,11 @@ class ProductProcessController(
fun stopProcess(
@PathVariable id: Long,
@RequestBody request: StopProcessRequest
): ProductionProcessIssueResponse { // 改为 Issue Response
): ProductionProcessIssueResponse { // 改为 Issue Response
println("⏸️ Controller: POST /product-process/$id/stop")
println("📦 Controller: Reason: ${request.reason}")
val result = productProcessService.stopProcessAsDto(id, request) // 使用返回 DTO 的方法
println(" Controller: Process stopped, issue ID: ${result.id}")
val result = productProcessService.stopProcessAsDto(id, request) // 使用返回 DTO 的方法
println(" Controller: Process stopped, issue ID: ${result.id}")
return result
}

@@ -72,18 +72,18 @@ class ProductProcessController(
fun resumeProcess(
@PathVariable id: Long,
@PathVariable issueId: Long
): ProductionProcessIssueResponse { // 改为 Issue Response
): ProductionProcessIssueResponse { // 改为 Issue Response
println("▶️ Controller: POST /product-process/$id/resume/$issueId")
val result = productProcessService.resumeProcessAsDto(id, issueId) // 使用返回 DTO 的方法
println(" Controller: Process resumed, stop duration: ${result.totalTime} min")
val result = productProcessService.resumeProcessAsDto(id, issueId) // 使用返回 DTO 的方法
println(" Controller: Process resumed, stop duration: ${result.totalTime} min")
return result
}

@PostMapping("/{id}/complete")
fun completeProcess(@PathVariable id: Long): ProductProcessSimpleResponse {
println("✔️ Controller: POST /product-process/$id/complete")
val result = productProcessService.completeProcessAsDto(id) // 改用 AsDto 方法
println(" Controller: Process completed")
val result = productProcessService.completeProcessAsDto(id) // 改用 AsDto 方法
println(" Controller: Process completed")
return result
}

@@ -95,7 +95,7 @@ class ProductProcessController(
println("➕ Controller: POST /product-process/$id/lines")
println("📦 Controller: Line - name: ${request.name}, equipment: ${request.equipmentType}")
val result = productProcessService.addLine(id, request)
println(" Controller: Line added with ID: ${result.id}")
println(" Controller: Line added with ID: ${result.id}")
return result
}

@@ -103,10 +103,10 @@ class ProductProcessController(
fun updateLineOutput(
@PathVariable lineId: Long,
@RequestBody request: UpdateLineOutputRequest
): ProductProcessLineResponse { // 改为 ProductProcessLineResponse
): ProductProcessLineResponse { // 改为 ProductProcessLineResponse
println("📊 Controller: PUT /lines/$lineId/output")
val result = productProcessService.updateLineOutputAsDto(lineId, request)
println(" Controller: Line output updated")
println(" Controller: Line output updated")
return result
}

@@ -114,32 +114,32 @@ class ProductProcessController(
fun getLines(@PathVariable id: Long): List<ProductProcessLineResponse> {
println("📋 Controller: GET /product-process/$id/lines")
val result = productProcessService.getLinesAsDto(id)
println(" Controller: Found ${result.size} lines")
println(" Controller: Found ${result.size} lines")
return result
}

@GetMapping("/{id}/issues")
fun getIssues(@PathVariable id: Long): List<ProductionProcessIssueResponse> { // 改为 Issue Response List
fun getIssues(@PathVariable id: Long): List<ProductionProcessIssueResponse> { // 改为 Issue Response List
println("📋 Controller: GET /product-process/$id/issues")
val result = productProcessService.getIssuesAsDto(id) // 使用返回 DTO 的方法
println(" Controller: Found ${result.size} issues")
val result = productProcessService.getIssuesAsDto(id) // 使用返回 DTO 的方法
println(" Controller: Found ${result.size} issues")
return result
}

@GetMapping("/by-job-order/{jobOrderId}")
fun findByJobOrderId(@PathVariable jobOrderId: Long): List<ProductProcessWithLinesResponse> { // 改为返回 DTO
fun findByJobOrderId(@PathVariable jobOrderId: Long): List<ProductProcessWithLinesResponse> { // 改为返回 DTO
println("🔍 Controller: GET /product-process/by-job-order/$jobOrderId")
val result = productProcessService.findByJobOrderIdWithLines(jobOrderId) // 直接使用 with-lines 方法
println(" Controller: Found ${result.size} processes for Job Order $jobOrderId")
val result = productProcessService.findByJobOrderIdWithLines(jobOrderId) // 直接使用 with-lines 方法
println(" Controller: Found ${result.size} processes for Job Order $jobOrderId")
return result
}

// 额外添加:获取带 lines 的完整数据
// 额外添加:获取带 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")
val result = productProcessService.findByJobOrderIdWithLines(jobOrderId)
println(" Controller: Found ${result.size} processes with lines for Job Order $jobOrderId")
println(" Controller: Found ${result.size} processes with lines for Job Order $jobOrderId")
return result
}

@@ -147,7 +147,7 @@ class ProductProcessController(
fun startLine(@PathVariable lineId: Long, @RequestParam userId: Long): ProductProcessLineResponse {
println("▶️ Controller: POST /product-process/lines/$lineId/start - userId: $userId")
val entity = productProcessService.startLine(lineId, userId)
// 修复:返回正确的 DTO
// 修复:返回正确的 DTO
return productProcessService.getLinesAsDto(entity.productProcess?.id!!).find { it.id == entity.id!! }!!
}

@@ -183,10 +183,10 @@ class ProductProcessController(
fun demoupdateqty(@PathVariable lineId: Long, @RequestBody request: UpdateProductProcessLineQtyRequest): MessageResponse {
return productProcessService.updateProductProcessLineQty(request)
}
/*
@GetMapping("/Demo/Process/all")
fun demoprocessall(): List<AllJoborderProductProcessInfoResponse> {
return productProcessService.getAllJoborderProductProcessInfo()
}
*/
}

+ 8
- 8
src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt View File

@@ -27,8 +27,8 @@ data class ProductionProcessIssueResponse(
)
data class SaveProductProcessResponse(
val id: Long,
val productProcessCode: String? = null, // 添加
val linesCreated: Int = 0 // 添加
val productProcessCode: String? = null, // 添加
val linesCreated: Int = 0 // 添加
)
data class ProductProcessWithLinesResponse(
val id: Long,
@@ -142,22 +142,22 @@ data class AllJoborderProductProcessInfoResponse(
val endTime: LocalDateTime?,
val date: LocalDate?,
val bomId: Long?,
val bomName: String?,
val itemName: String?,
val jobOrderId: Long?,
val jobOrderCode: String?,
val productProcessLineCount: Int,
val FinishedProductProcessLineCount: Int,

val lines: List<ProductProcessInfoResponse>
)
data class ProductProcessInfoResponse(
val id: Long,
val productProcessCode: String?,
val operatorId: Long?,
val operatorName: String?,
val equipmentId: Long?,
val equipmentName: String?,
val startTime: LocalDateTime?,
val endTime: LocalDateTime?,
val date: LocalDate?,
val status: String,
val bomId: Long?,
val bomName: String?,
val jobOrderId: Long?

)

+ 2
- 2
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt View File

@@ -121,7 +121,7 @@ open class InventoryLotLineService(

val updatedLotLine = saveInventoryLotLine(updateRequest)

// ADD THIS: Update inventory table after lot line status change
// ADD THIS: Update inventory table after lot line status change
updateInventoryTable(updatedLotLine)

return MessageResponse(
@@ -134,7 +134,7 @@ open class InventoryLotLineService(
)
}

// ADD THIS: New method to update inventory table
// ADD THIS: New method to update inventory table
private fun updateInventoryTable(inventoryLotLine: InventoryLotLine) {
try {
// Get the item ID from the inventory lot


+ 7
- 7
src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt View File

@@ -97,7 +97,7 @@ val existingStockOutLine = stockOutLineRepository.findByPickOrderLineIdAndInvent
)
if (existingStockOutLine.isNotEmpty()) {
// 如果已存在,返回 null 表示不需要创建
// 如果已存在,返回 null 表示不需要创建
return MessageResponse(
id = null,
name = "Stock out line already exists",
@@ -189,7 +189,7 @@ fun handleQc(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): Li
@Transactional
open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): MessageResponse {
try {
// Get stockOutId from pickOrderLineId with detailed error
// Get stockOutId from pickOrderLineId with detailed error
val stockOutId = getStockOutIdFromPickOrderLine(request.pickOrderLineId)
println("Found stockOutId: $stockOutId for pickOrderLineId: ${request.pickOrderLineId}")
@@ -253,16 +253,16 @@ open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): Mes
}
}

// Update helper method with detailed error messages
// Update helper method with detailed error messages
private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long {
println("Getting stockOutId for pickOrderLineId: $pickOrderLineId")
// Fixed: Use poId instead of pick_order_id
// Fixed: Use poId instead of pick_order_id
val sql = """
SELECT so.id as stockOutId, so.consoPickOrderCode, po.consoCode
FROM stock_out so
JOIN pick_order po ON po.consoCode = so.consoPickOrderCode
JOIN pick_order_line pol ON pol.poId = po.id -- Fixed: Use poId
JOIN pick_order_line pol ON pol.poId = po.id -- Fixed: Use poId
WHERE pol.id = :pickOrderLineId
""".trimIndent()
@@ -569,7 +569,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long {
throw e
}
}
// ADD THIS: Handle lot rejection when stock out line is rejected
// ADD THIS: Handle lot rejection when stock out line is rejected
private fun handleLotRejectionFromStockOutLine(stockOutLine: StockOutLine) {
try {
println("=== HANDLING LOT REJECTION FROM STOCK OUT LINE ===")
@@ -612,7 +612,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long {
}
}

// ADD THIS: Update inventory table after lot rejection
// ADD THIS: Update inventory table after lot rejection
private fun updateInventoryTableAfterLotRejection(inventoryLotLine: InventoryLotLine) {
try {
println("=== UPDATING INVENTORY TABLE ===")


+ 72
- 72
src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt View File

@@ -5,9 +5,9 @@ import com.ffii.fpsms.modules.pickOrder.entity.PickOrder

import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // 添加
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // 添加
import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory
import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus // 添加
import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus // 添加
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository
import com.ffii.fpsms.modules.stock.entity.*
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus
@@ -20,8 +20,8 @@ import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotResponse
import org.springframework.stereotype.Service
import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime // 添加
import java.time.format.DateTimeFormatter // 添加
import java.time.LocalDateTime // 添加
import java.time.format.DateTimeFormatter // 添加
import kotlin.jvm.optionals.getOrDefault
import kotlin.jvm.optionals.getOrNull
import org.springframework.transaction.annotation.Transactional
@@ -46,7 +46,7 @@ open class SuggestedPickLotService(
val pickOrderLineRepository: PickOrderLineRepository,
val inventoryLotLineService: InventoryLotLineService,
val itemUomService: ItemUomService,
val pickExecutionIssueRepository: PickExecutionIssueRepository, // 添加逗号
val pickExecutionIssueRepository: PickExecutionIssueRepository, // 添加逗号
val pickOrderRepository: PickOrderRepository,
val inventoryRepository: InventoryRepository,
val failInventoryLotLineRepository: FailInventoryLotLineRepository,
@@ -114,13 +114,13 @@ open class SuggestedPickLotService(
val lotLines = availableInventoryLotLines[line.item?.id].orEmpty()
val ratio = one // (salesUnit?.ratioN ?: one).divide(salesUnit?.ratioD ?: one, 10, RoundingMode.HALF_UP)
// FIX: Calculate remaining quantity needed (not the full required quantity)
// FIX: Calculate remaining quantity needed (not the full required quantity)
val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(line.id!!)
val totalPickedQty = stockOutLines
.filter {
it.status == "completed" ||
it.status == "partially_completed" ||
(it.status == "rejected" && (it.qty ?: zero) > zero) // 包含已 picked 的 rejected
(it.status == "rejected" && (it.qty ?: zero) > zero) // 包含已 picked 的 rejected
}
.sumOf { it.qty ?: zero }
val requiredQty = line.qty ?: zero
@@ -132,7 +132,7 @@ open class SuggestedPickLotService(
println("Remaining qty needed: $remainingQty")
println("Stock out lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}")
// FIX: Use remainingQty instead of line.qty
// FIX: Use remainingQty instead of line.qty
var remainingQtyToAllocate = remainingQty
println("remaining1 $remainingQtyToAllocate (sales units)")
val updatedLotLines = mutableListOf<InventoryLotLineInfo>()
@@ -142,7 +142,7 @@ open class SuggestedPickLotService(
println("calculateRemainingQtyForInfo(lotLine) ${calculateRemainingQtyForInfo(lotLine)}")
// 修复:计算可用数量,转换为销售单位
// 修复:计算可用数量,转换为销售单位
val availableQtyInBaseUnits = calculateRemainingQtyForInfo(lotLine)
val holdQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero
val availableQtyInSalesUnits = availableQtyInBaseUnits
@@ -159,11 +159,11 @@ open class SuggestedPickLotService(
val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() }
val originalHoldQty = inventoryLotLine?.holdQty
// 修复:在销售单位中计算分配数量
// 修复:在销售单位中计算分配数量
val assignQtyInSalesUnits = minOf(availableQtyInSalesUnits, remainingQtyToAllocate)
remainingQtyToAllocate = remainingQtyToAllocate.minus(assignQtyInSalesUnits)
val newHoldQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero
// 修复:将销售单位转换为基础单位来更新 holdQty
// 修复:将销售单位转换为基础单位来更新 holdQty
val assignQtyInBaseUnits = assignQtyInSalesUnits.multiply(ratio)
holdQtyMap[lotLine.id] = (holdQtyMap[lotLine.id] ?: zero).plus(assignQtyInBaseUnits)
@@ -171,10 +171,10 @@ open class SuggestedPickLotService(
type = SuggestedPickLotType.PICK_ORDER
suggestedLotLine = inventoryLotLine
pickOrderLine = line
qty = assignQtyInSalesUnits // 保存销售单位
qty = assignQtyInSalesUnits // 保存销售单位
}
}
// 修复:计算现有 suggestions 中 pending/checked 状态满足的数量
// 修复:计算现有 suggestions 中 pending/checked 状态满足的数量
var existingSatisfiedQty = BigDecimal.ZERO

// 查询现有的 suggestions 用于这个 pick order line
@@ -194,7 +194,7 @@ open class SuggestedPickLotService(
}
}

// 调整 remainingQtyToAllocate,减去已经通过现有 suggestions 满足的数量
// 调整 remainingQtyToAllocate,减去已经通过现有 suggestions 满足的数量
remainingQtyToAllocate = remainingQtyToAllocate.minus(existingSatisfiedQty)
println("Existing satisfied qty: $existingSatisfiedQty")
println("Adjusted remaining qty: $remainingQtyToAllocate")
@@ -209,7 +209,7 @@ open class SuggestedPickLotService(
type = SuggestedPickLotType.PICK_ORDER
suggestedLotLine = null
pickOrderLine = line
qty = remainingQtyToAllocate // 保存销售单位
qty = remainingQtyToAllocate // 保存销售单位
}
try {
/*
@@ -337,7 +337,7 @@ open class SuggestedPickLotService(
}
val savedStockOutLine = stockOutLIneRepository.saveAndFlush(stockOutLine)
println(" Created stock out line ID: ${savedStockOutLine.id} for suggestion ID: ${suggestion.id}")
println(" Created stock out line ID: ${savedStockOutLine.id} for suggestion ID: ${suggestion.id}")
return savedStockOutLine
@@ -357,7 +357,7 @@ open class SuggestedPickLotService(
println("Pick Order Code: ${pickOrder.code}")
println("Pick Order Status: ${pickOrder.status}")
// NEW: Get ALL pick orders for the same items
// NEW: Get ALL pick orders for the same items
val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct()
println("Item IDs in current pick order: $itemIds")
@@ -373,7 +373,7 @@ open class SuggestedPickLotService(
allCompetingPickOrders.addAll(competingOrders)
}
// FIX: Only resuggest pick orders that have rejected stock out lines
// FIX: Only resuggest pick orders that have rejected stock out lines
val allPickOrdersToResuggest = (listOf(pickOrder) + allCompetingPickOrders)
.filter { pickOrderToCheck ->
// Only resuggest if the pick order has rejected stock out lines
@@ -397,7 +397,7 @@ open class SuggestedPickLotService(
println("Filtered pick orders to resuggest: ${allPickOrdersToResuggest.size}")
println("Pick orders being resuggested: ${allPickOrdersToResuggest.map { "${it.code}(${it.status})" }}")
// FIX: Only resuggest if there are actually pick orders with rejected lots
// FIX: Only resuggest if there are actually pick orders with rejected lots
if (allPickOrdersToResuggest.isEmpty()) {
println("No pick orders need resuggesting - no rejected lots found")
return MessageResponse(
@@ -410,21 +410,21 @@ open class SuggestedPickLotService(
)
}
// FIX: Get all pick order line IDs for the orders to resuggest
// FIX: Get all pick order line IDs for the orders to resuggest
val allPickOrderLineIds = allPickOrdersToResuggest
.flatMap { it.pickOrderLines }
.mapNotNull { it.id }
println("All pick order line IDs to resuggest: $allPickOrderLineIds")
// FIX: Get all existing suggestions for these pick order lines
// FIX: Get all existing suggestions for these pick order lines
val allSuggestions = suggestedPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIds)
println("Found ${allSuggestions.size} existing suggestions")
// 删除第 376-395 行的旧代码,替换为:
// FIX: Separate suggestions to keep (those WITHOUT rejected stock out lines) and delete
// 删除第 376-395 行的旧代码,替换为:
// FIX: Separate suggestions to keep (those WITHOUT rejected stock out lines) and delete
val suggestionsToKeep = allSuggestions.filter { suggestion ->
val pickOrderLineId = suggestion.pickOrderLine?.id
val suggestedLotLineId = suggestion.suggestedLotLine?.id
@@ -434,14 +434,14 @@ val suggestionsToKeep = allSuggestions.filter { suggestion ->
pickOrderLineId,
suggestedLotLineId
)
// 保留没有 rejected stock out lines 的 suggestions
// 保留没有 rejected stock out lines 的 suggestions
!stockOutLines.any { it.status == "rejected" }
} else {
true // 保留有问题的 suggestions 用于调试
}
}

// 只删除有 rejected stock out lines 的 suggestions
// 只删除有 rejected stock out lines 的 suggestions
val suggestionsToDelete = allSuggestions.filter { suggestion ->
val pickOrderLineId = suggestion.pickOrderLine?.id
val suggestedLotLineId = suggestion.suggestedLotLine?.id
@@ -451,7 +451,7 @@ val suggestionsToDelete = allSuggestions.filter { suggestion ->
pickOrderLineId,
suggestedLotLineId
)
stockOutLines.any { it.status == "rejected" } // 只删除 rejected 的
stockOutLines.any { it.status == "rejected" } // 只删除 rejected 的
} else {
suggestedLotLineId == null
}
@@ -461,7 +461,7 @@ val suggestionsToDelete = allSuggestions.filter { suggestion ->
println("Suggestions to keep (with rejected stock out lines): ${suggestionsToKeep.size}")
println("Suggestions to delete: ${suggestionsToDelete.size}")
// FIX: Clear holdQty ONLY for lots that have rejected stock out lines
// FIX: Clear holdQty ONLY for lots that have rejected stock out lines
val rejectedLotIds = suggestionsToDelete.mapNotNull { it.suggestedLotLine?.id }.distinct()
println("Rejected lot IDs (clearing holdQty only): $rejectedLotIds")
@@ -477,7 +477,7 @@ val suggestionsToDelete = allSuggestions.filter { suggestion ->

println("Keeping all suggestions (including rejected ones for display)")
// NEW: Build holdQtyMap with existing holdQty from other pick orders
// NEW: Build holdQtyMap with existing holdQty from other pick orders
val existingHoldQtyMap = mutableMapOf<Long?, BigDecimal?>()
// Get all lots that are being used by other pick orders (including those NOT being resuggested)
@@ -507,9 +507,9 @@ println("Keeping all suggestions (including rejected ones for display)")
println("Final existing holdQtyMap: $existingHoldQtyMap")
// FIX: Create new suggestions for all pick orders to resuggest
// FIX: Create new suggestions for all pick orders to resuggest
allPickOrdersToResuggest.forEach { pickOrderToResuggest ->
// 只获取有 rejected stock out lines 的 pick order lines
// 只获取有 rejected stock out lines 的 pick order lines
val problematicPickOrderLines = pickOrderToResuggest.pickOrderLines.filter { pol ->
val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!)
stockOutLines.any { it.status == "rejected" }
@@ -518,7 +518,7 @@ println("Keeping all suggestions (including rejected ones for display)")
if (problematicPickOrderLines.isNotEmpty()) {
println("=== Creating new suggestions for pick order: ${pickOrderToResuggest.code} ===")
// 调用 suggestionForPickOrderLines 生成新的 suggestions
// 调用 suggestionForPickOrderLines 生成新的 suggestions
val request = SuggestedPickLotForPolRequest(
pickOrderLines = problematicPickOrderLines,
holdQtyMap = existingHoldQtyMap.toMutableMap()
@@ -533,24 +533,24 @@ println("Keeping all suggestions (including rejected ones for display)")
if (response.suggestedList.isNotEmpty()) {
println("Saving ${response.suggestedList.size} new suggestions")
// 获取现有的 pending/checked 状态的 suggestions(可以更新的)
// 获取现有的 pending/checked 状态的 suggestions(可以更新的)
val existingUpdatableSuggestions = suggestionsToKeep
.filter { it.suggestedLotLine?.id != null }
.groupBy { it.pickOrderLine?.id to it.suggestedLotLine?.id }
.mapValues { it.value.first() } // 每个 (lineId, lotId) 只取第一个

// 处理新的 suggestions:更新现有的或创建新的
// 处理新的 suggestions:更新现有的或创建新的
val suggestionsToSave = response.suggestedList.mapNotNull { newSugg ->
val key = newSugg.pickOrderLine?.id to newSugg.suggestedLotLine?.id
val lineId = newSugg.pickOrderLine?.id
val lotId = newSugg.suggestedLotLine?.id

if (lineId != null && lotId != null) {
// 检查这个 lot 是否已有 suggestion
// 检查这个 lot 是否已有 suggestion
val existingSugg = existingUpdatableSuggestions[key]
if (existingSugg != null) {
// 检查现有 suggestion 的 stock_out_line 状态
// 检查现有 suggestion 的 stock_out_line 状态
val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
lineId, lotId
)
@@ -560,23 +560,23 @@ println("Keeping all suggestions (including rejected ones for display)")
}
if (canUpdate) {
// Case 1: 更新现有的 suggestion
// Case 1: 更新现有的 suggestion
existingSugg.qty = newSugg.qty
existingSugg.modified = LocalDateTime.now()
existingSugg.modifiedBy = "system"
println("⚠️ Updated existing suggestion ${existingSugg.id} for lot $lotId: new qty=${newSugg.qty}")
existingSugg
} else {
// Case 2: 已完成/拒绝,跳过(不更新,也不创建新的)
// Case 2: 已完成/拒绝,跳过(不更新,也不创建新的)
println("⏭️ Skipping lot $lotId - already ${stockOutLines.first().status}")
null
}
} else {
// 没有现有的 suggestion,创建新的
// 没有现有的 suggestion,创建新的
newSugg
}
} else if (lotId == null) {
// lotId=null:检查是否已有 resuggest_issue
// lotId=null:检查是否已有 resuggest_issue
val existingResuggestIssues = pickExecutionIssueRepository
.findByPickOrderLineIdAndDeletedFalse(lineId ?: 0L)
.filter { it.issueCategory.name == "resuggest_issue" }
@@ -601,20 +601,20 @@ println("Keeping all suggestions (including rejected ones for display)")
if (updatedSuggestions.isNotEmpty()) {
val savedUpdated = suggestedPickLotRepository.saveAllAndFlush(updatedSuggestions)
allSavedSuggestions.addAll(savedUpdated)
println(" Updated ${savedUpdated.size} existing suggestions")
println(" Updated ${savedUpdated.size} existing suggestions")
}

// 保存新的 suggestions
if (newSuggestions.isNotEmpty()) {
val savedNew = suggestedPickLotRepository.saveAllAndFlush(newSuggestions)
allSavedSuggestions.addAll(savedNew)
println(" Created ${savedNew.size} new suggestions")
println(" Created ${savedNew.size} new suggestions")
}

val savedSuggestions = allSavedSuggestions
println("Saved/Updated ${savedSuggestions.size} suggestions")
// 为每个新 suggestion 创建 stock out line 或 issue
// 为每个新 suggestion 创建 stock out line 或 issue
savedSuggestions.forEach { suggestion ->
if (suggestion.suggestedLotLine != null) {
val isNewSuggestion = response.suggestedList.any {
@@ -624,10 +624,10 @@ println("Keeping all suggestions (including rejected ones for display)")
}
val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest)
if (stockOutLine != null) {
println(" Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}")
println(" Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}")
}
} else {
// 如果 lot 是 null,表示没有可用的 lot,创建 resuggest_issue
// 如果 lot 是 null,表示没有可用的 lot,创建 resuggest_issue
println("❌ No available lot for pick order line ${suggestion.pickOrderLine?.id}, creating resuggest_issue")
val pickOrderLine = suggestion.pickOrderLine
@@ -637,7 +637,7 @@ println("Keeping all suggestions (including rejected ones for display)")
.findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!)
.filter { it.status == "rejected" }
println("Rejected stock out lines: ${rejectedStockOutLines.size}")
// 修复:只创建一个 resuggest_issue(如果有 rejected lines)
// 修复:只创建一个 resuggest_issue(如果有 rejected lines)
if (rejectedStockOutLines.isNotEmpty()) {
println("Creating resuggest failure issue")
createResuggestFailureIssue(
@@ -653,7 +653,7 @@ println("Keeping all suggestions (including rejected ones for display)")
}
}
// 更新 holdQty
// 更新 holdQty
response.holdQtyMap.forEach { (lotId, newHoldQty) ->
if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) {
val lot = inventoryLotLineRepository.findById(lotId).orElse(null)
@@ -670,7 +670,7 @@ println("Keeping all suggestions (including rejected ones for display)")
}
}
} else {
// 如果完全没有生成任何 suggestions
// 如果完全没有生成任何 suggestions
println("No suggestions generated at all for pick order: ${pickOrderToResuggest.code}")
problematicPickOrderLines.forEach { pickOrderLine ->
@@ -693,7 +693,7 @@ println("Keeping all suggestions (including rejected ones for display)")
}
}
}
// FIX: Update inventory table for each pick order
// FIX: Update inventory table for each pick order
allPickOrdersToResuggest.forEach { pickOrderToUpdate ->
println("=== Updating inventory table for pick order: ${pickOrderToUpdate.code} ===")
updateInventoryTableAfterResuggest(pickOrderToUpdate)
@@ -727,12 +727,12 @@ println("Keeping all suggestions (including rejected ones for display)")
private fun createResuggestFailureIssue(
pickOrder: PickOrder,
pickOrderLine: PickOrderLine,
rejectedStockOutLine: StockOutLineInfo // 使用 StockOutLineInfo
rejectedStockOutLine: StockOutLineInfo // 使用 StockOutLineInfo
) {
try {
val item = pickOrderLine.item
// 从 StockOutLineInfo 获取 inventoryLotLineId
// 从 StockOutLineInfo 获取 inventoryLotLineId
val inventoryLotLineId = rejectedStockOutLine.inventoryLotLineId
val inventoryLotLine = if (inventoryLotLineId != null) {
inventoryLotLineRepository.findById(inventoryLotLineId).orElse(null)
@@ -758,8 +758,8 @@ println("Keeping all suggestions (including rejected ones for display)")
lotNo = inventoryLotLine?.inventoryLot?.lotNo,
storeLocation = inventoryLotLine?.warehouse?.name,
requiredQty = pickOrderLine.qty,
actualPickQty = rejectedStockOutLine.qty ?: BigDecimal.ZERO, // 直接使用,不需要 toBigDecimal()
missQty = (pickOrderLine.qty ?: BigDecimal.ZERO).minus(rejectedStockOutLine.qty ?: BigDecimal.ZERO), // 直接使用
actualPickQty = rejectedStockOutLine.qty ?: BigDecimal.ZERO, // 直接使用,不需要 toBigDecimal()
missQty = (pickOrderLine.qty ?: BigDecimal.ZERO).minus(rejectedStockOutLine.qty ?: BigDecimal.ZERO), // 直接使用
badItemQty = BigDecimal.ZERO,
issueRemark = "Resuggest failed: No alternative lots available for rejected lot ${inventoryLotLine?.inventoryLot?.lotNo}",
pickerName = null,
@@ -775,7 +775,7 @@ println("Keeping all suggestions (including rejected ones for display)")
)
pickExecutionIssueRepository.save(issue)
println(" Created resuggest_issue: ${issue.issueNo} for pick order ${pickOrder.code}")
println(" Created resuggest_issue: ${issue.issueNo} for pick order ${pickOrder.code}")
} catch (e: Exception) {
println("❌ Error creating resuggest_issue: ${e.message}")
@@ -849,16 +849,16 @@ private fun generateOptimalSuggestionsForAllPickOrders(
.filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) }
.sortedBy { it.expiryDate }
// FIX: Get fresh lot data and reset holdQty to 0 for calculation
// FIX: Get fresh lot data and reset holdQty to 0 for calculation
val lotEntities = inventoryLotLineRepository.findAllByIdIn(availableLots.mapNotNull { it.id })
lotEntities.forEach { lot -> lot.holdQty = BigDecimal.ZERO }
// FIX: Calculate remaining quantity for each pick order line
// FIX: Calculate remaining quantity for each pick order line
// FIX: Calculate remaining quantity for each pick order line
// FIX: Calculate remaining quantity for each pick order line
val remainingQtyPerLine = pickOrderLines.associate { pol ->
val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!)
// FIX: Count picked qty from ALL statuses except 'rejected'
// FIX: Count picked qty from ALL statuses except 'rejected'
// This includes 'completed', 'pending', 'checked', 'partially_completed'
val totalPickedQty = stockOutLines
.sumOf { it.qty ?: BigDecimal.ZERO }
@@ -872,7 +872,7 @@ private fun generateOptimalSuggestionsForAllPickOrders(
pol.id to remainingQty
}.toMutableMap()
// FIX: Filter out pick order lines that don't need more qty
// FIX: Filter out pick order lines that don't need more qty
val remainingPickOrderLines = pickOrderLines.filter { pol ->
val remainingQty = remainingQtyPerLine[pol.id] ?: zero
remainingQty > zero
@@ -915,7 +915,7 @@ private fun generateOptimalSuggestionsForAllPickOrders(
}
}
// FIX: Create insufficient stock suggestions for remaining quantities
// FIX: Create insufficient stock suggestions for remaining quantities
remainingQtyPerLine.forEach { (lineId, remainingQty) ->
if (remainingQty > zero) {
val pickOrderLine = pickOrderLines.find { it.id == lineId }
@@ -939,11 +939,11 @@ private fun updateInventoryTableAfterResuggest(pickOrder: PickOrder) {
val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct()
itemIds.forEach { itemId ->
// FIX: Calculate onHoldQty for ALL pick orders that use this item, not just the current one
// FIX: Calculate onHoldQty for ALL pick orders that use this item, not just the current one
val onHoldQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE)
.sumOf { it.holdQty ?: BigDecimal.ZERO }
// FIX: Use enum method instead of string method
// FIX: Use enum method instead of string method
val unavailableQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.UNAVAILABLE)
.sumOf {
val inQty = it.inQty ?: BigDecimal.ZERO
@@ -1110,17 +1110,17 @@ private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): L
val salesUnit = itemUomService.findSalesUnitByItemId(itemId)
val ratio = one
// FIX: Get ALL inventory lots (both available and unavailable)
// FIX: Get ALL inventory lots (both available and unavailable)
val allLots = inventoryLotLineService
.allInventoryLotLinesByItemIdIn(listOf(itemId))
.filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) }
.sortedBy { it.expiryDate }
// FIX: Separate available and unavailable lots
// FIX: Separate available and unavailable lots
val availableLots = allLots.filter { it.status == InventoryLotLineStatus.AVAILABLE.value }
val unavailableLots = allLots.filter { it.status == InventoryLotLineStatus.UNAVAILABLE.value }
// FIX: Calculate total quantity that was previously held by unavailable lots
// FIX: Calculate total quantity that was previously held by unavailable lots
var totalUnavailableHoldQty = BigDecimal.ZERO
val modifiedUnavailableLots = mutableListOf<InventoryLotLine>()
@@ -1128,19 +1128,19 @@ private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): L
val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) }
lot?.let {
totalUnavailableHoldQty = totalUnavailableHoldQty.plus(it.holdQty ?: zero)
// Reset holdQty for unavailable lots
// Reset holdQty for unavailable lots
it.holdQty = BigDecimal.ZERO
modifiedUnavailableLots.add(it) // Keep reference to modified entity
modifiedUnavailableLots.add(it) // Keep reference to modified entity
}
}
// FIX: Save the modified entities (not fresh ones from database)
// FIX: Save the modified entities (not fresh ones from database)
if (modifiedUnavailableLots.isNotEmpty()) {
inventoryLotLineRepository.saveAll(modifiedUnavailableLots)
println("Reset holdQty for ${modifiedUnavailableLots.size} unavailable lots")
}
// FIX: Add the unavailable hold quantity to the required quantity
// FIX: Add the unavailable hold quantity to the required quantity
//val totalRequiredQty = requiredQty.plus(totalUnavailableHoldQty.divide(ratio, 2, RoundingMode.HALF_UP))
val totalRequiredQty = requiredQty
var remainingQtyInSalesUnits = totalRequiredQty
@@ -1222,7 +1222,7 @@ private fun generateCorrectSuggestionsWithOriginalHolds(
.filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) }
.sortedBy { it.expiryDate }
// Calculate total quantity that needs to be redistributed
// Calculate total quantity that needs to be redistributed
var totalRedistributeQty = requiredQty
originalHoldQtyMap.forEach { (lotId, originalHoldQty) ->
if (originalHoldQty > zero) {
@@ -1478,7 +1478,7 @@ open fun updateSuggestedLotLineId(suggestedPickLotId: Long, newLotLineId: Long):
suggestedPickLot.suggestedLotLine = newInventoryLotLine
val savedSuggestedPickLot = suggestedPickLotRepository.save(suggestedPickLot)

println(" Successfully updated suggested pick lot ${suggestedPickLotId} to use lot line ${newLotLineId}")
println(" Successfully updated suggested pick lot ${suggestedPickLotId} to use lot line ${newLotLineId}")

return MessageResponse(
id = savedSuggestedPickLot.id,
@@ -1509,7 +1509,7 @@ private fun createInsufficientStockIssue(
insufficientQty: BigDecimal
) {
try {
// 检查是否已存在相同的 issue(避免重复创建)
// 检查是否已存在相同的 issue(避免重复创建)
val existingIssues = pickExecutionIssueRepository
.findByPickOrderLineIdAndDeletedFalse(pickOrderLine.id ?: 0L)
.filter {
@@ -1557,7 +1557,7 @@ private fun createInsufficientStockIssue(
)
pickExecutionIssueRepository.save(issue)
println(" Auto-created issue ${issue.issueNo} for insufficient stock (line ${pickOrderLine.id}, qty ${insufficientQty})")
println(" Auto-created issue ${issue.issueNo} for insufficient stock (line ${pickOrderLine.id}, qty ${insufficientQty})")
} catch (e: Exception) {
println("❌ Error creating insufficient stock issue: ${e.message}")


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt View File

@@ -89,7 +89,7 @@ class InventoryLotLineController (
try {
val result = inventoryLotLineService.updateInventoryLotLineStatus(request)
println(" Controller: Update successful - $result")
println(" Controller: Update successful - $result")
return result
} catch (e: Exception) {
println("❌ Controller: Update failed - ${e.message}")


Loading…
Cancel
Save