Ver a proveniência

update

master
CANCERYS\kw093 há 2 meses
ascendente
cometimento
28e4d4c71e
2 ficheiros alterados com 280 adições e 17 eliminações
  1. +162
    -2
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt
  2. +118
    -15
      src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt

+ 162
- 2
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt Ver ficheiro

@@ -21,6 +21,16 @@ import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.stock.entity.StockOutRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
import com.ffii.fpsms.modules.deliveryOrder.service.DoPickOrderService
import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrderRepository
import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrderRecordRepository
import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus
import com.ffii.fpsms.modules.stock.web.model.StockOutStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus
@Service
open class PickExecutionIssueService(
private val pickExecutionIssueRepository: PickExecutionIssueRepository,
@@ -28,7 +38,14 @@ open class PickExecutionIssueService(
private val inventoryLotLineRepository: InventoryLotLineRepository,
private val inventoryRepository: InventoryRepository,
private val suggestedPickLotService: SuggestedPickLotService,
private val pickOrderRepository: PickOrderRepository
private val pickOrderRepository: PickOrderRepository,
private val jdbcDao: JdbcDao,
private val stockOutRepository: StockOutRepository,
private val pickOrderLineRepository: PickOrderLineRepository,
private val doPickOrderService: DoPickOrderService,
private val joPickOrderRepository: JoPickOrderRepository,
private val joPickOrderRecordRepository: JoPickOrderRecordRepository

) {

@Transactional(rollbackFor = [Exception::class])
@@ -137,7 +154,19 @@ open class PickExecutionIssueService(
}
}


val pickOrderForCompletion = pickOrderRepository.findById(request.pickOrderId).orElse(null)
val consoCode = pickOrderForCompletion?.consoCode
if (consoCode != null) {
println("🔍 Checking if pick order $consoCode should be completed after lot rejection...")
try {
checkAndCompletePickOrder(consoCode)
} catch (e: Exception) {
println("⚠️ Error checking pick order completion: ${e.message}")
}
}
return MessageResponse(
id = savedIssue.id,
name = "Pick execution issue recorded successfully",
@@ -185,6 +214,137 @@ open class PickExecutionIssueService(
// 格式化为 SKO-YYMM-001
return "SKO-${yearMonth}-${nextSequence.toString().padStart(3, '0')}"
}

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun checkAndCompletePickOrder(consoCode: String) {
println("=== DEBUG: checkAndCompletePickOrder ===")
println("consoCode: $consoCode")
// 1. 查找 StockOut
val stockOut = stockOutRepository.findByConsoPickOrderCode(consoCode).orElse(null)
if (stockOut == null) {
println("❌ No stock_out found for consoCode: $consoCode")
return
}
// 2. 查找所有相关的 stock out lines
val stockOutLinesSql = """
SELECT sol.*
FROM stock_out_line sol
JOIN pick_order_line pol ON pol.id = sol.pickOrderLineId
JOIN pick_order po ON po.id = pol.poId
WHERE po.consoCode = :consoCode
AND sol.deleted = false
""".trimIndent()
val stockOutLinesResult = jdbcDao.queryForList(stockOutLinesSql, mapOf("consoCode" to consoCode))
val stockOutLineIds = stockOutLinesResult.mapNotNull { row ->
when (val id = row["id"]) {
is Number -> id.toLong()
is String -> id.toLongOrNull()
else -> null
}
}
val stockOutLines = if (stockOutLineIds.isNotEmpty()) {
stockOutLineRepository.findAllById(stockOutLineIds)
} else {
emptyList()
}
println("Total stock out lines for consoCode $consoCode: ${stockOutLines.size}")
// 3. 检查是否有未完成的行
val unfinishedLines = stockOutLines.filter {
it.status != StockOutLineStatus.COMPLETE.status
&& it.status != StockOutLineStatus.REJECTED.status
}
println("📊 Stock out lines: ${stockOutLines.size}, Unfinished: ${unfinishedLines.size}")
// 4. 如果所有行都完成或被拒绝,则完成 pick order
if (unfinishedLines.isEmpty()) {
println("✅ All stock out lines completed or rejected, completing pick order...")
// 4.1 更新 StockOut 状态
stockOut.status = StockOutStatus.COMPLETE.status
stockOutRepository.saveAndFlush(stockOut)
// 4.2 更新所有 pick order lines 状态
val completedPickOrderLineIds = stockOutLines.mapNotNull { it.pickOrderLine?.id }
println("Completed pick order line IDs: $completedPickOrderLineIds")
if (completedPickOrderLineIds.isNotEmpty()) {
val pickOrderLines = pickOrderLineRepository.findAllById(completedPickOrderLineIds)
pickOrderLines.forEach { line ->
line.status = PickOrderLineStatus.COMPLETED
println("Updated pick order line ${line.id} to COMPLETED")
}
pickOrderLineRepository.saveAll(pickOrderLines)
println("✅ Updated ${pickOrderLines.size} pick order lines to COMPLETED status")
// 4.3 获取所有受影响的 pick orders
val pickOrderIds = pickOrderLines.mapNotNull { it.pickOrder?.id }.distinct()
println("Affected pick order IDs: $pickOrderIds")
// 4.4 检查每个 pick order 是否完全完成
pickOrderIds.forEach { pickOrderId ->
val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null)
if (pickOrder != null) {
// 检查这个 pick order 的所有行是否都完成了
val allLines = pickOrder.pickOrderLines
val completedLines = allLines.filter { it.status == PickOrderLineStatus.COMPLETED }
println("Pick order ${pickOrder.code}: ${completedLines.size}/${allLines.size} lines completed")
if (completedLines.size == allLines.size && allLines.isNotEmpty()) {
// 所有行都完成了,更新 pick order 状态
pickOrder.status = PickOrderStatus.COMPLETED
pickOrder.completeDate = LocalDateTime.now()
pickOrderRepository.save(pickOrder)
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}")
doPickOrderService.completeDoPickOrderRecordsForPickOrder(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}")
}
// 4.6 处理 JO pick order 相关记录
if (pickOrder.jobOrder != null) {
try {
val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId)
joPickOrders.forEach {
it.ticketCompleteTime = LocalDateTime.now()
}
joPickOrderRepository.saveAll(joPickOrders)
val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId)
joPickOrderRecords.forEach {
it.ticketCompleteTime = LocalDateTime.now()
}
joPickOrderRecordRepository.saveAll(joPickOrderRecords)
println("✅ Set jo_pick_order ticketCompleteTime for pick order ${pickOrderId}")
} catch (e: Exception) {
println("⚠️ Error updating JO pick order records: ${e.message}")
}
}
}
}
}
}
println("🎉 Pick order completed successfully!")
} else {
println("⏳ Still have ${unfinishedLines.size} unfinished lines, pick order not completed yet")
}
}
// FPSMS-backend/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt
// ✅ 修复:处理有部分拣货但有 miss item 的情况
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])


