浏览代码

update

master
CANCERYS\kw093 2 个月前
父节点
当前提交
45e63f4f0c
共有 7 个文件被更改,包括 755 次插入72 次删除
  1. +1
    -1
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrderRepository.kt
  2. +663
    -68
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  3. +58
    -2
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt
  4. +11
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanIssueRequest.kt
  5. +15
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanSubmitRequest.kt
  6. +4
    -1
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt
  7. +3
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt

+ 1
- 1
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrderRepository.kt 查看文件

@@ -15,5 +15,5 @@ import java.time.LocalDateTime
interface JoPickOrderRepository : JpaRepository<JoPickOrder, Long> {

fun findByPickOrderId(pickOrderId: Long): List<JoPickOrder>
fun findByPickOrderIdAndItemId(pickOrderId: Long, itemId: Long): java.util.Optional<JoPickOrder>
}

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

@@ -13,15 +13,25 @@ import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import java.time.LocalDateTime

import java.time.LocalDateTime
import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.jobOrder.web.model.SecondScanSubmitRequest
import java.util.Optional
import com.ffii.fpsms.modules.jobOrder.web.model.SecondScanIssueRequest
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
@Service
open class JoPickOrderService(
private val joPickOrderRepository: JoPickOrderRepository,
private val joPickOrderRecordRepository: JoPickOrderRecordRepository,
private val pickOrderService: PickOrderService,
private val userService: UserService,
private val pickOrderRepository: PickOrderRepository
private val pickOrderRepository: PickOrderRepository,
private val jdbcDao: JdbcDao,
private val pickExecutionIssueRepository: PickExecutionIssueRepository
) {
open fun save(record: JoPickOrder): JoPickOrder {
@@ -108,7 +118,6 @@ open class JoPickOrderService(
return joPickOrderRecordRepository.saveAll(joPickOrderRecords)
}

// ✅ Get all job order lots with details hierarchical
open fun getAllJobOrderLotsWithDetailsHierarchical(userId: Long): Map<String, Any?> {
println("=== Debug: getAllJobOrderLotsWithDetailsHierarchical ===")
println("today: ${LocalDate.now()}")
@@ -172,7 +181,7 @@ open class JoPickOrderService(
)
}
// Use the same SQL query but transform the results into hierarchical structure
// ✅ 修复:简化 SQL 查询,移除不存在的字段
val pickOrderIdsStr = pickOrderIds.joinToString(",")
val sql = """
@@ -189,7 +198,6 @@ open class JoPickOrderService(
-- Job Order Information
jo.id as jobOrderId,
jo.code as jobOrderCode,
jo.name as jobOrderName,
-- Pick Order Line Information
pol.id as pickOrderLineId,
@@ -311,87 +319,361 @@ open class JoPickOrderService(
println("🔍 Executing SQL for job order hierarchical structure: $sql")
println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr")
// ✅ 修复:使用 PickOrderService 的现有方法
val results = pickOrderService.getAllPickOrderLotsWithDetailsHierarchical(userId)
// 如果结果为空,返回空结构
if (results.isEmpty()) {
// ✅ 修复:直接执行 SQL 查询并构建分层结构
try {
val results = jdbcDao.queryForList(sql, mapOf("userId" to userId))
if (results.isEmpty()) {
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
// 构建分层结构
val pickOrderInfo = results.firstOrNull()?.let { firstRow: Map<String, Any?> ->
mapOf(
"id" to firstRow["pickOrderId"],
"code" to firstRow["pickOrderCode"],
"consoCode" to firstRow["pickOrderConsoCode"],
"targetDate" to firstRow["pickOrderTargetDate"],
"type" to firstRow["pickOrderType"],
"status" to firstRow["pickOrderStatus"],
"assignTo" to firstRow["pickOrderAssignTo"],
"jobOrder" to mapOf(
"id" to firstRow["jobOrderId"],
"code" to firstRow["jobOrderCode"],
"name" to "Job Order ${firstRow["jobOrderCode"]}" // ✅ 使用生成的名称
)
)
}
// 按 pick order line 分组
val groupedByLine = results.groupBy { row: Map<String, Any?> -> row["pickOrderLineId"] }
val pickOrderLines = groupedByLine.map { (lineId: Any?, lineRows: List<Map<String, Any?>>) ->
val firstLineRow = lineRows.first()
mapOf(
"id" to lineId,
"itemId" to firstLineRow["itemId"],
"itemCode" to firstLineRow["itemCode"],
"itemName" to firstLineRow["itemName"],
"requiredQty" to firstLineRow["pickOrderLineRequiredQty"],
"uomCode" to firstLineRow["uomCode"],
"uomDesc" to firstLineRow["uomDesc"],
"lots" to lineRows.map { row: Map<String, Any?> ->
mapOf(
"lotId" to row["lotId"],
"lotNo" to row["lotNo"],
"expiryDate" to row["expiryDate"],
"location" to row["location"],
"availableQty" to row["availableQty"],
"requiredQty" to row["requiredQty"],
"actualPickQty" to row["actualPickQty"],
"processingStatus" to row["processingStatus"],
"lotAvailability" to row["lotAvailability"],
"pickOrderId" to row["pickOrderId"],
"pickOrderCode" to row["pickOrderCode"],
"pickOrderConsoCode" to row["pickOrderConsoCode"],
"pickOrderLineId" to row["pickOrderLineId"],
"stockOutLineId" to row["stockOutLineId"],
"stockOutLineStatus" to row["stockOutLineStatus"],
"routerIndex" to row["routerIndex"],
"routerArea" to row["routerArea"],
"routerRoute" to row["routerRoute"],
"uomShortDesc" to row["uomShortDesc"],
"secondQrScanStatus" to row["secondQrScanStatus"],
"secondQrScanBy" to row["secondQrScanBy"],
"secondQrScanQty" to row["secondQrScanQty"]
)
}
)
}
return mapOf(
"pickOrder" to pickOrderInfo as Any?,
"pickOrderLines" to pickOrderLines as Any?
)
} catch (e: Exception) {
println("❌ Error executing Job Order SQL: ${e.message}")
e.printStackTrace()
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
// 获取 pick order 信息
val pickOrderInfo = results["pickOrder"] as? Map<String, Any?>
val pickOrderLines = results["pickOrderLines"] as? List<Map<String, Any?>>
// 添加 Job Order 信息到 pick order info
val enhancedPickOrderInfo = pickOrderInfo?.toMutableMap() ?: mutableMapOf()
enhancedPickOrderInfo["jobOrder"] = mapOf(
"id" to pickOrderInfo?.get("id"),
"code" to "JO-${pickOrderInfo?.get("code")}",
"name" to "Job Order ${pickOrderInfo?.get("code")}"
)
return mapOf(
"pickOrder" to enhancedPickOrderInfo as Any?,
"pickOrderLines" to (pickOrderLines ?: emptyList()) as Any?
)
}

// Get completed job order pick orders (for second tab)
open fun getCompletedJobOrderPickOrders(userId: Long): List<Map<String, Any?>> {
println("=== getCompletedJobOrderPickOrders ===")
println("userId: $userId")
return try {
// Get completed pick orders for job orders
// ✅ Fix the getCompletedJobOrderLotsHierarchical method
open fun getCompletedJobOrderLotsHierarchical(userId: Long): Map<String, Any?> {
println("=== Debug: getCompletedJobOrderLotsHierarchical ===")
println("today: ${LocalDate.now()}")
println("userId filter: $userId")
// Get all COMPLETED pick orders assigned to the user with joId
val user = userService.find(userId).orElse(null)
if (user == null) {
println("❌ User not found: $userId")
return emptyMap()
}
// Get all COMPLETED pick orders assigned to user that have joId
val completedPickOrders = pickOrderRepository.findAllByAssignToIdAndStatusIn(
userId,
listOf(PickOrderStatus.COMPLETED)
).filter { it.jobOrder != null } // Only job order pick orders
).filter { it.jobOrder != null } // Only pick orders with joId

println("Found ${completedPickOrders.size} completed job order pick orders for user $userId")
println("🔍 DEBUG: Found ${completedPickOrders.size} completed job order pick orders assigned to user $userId")

val jobOrderPickOrders = completedPickOrders.mapNotNull { pickOrder ->
// Get job order details
val jobOrder = pickOrder.jobOrder
if (jobOrder != null) {
val visiblePickOrders = completedPickOrders.filter { pickOrder ->
val joPickOrders = findByPickOrderId(pickOrder.id!!)
val isHidden = joPickOrders.any { it.hide }
if (!isHidden) {
joPickOrders.none { it.hide } // Only show hide = false orders
}
if (visiblePickOrders.isEmpty()) {
println("🔍 DEBUG: No visible completed job orders found")
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
// For completed orders, show the latest one
val latestCompletedOrder = visiblePickOrders.maxByOrNull {
it.completeDate ?: it.modified ?: LocalDateTime.MIN
}
if (latestCompletedOrder == null) {
println("🔍 DEBUG: No latest completed order found")
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
println("🔍 DEBUG: Using latest completed order: ${latestCompletedOrder.code}")
// ✅ Use the same SQL query approach as getAllJobOrderLotsWithDetailsHierarchical
val pickOrderIds = listOf(latestCompletedOrder.id!!)
val pickOrderIdsStr = pickOrderIds.joinToString(",")
val sql = """
SELECT
-- Pick Order Information
po.id as pickOrderId,
po.code as pickOrderCode,
po.consoCode as pickOrderConsoCode,
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as pickOrderTargetDate,
'jo' as pickOrderType,
po.status as pickOrderStatus,
po.assignTo as pickOrderAssignTo,
-- Job Order Information
jo.id as jobOrderId,
jo.code as jobOrderCode,
-- 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,
uc.udfShortDesc as uomShortDesc,
-- Lot Information
ill.id as lotId,
il.lotNo,
DATE_FORMAT(il.expiryDate, '%Y-%m-%d') as expiryDate,
w.name as location,
COALESCE(uc.udfudesc, 'N/A') as stockUnit,
-- Router Information
r.id as routerId,
r.index as routerIndex,
r.route as routerRoute,
r.area as routerArea,
-- Set quantities to NULL for rejected lots
CASE
WHEN sol.status = 'rejected' THEN NULL
ELSE (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0))
END as availableQty,
-- Required quantity for this lot
COALESCE(spl.qty, 0) as requiredQty,
-- Actual picked quantity
COALESCE(sol.qty, 0) as actualPickQty,
-- Suggested pick lot information
spl.id as suggestedPickLotId,
ill.status as lotStatus,
-- Stock out line information
sol.id as stockOutLineId,
sol.status as stockOutLineStatus,
COALESCE(sol.qty, 0) as stockOutLineQty,
-- Additional detailed fields
COALESCE(ill.inQty, 0) as inQty,
COALESCE(ill.outQty, 0) as outQty,
COALESCE(ill.holdQty, 0) as holdQty,
COALESCE(spl.suggestedLotLineId, ill.id) as debugSuggestedLotLineId,
ill.inventoryLotId as debugInventoryLotId,
-- Calculate total picked quantity by ALL pick orders for this lot
COALESCE((
SELECT SUM(sol_all.qty)
FROM fpsmsdb.stock_out_line sol_all
WHERE sol_all.inventoryLotLineId = ill.id
AND sol_all.deleted = false
AND sol_all.status IN ('pending', 'checked', 'partially_completed', 'completed')
), 0) as totalPickedByAllPickOrders,
-- Calculate remaining available quantity correctly
(COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) as remainingAfterAllPickOrders,
-- Lot availability status
CASE
WHEN (il.expiryDate IS NOT NULL AND il.expiryDate < CURDATE()) THEN 'expired'
WHEN sol.status = 'rejected' THEN 'rejected'
WHEN (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) <= 0 THEN 'insufficient_stock'
WHEN ill.status = 'unavailable' THEN 'status_unavailable'
ELSE 'available'
END as lotAvailability,
-- Processing status
CASE
WHEN sol.status = 'completed' THEN 'completed'
WHEN sol.status = 'rejected' THEN 'rejected'
WHEN sol.status = 'created' THEN 'pending'
ELSE 'pending'
END as processingStatus,
-- JoPickOrder second scan status
jpo.second_qr_scan_status as secondQrScanStatus,
jpo.second_qr_scan_by as secondQrScanBy,
jpo.second_qr_scan_qty as secondQrScanQty
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.job_order jo ON jo.id = po.joId
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
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.router r ON r.inventoryLotId = ill.id AND r.deleted = false
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
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
AND po.id IN ($pickOrderIdsStr)
AND pol.deleted = false
AND po.status = 'COMPLETED'
AND po.assignTo = :userId
AND ill.deleted = false
AND il.deleted = false
AND (spl.pickOrderLineId IS NOT NULL OR sol.pickOrderLineId IS NOT NULL)
AND jpo.second_qr_scan_status = 'pending' OR jpo.second_qr_scan_status = 'scanned'
ORDER BY
CASE WHEN sol.status = 'rejected' THEN 0 ELSE 1 END,
COALESCE(r.index, 0) ASC,
po.code ASC,
i.code ASC,
il.expiryDate ASC,
il.lotNo ASC
""".trimIndent()
println("🔍 Executing SQL for completed job order hierarchical structure: $sql")
println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr")
// ✅ Execute SQL query and build hierarchical structure
try {
val results = jdbcDao.queryForList(sql, mapOf("userId" to userId))
if (results.isEmpty()) {
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
// Build hierarchical structure
val pickOrderInfo = results.firstOrNull()?.let { firstRow: Map<String, Any?> ->
mapOf(
"pickOrderId" to pickOrder.id,
"pickOrderCode" to pickOrder.code,
"pickOrderConsoCode" to pickOrder.consoCode,
"pickOrderTargetDate" to pickOrder.targetDate,
"pickOrderStatus" to pickOrder.status,
"jobOrderId" to jobOrder.id,
"jobOrderCode" to jobOrder.code,
"jobOrderName" to jobOrder.bom?.name,
"reqQty" to jobOrder.reqQty,
"uom" to jobOrder.bom?.uom?.code,
"planStart" to jobOrder.planStart,
"planEnd" to jobOrder.planEnd,
"completeDate" to pickOrder.completeDate
)
} else {
println("❌ Pick order ${pickOrder.id} is hidden, skipping.")
null
}
} else {
println("❌ Pick order ${pickOrder.id} has no job order, skipping.")
null
"id" to firstRow["pickOrderId"],
"code" to firstRow["pickOrderCode"],
"consoCode" to firstRow["pickOrderConsoCode"],
"targetDate" to firstRow["pickOrderTargetDate"],
"type" to firstRow["pickOrderType"],
"status" to firstRow["pickOrderStatus"],
"assignTo" to firstRow["pickOrderAssignTo"],
"jobOrder" to mapOf(
"id" to firstRow["jobOrderId"],
"code" to firstRow["jobOrderCode"],
"name" to "Job Order ${firstRow["jobOrderCode"]}"
)
)
}
// Group by pick order line
val groupedByLine = results.groupBy { row: Map<String, Any?> -> row["pickOrderLineId"] }
val pickOrderLines = groupedByLine.map { (lineId: Any?, lineRows: List<Map<String, Any?>>) ->
val firstLineRow = lineRows.first()
mapOf(
"id" to lineId,
"itemId" to firstLineRow["itemId"],
"itemCode" to firstLineRow["itemCode"],
"itemName" to firstLineRow["itemName"],
"requiredQty" to firstLineRow["pickOrderLineRequiredQty"],
"uomCode" to firstLineRow["uomCode"],
"uomDesc" to firstLineRow["uomDesc"],
"lots" to lineRows.map { row: Map<String, Any?> ->
mapOf(
"lotId" to row["lotId"],
"lotNo" to row["lotNo"],
"expiryDate" to row["expiryDate"],
"location" to row["location"],
"availableQty" to row["availableQty"],
"requiredQty" to row["requiredQty"],
"actualPickQty" to row["actualPickQty"],
"processingStatus" to row["processingStatus"],
"lotAvailability" to row["lotAvailability"],
"pickOrderId" to row["pickOrderId"],
"pickOrderCode" to row["pickOrderCode"],
"pickOrderConsoCode" to row["pickOrderConsoCode"],
"pickOrderLineId" to row["pickOrderLineId"],
"stockOutLineId" to row["stockOutLineId"],
"stockOutLineStatus" to row["stockOutLineStatus"],
"routerIndex" to row["routerIndex"],
"routerArea" to row["routerArea"],
"routerRoute" to row["routerRoute"],
"uomShortDesc" to row["uomShortDesc"],
"secondQrScanStatus" to row["secondQrScanStatus"]
)
}
}

println("Returning ${jobOrderPickOrders.size} completed job order pick orders")
jobOrderPickOrders
)
}
return mapOf(
"pickOrder" to pickOrderInfo as Any?,
"pickOrderLines" to pickOrderLines as Any?
)
} catch (e: Exception) {
println("❌ Error in getCompletedJobOrderPickOrders: ${e.message}")
println("❌ Error executing completed Job Order SQL: ${e.message}")
e.printStackTrace()
emptyList()
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
}

@@ -444,4 +726,317 @@ open class JoPickOrderService(
emptyList()
}
}

// 修复 getUnassignedJobOrderPickOrders 方法
open fun getUnassignedJobOrderPickOrders(): List<Map<String, Any?>> {
println("=== getUnassignedJobOrderPickOrders ===")
return try {
// ✅ 修复:使用正确的 repository 方法
val unassignedPickOrders = pickOrderRepository.findAllByStatusAndAssignToIsNullAndDeletedFalse(
com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED
).filter { pickOrder ->
pickOrder.jobOrder != null // Only job order pick orders
}

println("Found ${unassignedPickOrders.size} unassigned job order pick orders")

val jobOrderPickOrders = unassignedPickOrders.mapNotNull { pickOrder ->
val jobOrder = pickOrder.jobOrder
if (jobOrder != null) {
mapOf(
"pickOrderId" to pickOrder.id,
"pickOrderCode" to pickOrder.code,
"pickOrderConsoCode" to pickOrder.consoCode,
"pickOrderTargetDate" to pickOrder.targetDate,
"pickOrderStatus" to pickOrder.status,
"jobOrderId" to jobOrder.id,
"jobOrderCode" to jobOrder.code,
"jobOrderName" to jobOrder.bom?.name,
"reqQty" to jobOrder.reqQty,
"uom" to jobOrder.bom?.uom?.code,
"planStart" to jobOrder.planStart,
"planEnd" to jobOrder.planEnd
)
} else {
println("❌ Pick order ${pickOrder.id} has no job order, skipping.")
null
}
}

println("Returning ${jobOrderPickOrders.size} unassigned job order pick orders")
jobOrderPickOrders
} catch (e: Exception) {
println("❌ Error in getUnassignedJobOrderPickOrders: ${e.message}")
e.printStackTrace()
emptyList()
}
}

// ✅ 修复:assignJobOrderPickOrderToUser 方法中的自引用问题
@Transactional
open fun assignJobOrderPickOrderToUser(pickOrderId: Long, userId: Long): MessageResponse {
println("=== assignJobOrderPickOrderToUser ===")
println("pickOrderId: $pickOrderId, userId: $userId")
return try {
val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null)
if (pickOrder == null) {
return MessageResponse(
id = null,
code = null,
name = null,
type = null,
message = "Pick order not found",
errorPosition = null
)
}
if (pickOrder.assignTo != null) {
return MessageResponse(
id = null,
code = null,
name = null,
type = null,
message = "Pick order is already assigned",
errorPosition = null
)
}
// Assign pick order to user
pickOrder.assignTo = userService.find(userId).orElse(null)
pickOrderRepository.save(pickOrder)
// ✅ 修复:使用 this 而不是 joPickOrderService
this.updateHandledByForPickOrder(pickOrderId, userId)
MessageResponse(
id = pickOrder.id,
code = pickOrder.code,
name = pickOrder.jobOrder?.bom?.name,
type = null,
message = "Successfully assigned",
errorPosition = null
)
} catch (e: Exception) {
println("❌ Error in assignJobOrderPickOrderToUser: ${e.message}")
e.printStackTrace()
MessageResponse(
id = null,
code = null,
name = null,
type = null,
message = "Error occurred: ${e.message}",
errorPosition = null
)
}
}
// ✅ Fix the updateSecondQrScanStatus method
open fun updateSecondQrScanStatus(pickOrderId: Long, itemId: Long): MessageResponse {
try {
println("=== Debug: updateSecondQrScanStatus ===")
println("pickOrderId: $pickOrderId, itemId: $itemId")
val joPickOrder = joPickOrderRepository.findByPickOrderIdAndItemId(pickOrderId, itemId)
if (joPickOrder.isEmpty) {
return MessageResponse(
id = null,
name = null,
code = "ERROR",
type = "NOT_FOUND",
message = "Job Order Pick Order not found for pickOrderId: $pickOrderId, itemId: $itemId",
errorPosition = null
)
}
val joPickOrderEntity = joPickOrder.get()
joPickOrderEntity.second_qr_scan_status = JoPickOrderStatus.scanned // ✅ Use enum instead of string
joPickOrderRepository.save(joPickOrderEntity)
return MessageResponse(
id = null,
name = null,
code = "SUCCESS",
type = "UPDATED",
message = "Second QR scan status updated to checked",
errorPosition = null
)
} catch (e: Exception) {
println("❌ Error updating second QR scan status: ${e.message}")
return MessageResponse(
id = null,
name = null,
code = "ERROR",
type = "EXCEPTION",
message = "Error updating second QR scan status: ${e.message}",
errorPosition = null
)
}
}

open fun submitSecondScanQty(request: SecondScanSubmitRequest): MessageResponse {
try {
println("=== Debug: submitSecondScanQty ===")
println("pickOrderId: ${request.pickOrderId}, itemId: ${request.itemId}, qty: ${request.qty}")
val joPickOrder = joPickOrderRepository.findByPickOrderIdAndItemId(request.pickOrderId, request.itemId)
if (joPickOrder.isEmpty) {
return MessageResponse(
id = null,
name = null,
code = "ERROR",
type = "NOT_FOUND",
message = "Job Order Pick Order not found for pickOrderId: ${request.pickOrderId}, itemId: ${request.itemId}",
errorPosition = null
)
}
val joPickOrderEntity = joPickOrder.get()
joPickOrderEntity.second_qr_scan_qty = request.qty.toInt()
// ✅ Always set status to completed when submitting quantity
joPickOrderEntity.second_qr_scan_status = JoPickOrderStatus.completed
joPickOrderRepository.save(joPickOrderEntity)
println("✅ Updated jo_pick_order: status=${joPickOrderEntity.second_qr_scan_status}, qty=${joPickOrderEntity.second_qr_scan_qty}")
return MessageResponse(
id = null,
name = null,
code = "SUCCESS",
type = "UPDATED",
message = "Second scan quantity submitted successfully",
errorPosition = null
)
} catch (e: Exception) {
println("❌ Error submitting second scan quantity: ${e.message}")
return MessageResponse(
id = null,
name = null,
code = "ERROR",
type = "EXCEPTION",
message = "Error submitting second scan quantity: ${e.message}",
errorPosition = null
)
}
}


// ✅ Fix the recordSecondScanIssue method
open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse {
try {
println("=== Debug: recordSecondScanIssue ===")
println("pickOrderId: ${request.pickOrderId}, itemId: ${request.itemId}")
val joPickOrder = joPickOrderRepository.findByPickOrderIdAndItemId(request.pickOrderId, request.itemId)
if (joPickOrder.isEmpty) {
return MessageResponse(
id = null,
name = null,
code = "ERROR",
type = "NOT_FOUND",
message = "Job Order Pick Order not found for pickOrderId: ${request.pickOrderId}, itemId: ${request.itemId}",
errorPosition = null
)
}
// ✅ Get pick order line details with item information
val pickOrderLineResults = jdbcDao.queryForList(
"""
SELECT
pol.id as pickOrderLineId,
i.code as itemCode,
i.name as itemName,
pol.qty as requiredQty
FROM fpsmsdb.pick_order_line pol
JOIN fpsmsdb.items i ON i.id = pol.itemId
WHERE pol.poId = :pickOrderId AND pol.itemId = :itemId AND pol.deleted = false
""",
mapOf(
"pickOrderId" to request.pickOrderId,
"itemId" to request.itemId
)
)
if (pickOrderLineResults.isEmpty()) {
return MessageResponse(
id = null,
name = null,
code = "ERROR",
type = "NOT_FOUND",
message = "Pick order line not found for pickOrderId: ${request.pickOrderId}, itemId: ${request.itemId}",
errorPosition = null
)
}
val lineData = pickOrderLineResults.first()
val pickOrderLineId = (lineData["pickOrderLineId"] as Number).toLong()
val itemCode = lineData["itemCode"] as String?
val itemName = lineData["itemName"] as String?
val requiredQty = lineData["requiredQty"]?.let {
BigDecimal(it.toString())
}

val joPickOrderEntity = joPickOrder.get()
joPickOrderEntity.second_qr_scan_status = JoPickOrderStatus.completed
joPickOrderEntity.second_qr_scan_qty = request.qty.toInt()
joPickOrderRepository.save(joPickOrderEntity)
// ✅ Create pick execution issue with complete data
val pickExecutionIssue = PickExecutionIssue(
pickOrderId = request.pickOrderId,
pickOrderCode = "JO-${request.pickOrderId}",
pickOrderLineId = pickOrderLineId,
itemId = request.itemId,
itemCode = itemCode, // ✅ Include item code
itemDescription = itemName, // ✅ Include item name
lotId = null,
lotNo = null,
storeLocation = null,
requiredQty = requiredQty, // ✅ Include required quantity
actualPickQty = request.qty.toBigDecimal(), // ✅ Include actual pick quantity
missQty = if (request.isMissing) request.qty.toBigDecimal() else BigDecimal.ZERO,
badItemQty = if (request.isBad) request.qty.toBigDecimal() else BigDecimal.ZERO,
issueRemark = request.reason,
pickerName = null,
handleStatus = HandleStatus.jopending,
handleDate = null,
handledBy = null,
created = LocalDateTime.now(),
createdBy = request.createdBy.toString(),
version = 0,
modified = LocalDateTime.now(),
modifiedBy = null,
deleted = false
)
pickExecutionIssueRepository.save(pickExecutionIssue)
return MessageResponse(
id = null,
name = null,
code = "SUCCESS",
type = "CREATED",
message = "Second scan issue recorded successfully",
errorPosition = null
)
} catch (e: Exception) {
println("❌ Error recording second scan issue: ${e.message}")
return MessageResponse(
id = null,
name = null,
code = "ERROR",
type = "EXCEPTION",
message = "Error recording second scan issue: ${e.message}",
errorPosition = null
)
}
}
}

+ 58
- 2
src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt 查看文件

@@ -19,6 +19,8 @@ import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import com.ffii.fpsms.modules.jobOrder.service.JoPickOrderService
import com.ffii.fpsms.modules.jobOrder.web.model.SecondScanSubmitRequest
import com.ffii.fpsms.modules.jobOrder.web.model.SecondScanIssueRequest
@RestController
@RequestMapping("/jo")
class JobOrderController(
@@ -65,12 +67,66 @@ class JobOrderController(
}

@GetMapping("/completed-job-order-pick-orders/{userId}")
fun getCompletedJobOrderPickOrders(@PathVariable userId: Long): List<Map<String, Any?>> {
return joPickOrderService.getCompletedJobOrderPickOrders(userId)
fun getCompletedJobOrderLotsHierarchical(@PathVariable userId: Long): Map<String, Any?> {
return joPickOrderService.getCompletedJobOrderLotsHierarchical(userId)
}

@GetMapping("/completed-job-order-pick-order-records/{userId}")
fun getCompletedJobOrderPickOrderRecords(@PathVariable userId: Long): List<Map<String, Any?>> {
return joPickOrderService.getCompletedJobOrderPickOrderRecords(userId)
}
@GetMapping("/unassigned-job-order-pick-orders")
fun getUnassignedJobOrderPickOrders(): List<Map<String, Any?>> {
return joPickOrderService.getUnassignedJobOrderPickOrders()
}

@PostMapping("/assign-job-order-pick-order/{pickOrderId}/{userId}")
fun assignJobOrderPickOrderToUser(
@PathVariable pickOrderId: Long,
@PathVariable userId: Long
): MessageResponse {
return joPickOrderService.assignJobOrderPickOrderToUser(pickOrderId, userId)
}

@PostMapping("/second-scan-qr/{pickOrderId}/{itemId}")
fun updateSecondQrScanStatus(
@PathVariable pickOrderId: Long,
@PathVariable itemId: Long
): MessageResponse {
return joPickOrderService.updateSecondQrScanStatus(pickOrderId, itemId)
}

@PostMapping("/second-scan-submit/{pickOrderId}/{itemId}")
fun submitSecondScanQuantity(
@PathVariable pickOrderId: Long,
@PathVariable itemId: Long,
@RequestBody data: Map<String, Any>
): MessageResponse {
val request = SecondScanSubmitRequest(
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
)
return joPickOrderService.submitSecondScanQty(request)
}
@PostMapping("/second-scan-issue/{pickOrderId}/{itemId}")
fun recordSecondScanIssue(
@PathVariable pickOrderId: Long,
@PathVariable itemId: Long,
@RequestBody data: Map<String, Any>
): MessageResponse {
val request = SecondScanIssueRequest(
pickOrderId = pickOrderId, // ✅ Use path variable
itemId = itemId, // ✅ Use path variable
qty = (data["qty"] as Number).toDouble(),
isMissing = data["isMissing"] as Boolean,
isBad = data["isBad"] as Boolean,
reason = data["reason"] as String,
createdBy = (data["createdBy"] as Number).toLong()
)
return joPickOrderService.recordSecondScanIssue(request)
}
}

+ 11
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanIssueRequest.kt 查看文件

@@ -0,0 +1,11 @@
package com.ffii.fpsms.modules.jobOrder.web.model

data class SecondScanIssueRequest(
val pickOrderId: Long,
val itemId: Long,
val qty: Double,
val isMissing: Boolean,
val isBad: Boolean,
val reason: String,
val createdBy: Long
)

+ 15
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanSubmitRequest.kt 查看文件

@@ -0,0 +1,15 @@
package com.ffii.fpsms.modules.jobOrder.web.model

import jakarta.validation.constraints.NotNull

data class SecondScanSubmitRequest(
@NotNull
val pickOrderId: Long, // ✅ Add missing property
@NotNull
val itemId: Long, // ✅ Add missing property
@NotNull
val qty: Double,
val isMissing: Boolean = false,
val isBad: Boolean = false,
val reason: String? = null
)

+ 4
- 1
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt 查看文件

@@ -127,5 +127,8 @@ class PickExecutionIssue(
enum class HandleStatus {
pending, // ✅ Change to lowercase to match database
handled, // ✅ Change to lowercase to match database
resolved // ✅ Change to lowercase to match database
resolved,
jopending,
johandled,
joresolved // ✅ Change to lowercase to match database
}

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

@@ -75,4 +75,7 @@ interface PickOrderRepository : AbstractRepository<PickOrder, Long> {

@Query("SELECT p FROM PickOrder p WHERE p.deliveryOrder.id = :deliveryOrderId AND p.deleted = false")
fun findByDeliveryOrderId(@Param("deliveryOrderId") deliveryOrderId: Long): List<PickOrder>

// 在 PickOrderRepository 中添加:
fun findAllByStatusAndAssignToIsNullAndDeletedFalse(status: PickOrderStatus): List<PickOrder>
}

正在加载...
取消
保存