Browse Source

update

master
CANCERYS\kw093 2 months ago
parent
commit
0f87a52430
8 changed files with 345 additions and 180 deletions
  1. +2
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt
  2. +32
    -28
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  3. +14
    -4
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt
  4. +5
    -1
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt
  5. +49
    -49
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  6. +1
    -1
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/PickExecutionIssueRequest.kt
  7. +223
    -96
      src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt
  8. +19
    -0
      src/main/resources/db/changelog/changes/20251009_02_enson/03_altertable_enson.sql

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

@@ -39,7 +39,7 @@ import com.ffii.fpsms.modules.master.service.PrinterService
import java.time.format.DateTimeFormatter
import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository
import java.time.LocalTime
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.StockOutRepository
import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository
@@ -69,6 +69,7 @@ import com.ffii.fpsms.modules.pickOrder.entity.Router
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 // ✅ 添加这行

@Service
open class DeliveryOrderService(


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

@@ -23,6 +23,8 @@ import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository
import java.math.BigDecimal
import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus
import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory

@Service
open class JoPickOrderService(
private val joPickOrderRepository: JoPickOrderRepository,
@@ -1129,36 +1131,38 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse
// ✅ 生成 issueNo
val issueNo = generateIssueNoForJo()
// ✅ Create pick execution issue with complete data
// ✅ Create pick execution issue with complete data
val pickExecutionIssue = PickExecutionIssue(
issueNo = issueNo,
type = request.type ?: "match",
pickOrderId = request.pickOrderId,
pickOrderCode = pickOrderCode ?: "P-${request.pickOrderId}",
pickOrderCreateDate = pickOrderCreateDate, // ✅ 现在已经定义了
pickExecutionDate = LocalDate.now(),
pickOrderLineId = pickOrderLineId,
itemId = request.itemId,
itemCode = itemCode,
itemDescription = itemName,
lotId = lotId,
lotNo = lotNo,
storeLocation = storeLocation,
requiredQty = requiredQty,
actualPickQty = request.qty.toBigDecimal(),
missQty = request.missQty.toBigDecimal(), // ✅ 使用实际的 missQty
badItemQty = request.badItemQty.toBigDecimal(), // ✅ 使用实际的 badItemQty
issueRemark = request.reason,
pickerName = pickerName,
handleStatus = HandleStatus.pending,
handleDate = null,
handledBy = null,
created = LocalDateTime.now(),
createdBy = request.createdBy.toString(),
version = 0,
modified = LocalDateTime.now(),
modifiedBy = null,
deleted = false
id = null, // ✅ 1
pickOrderId = request.pickOrderId, // ✅ 2
pickOrderCode = pickOrderCode ?: "P-${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)


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

@@ -25,14 +25,19 @@ class PickExecutionIssue(
@Column(name = "pick_execution_date")
val pickExecutionDate: LocalDate? = null,

@Column(name = "Jo_pick_order_id")
val joPickOrderId: Long? = null,

@Column(name = "Do_pick_order_id")
val doPickOrderId: Long? = null,
@Column(name = "pick_order_line_id", nullable = false)
val pickOrderLineId: Long,
@Column(name = "issue_no", length = 50)
val issueNo: String? = null,

// ✅ 新增字段:type
@Column(name = "type", length = 50)
val type: String? = null,
@Enumerated(EnumType.STRING)
@Column(name = "issue_category")
val issueCategory: IssueCategory = IssueCategory.lot_issue,
@Column(name = "item_id", nullable = false)
val itemId: Long,

@@ -110,7 +115,7 @@ class PickExecutionIssue(
itemDescription = null,
lotId = null,
issueNo = null,
type = null,
issueCategory = IssueCategory.lot_issue,
lotNo = null,
storeLocation = null,
requiredQty = null,
@@ -135,4 +140,9 @@ enum class HandleStatus {
pending,
sort_and_repair,
dispose
}
enum class IssueCategory {
lot_issue, // 正常 Issue Button 提交
resuggest_issue, // Resuggest 失败
match_issue // Matching 问题
}

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

@@ -5,6 +5,8 @@ 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.HandleStatus
import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
@@ -51,7 +53,9 @@ open class PickExecutionIssueService(
// 2. 创建 pick execution issue 记录
val pickExecutionIssue = PickExecutionIssue(
issueNo = generateIssueNo(),
type = request.type,
issueCategory = IssueCategory.valueOf(
request.issueCategory ?: "lot_issue"
),
pickOrderId = request.pickOrderId,
pickOrderCode = request.pickOrderCode,
pickOrderCreateDate = request.pickOrderCreateDate,


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

@@ -1843,43 +1843,43 @@ open fun autoAssignAndReleasePickOrderByStoreAndTicket(storeId: String, ticketNo
val pickOrderIdsStr = pickOrderIds.joinToString(",")
val sql = """
SELECT
-- Pick Order Information
po.id as pickOrderId,
po.code as pickOrderCode,
SELECT
-- Pick Order Information
po.id as pickOrderId,
po.code as pickOrderCode,
po.consoCode as pickOrderConsoCode,
po.targetDate as pickOrderTargetDate,
po.type as pickOrderType,
po.status as pickOrderStatus,
po.assignTo as pickOrderAssignTo,
-- Pick Order Line Information
pol.id as pickOrderLineId,
pol.qty as pickOrderLineRequiredQty,
po.targetDate as pickOrderTargetDate,
po.type as pickOrderType,
po.status as pickOrderStatus,
po.assignTo as pickOrderAssignTo,
-- Pick Order Line Information
pol.id as pickOrderLineId,
pol.qty as pickOrderLineRequiredQty,
pol.status as pickOrderLineStatus,
-- Item Information
i.id as itemId,
i.code as itemCode,
i.name as itemName,
uc.code as uomCode,
uc.udfudesc as uomDesc,
-- ✅ Calculate total picked quantity from stock_out_line table
COALESCE((
SELECT SUM(sol_picked.qty)
FROM fpsmsdb.stock_out_line sol_picked
WHERE sol_picked.pickOrderLineId = pol.id
AND sol_picked.deleted = false
AND sol_picked.status IN ('completed', 'COMPLETE', 'partially_completed','rejected')
), 0) as totalPickedQty,
-- ✅ Calculate available quantity from inventory
COALESCE((
SELECT inv.onHandQty - inv.onHoldQty - inv.unavailableQty
FROM fpsmsdb.inventory inv
WHERE inv.itemId = i.id
AND inv.deleted = false
-- Item Information
i.id as itemId,
i.code as itemCode,
i.name as itemName,
uc.code as uomCode,
uc.udfudesc as uomDesc,
-- ✅ Calculate total picked quantity from stock_out_line table
COALESCE((
SELECT SUM(sol_picked.qty)
FROM fpsmsdb.stock_out_line sol_picked
WHERE sol_picked.pickOrderLineId = pol.id
AND sol_picked.deleted = false
AND sol_picked.status IN ('completed', 'COMPLETE', 'partially_completed','rejected')
), 0) as totalPickedQty,
-- ✅ Calculate available quantity from inventory
COALESCE((
SELECT inv.onHandQty - inv.onHoldQty - inv.unavailableQty
FROM fpsmsdb.inventory inv
WHERE inv.itemId = i.id
AND inv.deleted = false
), 0) as availableQty,
-- ✅ Check if all stock out lines for this pick order line are completed
@@ -1899,15 +1899,15 @@ open fun autoAssignAndReleasePickOrderByStoreAndTicket(storeId: String, ticketNo
) THEN true
ELSE false
END as allLotsCompleted
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
WHERE po.deleted = false
AND po.id IN ($pickOrderIdsStr)
AND pol.deleted = false
AND po.status = 'RELEASED'
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
WHERE po.deleted = false
AND po.id IN ($pickOrderIdsStr)
AND pol.deleted = false
AND po.status = 'RELEASED'
AND po.type NOT IN ('do', 'job') -- ✅ 排除 do 和 job 类型
-- ✅ Only include lines that have incomplete stock out lines
AND (
@@ -1925,11 +1925,11 @@ open fun autoAssignAndReleasePickOrderByStoreAndTicket(storeId: String, ticketNo
AND sol_incomplete.status NOT IN ('completed', 'COMPLETE')
)
)
ORDER BY
po.code ASC,
i.code ASC
""".trimIndent()
ORDER BY
po.code ASC,
i.code ASC
""".trimIndent()
println("🔍 Executing optimized SQL: $sql")


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

@@ -16,7 +16,7 @@ data class PickExecutionIssueRequest(
val lotId: Long? = null,
val lotNo: String? = null,
val issueNo: String? = null,
val type: String? = null,
val issueCategory: String = "lot_issue",
val storeLocation: String? = null,
val requiredQty: BigDecimal? = null,
val actualPickQty: BigDecimal? = null,


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

@@ -2,8 +2,13 @@ package com.ffii.fpsms.modules.stock.service

import com.ffii.fpsms.modules.master.service.ItemUomService
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.IssueCategory
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
import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo
@@ -15,6 +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 kotlin.jvm.optionals.getOrDefault
import kotlin.jvm.optionals.getOrNull
import org.springframework.transaction.annotation.Transactional
@@ -29,6 +36,7 @@ import com.ffii.fpsms.modules.stock.entity.FailInventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.StockOutRepository
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus
import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo // ✅ 添加这行
@Service
open class SuggestedPickLotService(
val suggestedPickLotRepository: SuggestPickLotRepository,
@@ -37,6 +45,7 @@ open class SuggestedPickLotService(
val pickOrderLineRepository: PickOrderLineRepository,
val inventoryLotLineService: InventoryLotLineService,
val itemUomService: ItemUomService,
val pickExecutionIssueRepository: PickExecutionIssueRepository, // ✅ 添加逗号
val pickOrderRepository: PickOrderRepository,
val inventoryRepository: InventoryRepository,
val failInventoryLotLineRepository: FailInventoryLotLineRepository,
@@ -363,38 +372,49 @@ open class SuggestedPickLotService(
val allSuggestions = suggestedPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIds)
println("Found ${allSuggestions.size} existing suggestions")
// ✅ FIX: Separate suggestions to keep (those with rejected stock out lines) and delete
val suggestionsToKeep = allSuggestions.filter { suggestion ->
val pickOrderLineId = suggestion.pickOrderLine?.id
val suggestedLotLineId = suggestion.suggestedLotLine?.id
println("Checking suggestion ${suggestion.id}: pickOrderLineId=$pickOrderLineId, suggestedLotLineId=$suggestedLotLineId")
if (pickOrderLineId != null && suggestedLotLineId != null) {
val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
pickOrderLineId,
suggestedLotLineId
)
val hasRejectedStockOutLine = stockOutLines.any { it.status == "rejected" }
println(" Stock out lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}")
println(" Has rejected stock out line: $hasRejectedStockOutLine")
hasRejectedStockOutLine
} else {
println(" Missing pickOrderLineId or suggestedLotLineId")
false
}
}
val suggestionsToDelete = allSuggestions.filter { suggestion ->
!suggestionsToKeep.contains(suggestion)
}
// ✅ 删除第 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
if (pickOrderLineId != null && suggestedLotLineId != null) {
val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
pickOrderLineId,
suggestedLotLineId
)
// ✅ 保留没有 rejected stock out lines 的 suggestions
!stockOutLines.any { it.status == "rejected" }
} else {
true // 保留有问题的 suggestions 用于调试
}
}

// ✅ 只删除有 rejected stock out lines 的 suggestions
val suggestionsToDelete = allSuggestions.filter { suggestion ->
val pickOrderLineId = suggestion.pickOrderLine?.id
val suggestedLotLineId = suggestion.suggestedLotLine?.id
if (pickOrderLineId != null && suggestedLotLineId != null) {
val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
pickOrderLineId,
suggestedLotLineId
)
stockOutLines.any { it.status == "rejected" } // ✅ 只删除 rejected 的
} else {
false
}
}
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
val rejectedLotIds = suggestionsToKeep.mapNotNull { it.suggestedLotLine?.id }.distinct()
println("Rejected lot IDs: $rejectedLotIds")
val rejectedLotIds = suggestionsToDelete.mapNotNull { it.suggestedLotLine?.id }.distinct()
println("Rejected lot IDs (clearing holdQty only): $rejectedLotIds")
rejectedLotIds.forEach { lotId ->
val lot = inventoryLotLineRepository.findById(lotId).orElse(null)
@@ -407,29 +427,13 @@ open class SuggestedPickLotService(
}
// ✅ NEW: Reduce holdQty for lots that are no longer suggested
val deletedSuggestions = suggestionsToDelete
val deletedLotIds = deletedSuggestions.mapNotNull { it.suggestedLotLine?.id }.distinct()
println("Deleted lot IDs: $deletedLotIds")
deletedLotIds.forEach { lotId ->
val lot = inventoryLotLineRepository.findById(lotId).orElse(null)
lot?.let {
val originalHoldQty = it.holdQty ?: BigDecimal.ZERO
val deletedQty = deletedSuggestions
.filter { it.suggestedLotLine?.id == lotId }
.sumOf { it.qty ?: BigDecimal.ZERO }
val newHoldQty = originalHoldQty.minus(deletedQty)
it.holdQty = if (newHoldQty < BigDecimal.ZERO) BigDecimal.ZERO else newHoldQty
inventoryLotLineRepository.save(it)
println("Reduced holdQty for deleted lot ${lot.id}: $originalHoldQty - $deletedQty = ${it.holdQty}")
}
}
// ✅ FIX: Delete only the suggestions that should be deleted
if (suggestionsToDelete.isNotEmpty()) {
suggestedPickLotRepository.deleteAll(suggestionsToDelete)
println("Deleted ${suggestionsToDelete.size} suggestions")
}
// ✅ 不删除任何 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
val existingHoldQtyMap = mutableMapOf<Long?, BigDecimal?>()
@@ -462,61 +466,102 @@ open class SuggestedPickLotService(
println("Final existing holdQtyMap: $existingHoldQtyMap")
// ✅ FIX: Create new suggestions for all pick orders to resuggest
allPickOrdersToResuggest.forEach { pickOrderToResuggest ->
println("=== Creating new suggestions for pick order: ${pickOrderToResuggest.code} ===")
val request = SuggestedPickLotForPolRequest(
pickOrderLines = pickOrderToResuggest.pickOrderLines,
holdQtyMap = existingHoldQtyMap.toMutableMap() // ✅ Use existing holdQty
)
val response = suggestionForPickOrderLines(request)
println("Generated ${response.suggestedList.size} new suggestions")
response.suggestedList.forEach { suggestion ->
println(" - Suggestion: lotId=${suggestion.suggestedLotLine?.id}, qty=${suggestion.qty}")
allPickOrdersToResuggest.forEach { pickOrderToResuggest ->
// ✅ 只获取有 rejected stock out lines 的 pick order lines
val problematicPickOrderLines = pickOrderToResuggest.pickOrderLines.filter { pol ->
val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!)
stockOutLines.any { it.status == "rejected" }
}
// ✅ FIX: Save the suggestions to the database
if (response.suggestedList.isNotEmpty()) {
val savedSuggestions = suggestedPickLotRepository.saveAllAndFlush(response.suggestedList)
println("Saved ${savedSuggestions.size} new suggestions for pick order: ${pickOrderToResuggest.code}")
savedSuggestions.forEach { suggestion ->
if (suggestion.suggestedLotLine != null) {
val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest)
if (stockOutLine != null) {
println("✅ Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}")
if (problematicPickOrderLines.isNotEmpty()) {
println("=== Creating new suggestions for pick order: ${pickOrderToResuggest.code} ===")
// ✅ 调用 suggestionForPickOrderLines 生成新的 suggestions
val request = SuggestedPickLotForPolRequest(
pickOrderLines = problematicPickOrderLines,
holdQtyMap = existingHoldQtyMap.toMutableMap()
)
val response = suggestionForPickOrderLines(request)
println("Generated ${response.suggestedList.size} new suggestions")
response.suggestedList.forEach { suggestion ->
println(" - Suggestion: lotId=${suggestion.suggestedLotLine?.id}, qty=${suggestion.qty}")
}
if (response.suggestedList.isNotEmpty()) {
println("Saving ${response.suggestedList.size} new suggestions")
// ✅ 保存所有新生成的 suggestions
val savedSuggestions = suggestedPickLotRepository.saveAllAndFlush(response.suggestedList)
println("Saved ${savedSuggestions.size} new suggestions")
// ✅ 为每个新 suggestion 创建 stock out line 或 issue
savedSuggestions.forEach { suggestion ->
if (suggestion.suggestedLotLine != null) {
val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest)
if (stockOutLine != null) {
println("✅ Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}")
}
} else {
println("❌ Failed to create stock out line for suggestion ${suggestion.id}")
// ✅ 如果 lot 是 null,表示没有可用的 lot,创建 resuggest_issue
println("❌ No available lot for pick order line ${suggestion.pickOrderLine?.id}, creating resuggest_issue")
val pickOrderLine = suggestion.pickOrderLine
if (pickOrderLine != null) {
// 获取这个 line 的 rejected stock out lines
val rejectedStockOutLines = stockOutLIneRepository
.findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!)
.filter { it.status == "rejected" }
rejectedStockOutLines.forEach { rejectedLine ->
createResuggestFailureIssue(
pickOrder = pickOrderToResuggest,
pickOrderLine = pickOrderLine,
rejectedStockOutLine = rejectedLine
)
}
}
}
}
}
// ✅ FIX: Update holdQty for the lots that were suggested - CUMULATIVE
response.holdQtyMap.forEach { (lotId, newHoldQty) ->
if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) {
// ✅ 更新 holdQty
response.holdQtyMap.forEach { (lotId, newHoldQty) ->
if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) {
val lot = inventoryLotLineRepository.findById(lotId).orElse(null)
lot?.let {
val currentHoldQty = it.holdQty ?: BigDecimal.ZERO
val existingHoldQty = existingHoldQtyMap[lotId] ?: BigDecimal.ZERO
// ✅ FIX: Calculate the additional holdQty needed
val additionalHoldQty = newHoldQty.minus(existingHoldQty)
val finalHoldQty = currentHoldQty.plus(additionalHoldQty)
it.holdQty = finalHoldQty
inventoryLotLineRepository.save(it)
// ✅ Update the existingHoldQtyMap for next iteration
existingHoldQtyMap[lotId] = newHoldQty
println("Updated holdQty for lot $lotId: $currentHoldQty + $additionalHoldQty = $finalHoldQty")
val currentHoldQty = it.holdQty ?: BigDecimal.ZERO
val existingHoldQty = existingHoldQtyMap[lotId] ?: BigDecimal.ZERO
val additionalHoldQty = newHoldQty.minus(existingHoldQty)
val finalHoldQty = currentHoldQty.plus(additionalHoldQty)
it.holdQty = finalHoldQty
inventoryLotLineRepository.save(it)
existingHoldQtyMap[lotId] = newHoldQty
println("Updated holdQty for lot $lotId: $currentHoldQty + $additionalHoldQty = $finalHoldQty")
}
}
}
} else {
// ✅ 如果完全没有生成任何 suggestions
println("No suggestions generated at all for pick order: ${pickOrderToResuggest.code}")
problematicPickOrderLines.forEach { pickOrderLine ->
val rejectedStockOutLines = stockOutLIneRepository
.findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!)
.filter { it.status == "rejected" }
if (rejectedStockOutLines.isNotEmpty()) {
rejectedStockOutLines.forEach { rejectedLine ->
createResuggestFailureIssue(
pickOrder = pickOrderToResuggest,
pickOrderLine = pickOrderLine,
rejectedStockOutLine = rejectedLine
)
}
}
}
}
}
}
}
} else {
println("No suggestions generated for pick order: ${pickOrderToResuggest.code}")
}
}
// ✅ FIX: Update inventory table for each pick order
allPickOrdersToResuggest.forEach { pickOrderToUpdate ->
println("=== Updating inventory table for pick order: ${pickOrderToUpdate.code} ===")
@@ -546,6 +591,88 @@ allPickOrdersToResuggest.forEach { pickOrderToResuggest ->
)
}
}


private fun createResuggestFailureIssue(
pickOrder: PickOrder,
pickOrderLine: PickOrderLine,
rejectedStockOutLine: StockOutLineInfo // ✅ 使用 StockOutLineInfo
) {
try {
val item = pickOrderLine.item
// ✅ 从 StockOutLineInfo 获取 inventoryLotLineId
val inventoryLotLineId = rejectedStockOutLine.inventoryLotLineId
val inventoryLotLine = if (inventoryLotLineId != null) {
inventoryLotLineRepository.findById(inventoryLotLineId).orElse(null)
} else {
null
}
val issue = PickExecutionIssue(
id = null,
pickOrderId = pickOrder.id!!,
pickOrderCode = pickOrder.code!!,
pickOrderCreateDate = pickOrder.created?.toLocalDate(),
pickExecutionDate = LocalDate.now(),
pickOrderLineId = pickOrderLine.id!!,
issueNo = generateIssueNo(),
joPickOrderId=pickOrder.jobOrder?.id,
doPickOrderId=pickOrder.deliveryOrder?.id,
issueCategory = IssueCategory.resuggest_issue,
itemId = item?.id!!,
itemCode = item.code,
itemDescription = item.name,
lotId = inventoryLotLine?.id,
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), // ✅ 直接使用
badItemQty = BigDecimal.ZERO,
issueRemark = "Resuggest failed: No alternative lots available for rejected lot ${inventoryLotLine?.inventoryLot?.lotNo}",
pickerName = null,
handleStatus = HandleStatus.pending,
handleDate = null,
handledBy = null,
created = LocalDateTime.now(),
createdBy = "system",
version = 0,
modified = LocalDateTime.now(),
modifiedBy = "system",
deleted = false
)
pickExecutionIssueRepository.save(issue)
println("✅ Created resuggest_issue: ${issue.issueNo} for pick order ${pickOrder.code}")
} catch (e: Exception) {
println("❌ Error creating resuggest_issue: ${e.message}")
e.printStackTrace()
}
}
private fun generateIssueNo(): String {
val now = LocalDateTime.now()
val yearMonth = now.format(DateTimeFormatter.ofPattern("yyMM"))
// 查询当月最新的 issueNo
val latestIssueNo = pickExecutionIssueRepository.findLatestIssueNoByYearMonth(yearMonth)
// 计算下一个序列号
val nextSequence = if (latestIssueNo != null) {
val parts = latestIssueNo.split("-")
if (parts.size == 3) {
val currentSequence = parts[2].toIntOrNull() ?: 0
currentSequence + 1
} else {
1
}
} else {
1
}
return "SKO-${yearMonth}-${nextSequence.toString().padStart(3, '0')}"
}
private fun findAllSuggestionsForPickOrders(pickOrderIds: List<Long>): List<SuggestedPickLot> {
val allPickOrderLines = mutableListOf<PickOrderLine>()


+ 19
- 0
src/main/resources/db/changelog/changes/20251009_02_enson/03_altertable_enson.sql View File

@@ -0,0 +1,19 @@
--liquibase formatted sql

--changeset [enson]:update_pick_execution_issue_add_columns_and_modify_status

-- 1. 添加新列 issueNo
ALTER TABLE pick_execution_issue
ADD COLUMN issue_category ENUM(
'lot_issue',
'resuggest_issue',
'match_issue'
) NULL DEFAULT 'lot_issue' AFTER type;

ALTER TABLE pick_execution_issue
ADD COLUMN Do_pick_order_id INT NULL AFTER issue_category,
ADD COLUMN Jo_pick_order_id INT NULL AFTER Do_pick_order_id;

ALTER TABLE pick_execution_issue
DROP COLUMN type;

Loading…
Cancel
Save