+ 118
- 15
src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt Ver ficheiro

@@ -172,7 +172,34 @@ open class SuggestedPickLotService(
qty = assignQtyInSalesUnits // ✅ 保存销售单位
}
}
// ✅ 修复:计算现有 suggestions 中 pending/checked 状态满足的数量
var existingSatisfiedQty = BigDecimal.ZERO

// 查询现有的 suggestions 用于这个 pick order line
val existingSuggestions = suggestedPickLotRepository.findAllByPickOrderLineId(line.id!!)
existingSuggestions.forEach { existingSugg ->
if (existingSugg.suggestedLotLine?.id != null) {
val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
line.id!!, existingSugg.suggestedLotLine?.id!!
)
val canCountAsSatisfied = stockOutLines.isEmpty() || stockOutLines.any {
it.status == "pending" || it.status == "checked" || it.status == "partially_completed"
}
if (canCountAsSatisfied) {
existingSatisfiedQty = existingSatisfiedQty.plus(existingSugg.qty ?: BigDecimal.ZERO)
}
}
}

// ✅ 调整 remainingQtyToAllocate,减去已经通过现有 suggestions 满足的数量
remainingQtyToAllocate = remainingQtyToAllocate.minus(existingSatisfiedQty)
println("Existing satisfied qty: $existingSatisfiedQty")
println("Adjusted remaining qty: $remainingQtyToAllocate")

// if still have remainingQty
println("remaining2 $remainingQtyToAllocate (sales units)")

