瀏覽代碼

update jo edit and jo compelte record

master
CANCERYS\kw093 12 小時之前
父節點
當前提交
c404e6287c
共有 4 個文件被更改,包括 167 次插入1 次删除
  1. +8
    -1
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  2. +17
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt
  3. +136
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt
  4. +6
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickExecutionIssueController.kt

+ 8
- 1
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt 查看文件

@@ -1627,11 +1627,18 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
if (completedDate != null) {
val from = completedDate.atStartOfDay()
val toExclusive = completedDate.plusDays(1).atStartOfDay()
/*
pickOrderRepository.findAllCompletedWithJobOrderPlanEndOnDay(
PickOrderStatus.COMPLETED,
from,
toExclusive,
)
*/
pickOrderRepository.findAllCompletedWithJobOrderPlanStartOnDay(
PickOrderStatus.COMPLETED,
from,
toExclusive,
)
} else {
pickOrderRepository
.findAllByStatusIn(listOf(PickOrderStatus.COMPLETED))
@@ -1669,7 +1676,7 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
"${it.year}-${"%02d".format(it.monthValue)}-${"%02d".format(it.dayOfMonth)}"
},
"pickOrderStatus" to pickOrder.status,
"completedDate" to jobOrder.planEnd,
"completedDate" to jobOrder.planStart,
"jobOrderId" to jobOrder.id,
"jobOrderCode" to jobOrder.code,
"jobOrderName" to jobOrder.bom?.name,


+ 17
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt 查看文件

@@ -112,4 +112,21 @@ fun findAllCompletedWithJobOrderPlanEndOnDay(
@Param("planEndFrom") planEndFrom: LocalDateTime,
@Param("planEndToExclusive") planEndToExclusive: LocalDateTime,
): List<PickOrder>

@Query(
"""
SELECT po FROM PickOrder po
WHERE po.status = :status
AND po.deleted = false
AND po.jobOrder IS NOT NULL
AND po.jobOrder.planStart IS NOT NULL
AND po.jobOrder.planStart >= :planStartFrom
AND po.jobOrder.planStart < :planStartToExclusive
"""
)
fun findAllCompletedWithJobOrderPlanStartOnDay(
@Param("status") status: PickOrderStatus,
@Param("planStartFrom") planStartFrom: LocalDateTime,
@Param("planStartToExclusive") planStartToExclusive: LocalDateTime,
): List<PickOrder>
}

+ 136
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt 查看文件

@@ -401,6 +401,142 @@ open class PickExecutionIssueService(
)
}
}

