| @@ -1032,73 +1032,125 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse | |||
| ) | |||
| } | |||
| // ✅ Get pick order line details with item information | |||
| val pickOrderLineResults = jdbcDao.queryForList( | |||
| // ✅ Get pick order and pick order line details with item and lot information | |||
| val pickOrderDetails = jdbcDao.queryForList( | |||
| """ | |||
| SELECT | |||
| po.code as pickOrderCode, | |||
| DATE_FORMAT(po.created, '%Y-%m-%d') as pickOrderCreateDate, | |||
| pol.id as pickOrderLineId, | |||
| i.code as itemCode, | |||
| i.name as itemName, | |||
| pol.qty as requiredQty | |||
| FROM fpsmsdb.pick_order_line pol | |||
| pol.qty as requiredQty, | |||
| u.name as pickerName | |||
| 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 | |||
| WHERE pol.poId = :pickOrderId AND pol.itemId = :itemId AND pol.deleted = false | |||
| LEFT JOIN fpsmsdb.user u ON u.id = :createdBy | |||
| WHERE po.id = :pickOrderId | |||
| AND pol.itemId = :itemId | |||
| AND pol.deleted = false | |||
| AND po.deleted = false | |||
| """, | |||
| mapOf( | |||
| "pickOrderId" to request.pickOrderId, | |||
| "itemId" to request.itemId | |||
| "itemId" to request.itemId, | |||
| "createdBy" to request.createdBy | |||
| ) | |||
| ) | |||
| if (pickOrderLineResults.isEmpty()) { | |||
| // ✅ 修复:使用正确的变量名 pickOrderDetails | |||
| if (pickOrderDetails.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}", | |||
| message = "Pick order 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 { | |||
| // ✅ 修复:使用正确的变量名 pickOrderDetails | |||
| val orderData = pickOrderDetails.first() | |||
| val pickOrderCode = orderData["pickOrderCode"] as String? | |||
| val pickOrderCreateDate = orderData["pickOrderCreateDate"]?.let { | |||
| LocalDate.parse(it.toString()) | |||
| } | |||
| val pickOrderLineId = (orderData["pickOrderLineId"] as Number).toLong() | |||
| val itemCode = orderData["itemCode"] as String? | |||
| val itemName = orderData["itemName"] as String? | |||
| val pickerName = orderData["pickerName"] as String? | |||
| val requiredQty = orderData["requiredQty"]?.let { | |||
| BigDecimal(it.toString()) | |||
| } | |||
| // ✅ 获取 suggested lot 信息(用于 lot_id, lot_no, store_location) | |||
| val lotResults = jdbcDao.queryForList( | |||
| """ | |||
| SELECT | |||
| ill.id as lotId, | |||
| il.lotNo as lotNo, | |||
| w.name as storeLocation | |||
| FROM fpsmsdb.suggested_pick_lot spl | |||
| JOIN fpsmsdb.inventory_lot_line ill ON spl.suggestedLotLineId = ill.id | |||
| JOIN fpsmsdb.inventory_lot il ON ill.inventoryLotId = il.id | |||
| LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId | |||
| WHERE spl.pickOrderLineId = :pickOrderLineId | |||
| AND spl.deleted = false | |||
| AND ill.deleted = false | |||
| LIMIT 1 | |||
| """, | |||
| mapOf("pickOrderLineId" to pickOrderLineId) | |||
| ) | |||
| val lotId = if (lotResults.isNotEmpty()) { | |||
| (lotResults.first()["lotId"] as Number?)?.toLong() | |||
| } else null | |||
| val lotNo = if (lotResults.isNotEmpty()) { | |||
| lotResults.first()["lotNo"] as String? | |||
| } else null | |||
| val storeLocation = if (lotResults.isNotEmpty()) { | |||
| lotResults.first()["storeLocation"] as String? | |||
| } else null | |||
| val joPickOrderEntity = joPickOrder.get() | |||
| joPickOrderEntity.matchStatus = JoPickOrderStatus.completed | |||
| joPickOrderEntity.matchQty = request.qty.toInt() | |||
| // ✅ 添加:如果 ticketCompleteTime 还没设置,现在设置 | |||
| if (joPickOrderEntity.ticketCompleteTime == null) { | |||
| joPickOrderEntity.ticketCompleteTime = LocalDateTime.now() | |||
| } | |||
| joPickOrderRepository.save(joPickOrderEntity) | |||
| // ✅ 生成 issueNo | |||
| val issueNo = generateIssueNoForJo() | |||
| // ✅ Create pick execution issue with complete data | |||
| val pickExecutionIssue = PickExecutionIssue( | |||
| issueNo = issueNo, | |||
| type = request.type ?: "match", | |||
| pickOrderId = request.pickOrderId, | |||
| pickOrderCode = "JO-${request.pickOrderId}", | |||
| pickOrderCode = pickOrderCode ?: "P-${request.pickOrderId}", | |||
| pickOrderCreateDate = pickOrderCreateDate, // ✅ 现在已经定义了 | |||
| pickExecutionDate = LocalDate.now(), | |||
| 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, | |||
| 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 = null, | |||
| handleStatus = HandleStatus.jopending, | |||
| pickerName = pickerName, | |||
| handleStatus = HandleStatus.pending, | |||
| handleDate = null, | |||
| handledBy = null, | |||
| created = LocalDateTime.now(), | |||
| @@ -1118,19 +1170,41 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse | |||
| message = "Second scan issue recorded successfully", | |||
| errorPosition = null | |||
| ) | |||
| } catch (e: Exception) { | |||
| println("❌ Error recording second scan issue: ${e.message}") | |||
| println("=== ERROR in recordSecondScanIssue ===") | |||
| e.printStackTrace() | |||
| return MessageResponse( | |||
| id = null, | |||
| name = null, | |||
| code = "ERROR", | |||
| type = "EXCEPTION", | |||
| message = "Error recording second scan issue: ${e.message}", | |||
| message = "Error: ${e.message}", | |||
| errorPosition = null | |||
| ) | |||
| } | |||
| } | |||
| private fun generateIssueNoForJo(): String { | |||
| val now = LocalDateTime.now() | |||
| val yearMonth = now.format(java.time.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')}" | |||
| } | |||
| open fun getCompletedJobOrderPickOrdersWithCompletedSecondScan(userId: Long): List<Map<String, Any?>> { | |||
| println("=== getCompletedJobOrderPickOrdersWithCompletedSecondScan ===") | |||
| println("userId: $userId") | |||
| @@ -144,22 +144,26 @@ class JobOrderController( | |||
| } | |||
| @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) | |||
| } | |||
| fun recordSecondScanIssue( | |||
| @PathVariable pickOrderId: Long, | |||
| @PathVariable itemId: Long, | |||
| @RequestBody data: Map<String, Any> | |||
| ): MessageResponse { | |||
| val request = SecondScanIssueRequest( | |||
| pickOrderId = pickOrderId, // ✅ path 变量 | |||
| itemId = itemId, // ✅ path 变量 | |||
| qty = (data["qty"] as Number).toDouble(), | |||
| // ✅ 新增:安全读取 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, | |||
| isBad = data["isBad"] as? Boolean ?: false, | |||
| reason = data["reason"] as? String ?: "", | |||
| createdBy = (data["createdBy"] as Number).toLong(), | |||
| type = (data["type"] as? String) ?: "match" | |||
| ) | |||
| return joPickOrderService.recordSecondScanIssue(request) | |||
| } | |||
| @GetMapping("/completed-job-order-pick-orders-with-completed-second-scan/{userId}") | |||
| fun getCompletedJobOrderPickOrdersWithCompletedSecondScan(@PathVariable userId: Long): List<Map<String, Any?>> { | |||
| return joPickOrderService.getCompletedJobOrderPickOrdersWithCompletedSecondScan(userId) | |||
| @@ -3,9 +3,12 @@ package com.ffii.fpsms.modules.jobOrder.web.model | |||
| data class SecondScanIssueRequest( | |||
| val pickOrderId: Long, | |||
| val itemId: Long, | |||
| val qty: Double, | |||
| val qty: Double, // 这是 actual pick qty (verified 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, | |||
| val createdBy: Long | |||
| val createdBy: Long, | |||
| val type: String? = "match" | |||
| ) | |||
| @@ -27,7 +27,12 @@ class PickExecutionIssue( | |||
| @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, | |||
| @Column(name = "item_id", nullable = false) | |||
| val itemId: Long, | |||
| @@ -104,6 +109,8 @@ class PickExecutionIssue( | |||
| itemCode = null, | |||
| itemDescription = null, | |||
| lotId = null, | |||
| issueNo = null, | |||
| type = null, | |||
| lotNo = null, | |||
| storeLocation = null, | |||
| requiredQty = null, | |||
| @@ -125,10 +132,7 @@ class PickExecutionIssue( | |||
| } | |||
| enum class HandleStatus { | |||
| pending, // ✅ Change to lowercase to match database | |||
| handled, // ✅ Change to lowercase to match database | |||
| resolved, | |||
| jopending, | |||
| johandled, | |||
| joresolved // ✅ Change to lowercase to match database | |||
| pending, | |||
| sort_and_repair, | |||
| dispose | |||
| } | |||
| @@ -3,7 +3,8 @@ package com.ffii.fpsms.modules.pickOrder.entity | |||
| import org.springframework.data.jpa.repository.JpaRepository | |||
| import org.springframework.stereotype.Repository | |||
| import org.springframework.data.jpa.repository.Query | |||
| import org.springframework.data.repository.query.Param | |||
| @Repository | |||
| interface PickExecutionIssueRepository : JpaRepository<PickExecutionIssue, Long> { | |||
| fun findByPickOrderIdAndDeletedFalse(pickOrderId: Long): List<PickExecutionIssue> | |||
| @@ -13,4 +14,13 @@ interface PickExecutionIssueRepository : JpaRepository<PickExecutionIssue, Long> | |||
| pickOrderLineId: Long, | |||
| lotId: Long | |||
| ): List<PickExecutionIssue> | |||
| @Query(""" | |||
| SELECT p.issueNo | |||
| FROM PickExecutionIssue p | |||
| WHERE p.issueNo LIKE CONCAT('SKO-', :yearMonth, '-%') | |||
| AND p.deleted = false | |||
| ORDER BY p.issueNo DESC | |||
| LIMIT 1 | |||
| """) | |||
| fun findLatestIssueNoByYearMonth(@Param("yearMonth") yearMonth: String): String? | |||
| } | |||
| @@ -50,6 +50,8 @@ open class PickExecutionIssueService( | |||
| // 2. 创建 pick execution issue 记录 | |||
| val pickExecutionIssue = PickExecutionIssue( | |||
| issueNo = generateIssueNo(), | |||
| type = request.type, | |||
| pickOrderId = request.pickOrderId, | |||
| pickOrderCode = request.pickOrderCode, | |||
| pickOrderCreateDate = request.pickOrderCreateDate, | |||
| @@ -146,6 +148,31 @@ open class PickExecutionIssueService( | |||
| ) | |||
| } | |||
| } | |||
| private fun generateIssueNo(): String { | |||
| val now = LocalDateTime.now() | |||
| val yearMonth = now.format(java.time.format.DateTimeFormatter.ofPattern("yyMM")) | |||
| // 查询当月最新的 issueNo | |||
| val latestIssueNo = pickExecutionIssueRepository.findLatestIssueNoByYearMonth(yearMonth) | |||
| // 计算下一个序列号 | |||
| val nextSequence = if (latestIssueNo != null) { | |||
| // 从最新的 issueNo 中提取序列号 | |||
| // 例如: SKO-2510-001 -> 001 | |||
| val parts = latestIssueNo.split("-") | |||
| if (parts.size == 3) { | |||
| val currentSequence = parts[2].toIntOrNull() ?: 0 | |||
| currentSequence + 1 | |||
| } else { | |||
| 1 | |||
| } | |||
| } else { | |||
| 1 | |||
| } | |||
| // 格式化为 SKO-YYMM-001 | |||
| return "SKO-${yearMonth}-${nextSequence.toString().padStart(3, '0')}" | |||
| } | |||
| // FPSMS-backend/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt | |||
| // ✅ 修复:处理有部分拣货但有 miss item 的情况 | |||
| @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class]) | |||
| @@ -1843,57 +1843,93 @@ open fun autoAssignAndReleasePickOrderByStoreAndTicket(storeId: String, ticketNo | |||
| val pickOrderIdsStr = pickOrderIds.joinToString(",") | |||
| val sql = """ | |||
| SELECT | |||
| -- Pick Order Information | |||
| po.id as pickOrderId, | |||
| po.code as pickOrderCode, | |||
| po.consoCode as pickOrderConsoCode, -- ✅ 添加 consoCode | |||
| 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, | |||
| -- 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 | |||
| 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' | |||
| 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, | |||
| ORDER BY | |||
| po.code ASC, | |||
| i.code ASC | |||
| """.trimIndent() | |||
| -- 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 | |||
| ), 0) as availableQty, | |||
| -- ✅ Check if all stock out lines for this pick order line are completed | |||
| CASE | |||
| WHEN EXISTS ( | |||
| SELECT 1 | |||
| FROM fpsmsdb.stock_out_line sol_check | |||
| WHERE sol_check.pickOrderLineId = pol.id | |||
| AND sol_check.deleted = false | |||
| AND sol_check.status NOT IN ('completed', 'COMPLETE') | |||
| ) THEN false | |||
| WHEN EXISTS ( | |||
| SELECT 1 | |||
| FROM fpsmsdb.stock_out_line sol_check | |||
| WHERE sol_check.pickOrderLineId = pol.id | |||
| AND sol_check.deleted = false | |||
| ) 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' | |||
| AND po.type NOT IN ('do', 'job') -- ✅ 排除 do 和 job 类型 | |||
| -- ✅ Only include lines that have incomplete stock out lines | |||
| AND ( | |||
| NOT EXISTS ( | |||
| SELECT 1 | |||
| FROM fpsmsdb.stock_out_line sol_all_complete | |||
| WHERE sol_all_complete.pickOrderLineId = pol.id | |||
| AND sol_all_complete.deleted = false | |||
| ) | |||
| OR EXISTS ( | |||
| SELECT 1 | |||
| FROM fpsmsdb.stock_out_line sol_incomplete | |||
| WHERE sol_incomplete.pickOrderLineId = pol.id | |||
| AND sol_incomplete.deleted = false | |||
| AND sol_incomplete.status NOT IN ('completed', 'COMPLETE') | |||
| ) | |||
| ) | |||
| ORDER BY | |||
| po.code ASC, | |||
| i.code ASC | |||
| """.trimIndent() | |||
| println("🔍 Executing optimized SQL: $sql") | |||
| @@ -1966,6 +2002,7 @@ open fun autoAssignAndReleasePickOrderByStoreAndTicket(storeId: String, ticketNo | |||
| val releasedPickOrderIds = allPickOrders | |||
| .filter { it.status == PickOrderStatus.RELEASED } | |||
| .filter { it.assignTo?.id == userId } | |||
| .filter { it.type?.value != "do" && it.type?.value != "jo" } // ✅ 排除 do 和 job 类型 | |||
| .map { it.id!! } | |||
| if (releasedPickOrderIds.isEmpty()) { | |||
| @@ -15,6 +15,8 @@ data class PickExecutionIssueRequest( | |||
| val itemDescription: String? = null, | |||
| val lotId: Long? = null, | |||
| val lotNo: String? = null, | |||
| val issueNo: String? = null, | |||
| val type: String? = null, | |||
| val storeLocation: String? = null, | |||
| val requiredQty: BigDecimal? = null, | |||
| val actualPickQty: BigDecimal? = null, | |||
| @@ -0,0 +1,15 @@ | |||
| --liquibase formatted sql | |||
| --changeset [enson]:update_pick_execution_issue_add_columns_and_modify_status | |||
| -- 1. 添加新列 issueNo | |||
| ALTER TABLE pick_execution_issue | |||
| ADD COLUMN issue_no VARCHAR(50) NULL AFTER id; | |||
| -- 2. 添加新列 Type | |||
| ALTER TABLE pick_execution_issue | |||
| ADD COLUMN type VARCHAR(50) NULL AFTER issue_no; | |||
| -- 3. 修改 handle_status 枚举值 | |||
| ALTER TABLE pick_execution_issue | |||
| MODIFY COLUMN handle_status ENUM('pending', 'Sort and Repair', 'Dispose') NULL DEFAULT 'pending'; | |||