// if still have remainingQty
println("remaining2 $remainingQtyToAllocate (sales units)")
if (remainingQtyToAllocate > zero) {
@@ -409,7 +436,7 @@ val suggestionsToDelete = allSuggestions.filter { suggestion ->
)
stockOutLines.any { it.status == "rejected" } // ✅ 只删除 rejected 的
} else {
false
suggestedLotLineId == null
}
}
@@ -430,14 +457,7 @@ val suggestionsToDelete = allSuggestions.filter { suggestion ->
println("Cleared holdQty for rejected lot ${lot.id}: $originalHoldQty -> 0")
}
}
// ✅ NEW: Reduce holdQty for lots that are no longer suggested
// ✅ 不删除任何 suggestions - 保留 rejected suggestions 用于显示
// if (suggestionsToDelete.isNotEmpty()) {
// suggestedPickLotRepository.deleteAll(suggestionsToDelete)
// println("Deleted ${suggestionsToDelete.size} suggestions")
// }

println("Keeping all suggestions (including rejected ones for display)")
// ✅ NEW: Build holdQtyMap with existing holdQty from other pick orders
@@ -496,13 +516,95 @@ println("Keeping all suggestions (including rejected ones for display)")
if (response.suggestedList.isNotEmpty()) {
println("Saving ${response.suggestedList.size} new suggestions")
// ✅ 保存所有新生成的 suggestions
val savedSuggestions = suggestedPickLotRepository.saveAllAndFlush(response.suggestedList)
println("Saved ${savedSuggestions.size} new 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:更新现有的或创建新的
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
val existingSugg = existingUpdatableSuggestions[key]
if (existingSugg != null) {
// ✅ 检查现有 suggestion 的 stock_out_line 状态
val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
lineId, lotId
)
val canUpdate = stockOutLines.isEmpty() || stockOutLines.all {
it.status == "pending" || it.status == "checked" || it.status == "partially_completed"
}
if (canUpdate) {
// ✅ 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: 已完成/拒绝,跳过(不更新,也不创建新的)
println("⏭️ Skipping lot $lotId - already ${stockOutLines.first().status}")
null
}
} else {
// ✅ 没有现有的 suggestion,创建新的
newSugg
}
} else if (lotId == null) {
// ✅ lotId=null:检查是否已有 resuggest_issue
val existingResuggestIssues = pickExecutionIssueRepository
.findByPickOrderLineIdAndDeletedFalse(lineId ?: 0L)
.filter { it.issueCategory.name == "resuggest_issue" }
if (existingResuggestIssues.isEmpty()) {
newSugg // 创建新的 null suggestion(后续会创建 issue)
} else {
println("⏭️ Resuggest issue already exists for line $lineId, skipping null suggestion")
null // 跳过,避免创建重复的 resuggest_issue
}
} else {
newSugg
}
}.filterNotNull()

val updatedSuggestions = suggestionsToSave.filter { it.id != null } // 有 id 的是更新的
val newSuggestions = suggestionsToSave.filter { it.id == null } // 没有 id 的是新创建的

val allSavedSuggestions = mutableListOf<SuggestedPickLot>()

// 保存更新的 suggestions
if (updatedSuggestions.isNotEmpty()) {
val savedUpdated = suggestedPickLotRepository.saveAllAndFlush(updatedSuggestions)
allSavedSuggestions.addAll(savedUpdated)
println("✅ Updated ${savedUpdated.size} existing suggestions")
}

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

val savedSuggestions = allSavedSuggestions
println("Saved/Updated ${savedSuggestions.size} suggestions")
// ✅ 为每个新 suggestion 创建 stock out line 或 issue
savedSuggestions.forEach { suggestion ->
if (suggestion.suggestedLotLine != null) {
val isNewSuggestion = response.suggestedList.any {
it.pickOrderLine?.id == suggestion.pickOrderLine?.id &&
it.suggestedLotLine?.id == suggestion.suggestedLotLine?.id &&
it.id == null // 新的 suggestion 还没有 id
}
val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest)
if (stockOutLine != null) {
println("✅ Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}")
@@ -518,11 +620,12 @@ println("Keeping all suggestions (including rejected ones for display)")
.findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!)
.filter { it.status == "rejected" }
rejectedStockOutLines.forEach { rejectedLine ->
// ✅ 修复:只创建一个 resuggest_issue(如果有 rejected lines)
if (rejectedStockOutLines.isNotEmpty()) {
createResuggestFailureIssue(
pickOrder = pickOrderToResuggest,
pickOrderLine = pickOrderLine,
rejectedStockOutLine = rejectedLine
rejectedStockOutLine = rejectedStockOutLines.first()
)
}
}


Carregando…
Cancelar
Guardar