/**
* Jo / 无异常量场景:不写入 pick_execution_issue、不更新 inventory_lot_line.issue_qty。
* 仅 (1) 按 actualPickQty - requiredQty 调整 holdQty;(5) 将对应 stock_out_line 标为 checked(不改 qty)。
* 可重复调用,避免 DUPLICATE。
*/
open fun applyPickHoldAndMarkSolChecked(request: PickExecutionIssueRequest): MessageResponse {
try {
println("=== applyPickHoldAndMarkSolChecked: START ===")
val missQty = request.missQty ?: BigDecimal.ZERO
val badItemQty = request.badItemQty ?: BigDecimal.ZERO
if (missQty.compareTo(BigDecimal.ZERO) > 0 || badItemQty.compareTo(BigDecimal.ZERO) > 0) {
return MessageResponse(
id = null,
name = "Invalid request for hold-only API",
code = "ERROR",
type = "pick_execution_adjustment",
message = "This endpoint accepts only actual pick adjustment (miss and bad quantities must be zero). Use /recordIssue for issues.",
errorPosition = null
)
}
if (request.lotId == null) {
return MessageResponse(
id = null,
name = "lotId required",
code = "ERROR",
type = "pick_execution_adjustment",
message = "inventory lot line id (lotId) is required",
errorPosition = null
)
}

val inventoryLotLine = inventoryLotLineRepository.findById(request.lotId).orElse(null)
val bookQty = if (inventoryLotLine != null) {
val inQty = inventoryLotLine.inQty ?: BigDecimal.ZERO
val outQty = inventoryLotLine.outQty ?: BigDecimal.ZERO
inQty.subtract(outQty)
} else {
BigDecimal.ZERO
}

val requiredQty = request.requiredQty ?: BigDecimal.ZERO
val actualPickQty = request.actualPickQty ?: BigDecimal.ZERO
val lotRemainAvailable = bookQty
val maxAllowed = requiredQty.add(lotRemainAvailable)

if (actualPickQty > maxAllowed) {
return MessageResponse(
id = null,
name = "Actual pick qty too large",
code = "ERROR",
type = "pick_execution_adjustment",
message = "Actual pick qty cannot exceed required qty plus lot remaining available.",
errorPosition = null
)
}

if (inventoryLotLine != null) {
val deltaHold = actualPickQty.subtract(requiredQty)
if (deltaHold.compareTo(BigDecimal.ZERO) != 0) {
val latestLotLine = inventoryLotLineRepository.findById(request.lotId).orElse(null)
?: throw IllegalArgumentException("Inventory lot line not found: ${request.lotId}")

val currentHold = latestLotLine.holdQty ?: BigDecimal.ZERO
val currentOut = latestLotLine.outQty ?: BigDecimal.ZERO
val currentIn = latestLotLine.inQty ?: BigDecimal.ZERO

val newHold = currentHold.add(deltaHold)
if (newHold < BigDecimal.ZERO) {
return MessageResponse(
id = null,
name = "Invalid hold quantity adjustment",
code = "ERROR",
type = "pick_execution_adjustment",
message = "Cannot adjust holdQty by $deltaHold. Current holdQty=$currentHold, requiredQty=$requiredQty, actualPickQty=$actualPickQty",
errorPosition = null
)
}

if (deltaHold > BigDecimal.ZERO) {
val remaining = currentIn.subtract(currentOut).subtract(currentHold)
if (deltaHold > remaining) {
return MessageResponse(
id = null,
name = "Insufficient remaining quantity",
code = "ERROR",
type = "pick_execution_adjustment",
message = "Cannot reserve additional $deltaHold. Remaining=$remaining (in=$currentIn, out=$currentOut, hold=$currentHold)",
errorPosition = null
)
}
}

latestLotLine.holdQty = newHold
latestLotLine.modified = LocalDateTime.now()
latestLotLine.modifiedBy = "system"
inventoryLotLineRepository.saveAndFlush(latestLotLine)
println("✅ [hold-only] Adjusted inventory_lot_line ${request.lotId} holdQty: $currentHold -> $newHold (delta=$deltaHold)")
}
}

val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
request.pickOrderLineId,
request.lotId
)
stockOutLines.forEach { sol ->
sol.status = "checked"
sol.modified = LocalDateTime.now()
sol.modifiedBy = "system"
stockOutLineRepository.save(sol)
}
stockOutLineRepository.flush()

println("=== applyPickHoldAndMarkSolChecked: SUCCESS (${stockOutLines.size} SOL checked) ===")
return MessageResponse(
id = stockOutLines.firstOrNull()?.id,
name = "Pick hold adjusted and lines marked checked",
code = "SUCCESS",
type = "pick_execution_adjustment",
message = "Pick hold adjusted and stock out lines marked checked",
errorPosition = null
)
} catch (e: Exception) {
println("=== applyPickHoldAndMarkSolChecked: ERROR === ${e.message}")
e.printStackTrace()
return MessageResponse(
id = null,
name = "Failed to apply pick hold adjustment",
code = "ERROR",
type = "pick_execution_adjustment",
message = "Error: ${e.message}",
errorPosition = null
)
}
}

private fun handleAllZeroMarkCompleted(request: PickExecutionIssueRequest) {
val stockOutLines = stockOutLineRepository
.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(


+ 6
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickExecutionIssueController.kt 查看文件

@@ -20,6 +20,12 @@ class PickExecutionIssueController(
return pickExecutionIssueService.recordPickExecutionIssue(request)
}

/** 无 miss/bad:仅调整 hold + SOL 标 checked,不写 pick_execution_issue(可重复提交)。 */
@PostMapping("/applyHoldAndChecked")
fun applyPickHoldAndMarkSolChecked(@RequestBody request: PickExecutionIssueRequest): MessageResponse {
return pickExecutionIssueService.applyPickHoldAndMarkSolChecked(request)
}

@GetMapping("/issues/pickOrder/{pickOrderId}")
fun getPickExecutionIssuesByPickOrder(@PathVariable pickOrderId: Long): List<PickExecutionIssue> {
return pickExecutionIssueService.getPickExecutionIssuesByPickOrder(pickOrderId)


Loading…
取消
儲存