浏览代码

update

master
CANCERYS\kw093 2 个月前
父节点
当前提交
99fee87798
共有 12 个文件被更改,包括 1094 次插入8 次删除
  1. +80
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrder.kt
  2. +80
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrderRecord.kt
  3. +16
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrderRecordRepository.kt
  4. +19
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrderRepository.kt
  5. +7
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/enums/JoPickOrderStatus.kt
  6. +447
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  7. +94
    -2
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt
  8. +17
    -2
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt
  9. +284
    -3
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  10. +4
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt
  11. +1
    -1
      src/main/resources/application-db-local.yml
  12. +45
    -0
      src/main/resources/db/changelog/changes/202510927_01_enson/01_altertable_enson.sql

+ 80
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrder.kt 查看文件

@@ -0,0 +1,80 @@
package com.ffii.fpsms.modules.jobOrder.entity

import jakarta.persistence.*
import java.time.LocalDateTime
import java.time.LocalTime
import com.ffii.fpsms.modules.jobOrder.enums.JoPickOrderStatus
import org.hibernate.annotations.CreationTimestamp
import org.hibernate.annotations.UpdateTimestamp

@Entity
@Table(name = "jo_pick_order")
class JoPickOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
@Column(name = "pick_order_id")
var pickOrderId: Long? = null

@Column(name = "item_id")
var itemId: Long? = null
@Enumerated(EnumType.STRING)
@Column(name = "second_qr_scan_status")
var second_qr_scan_status: JoPickOrderStatus? = null
@Column(name = "second_qr_scan_by")
var second_qr_scan_by: Long? = null
@Column(name = "second_qr_scan_qty")
var second_qr_scan_qty: Int? = null
@Column(name = "handled_by")
var handledBy: Long? = null
@CreationTimestamp
@Column(name = "created")
var created: LocalDateTime? = null
@Column(name = "createdBy", length = 30)
var createdBy: String? = null
@Version
var version: Int = 0
@UpdateTimestamp
@Column(name = "modified")
var modified: LocalDateTime? = null
@Column(name = "modifiedBy", length = 30)
var modifiedBy: String? = null
@Column(name = "deleted")
var deleted: Boolean = false

@Column(name = "hide", nullable = false)
var hide: Boolean = false
// Default constructor for Hibernate
constructor()
// Constructor for creating new instances
constructor(
pickOrderId: Long? = null,
itemId: Long? = null,
second_qr_scan_status: JoPickOrderStatus? = null,
second_qr_scan_by: Long? = null,
second_qr_scan_qty: Int? = null,
handledBy: Long? = null,
createdBy: String? = null,
modifiedBy: String? = null
) {
this.pickOrderId = pickOrderId
this.itemId = itemId
this.second_qr_scan_status = second_qr_scan_status
this.second_qr_scan_by = second_qr_scan_by
this.second_qr_scan_qty = second_qr_scan_qty
this.handledBy = handledBy
this.createdBy = createdBy
this.modifiedBy = modifiedBy
}
}

+ 80
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrderRecord.kt 查看文件

@@ -0,0 +1,80 @@
package com.ffii.fpsms.modules.jobOrder.entity

import jakarta.persistence.*
import java.time.LocalDateTime
import java.time.LocalTime
import com.ffii.fpsms.modules.jobOrder.enums.JoPickOrderStatus
import org.hibernate.annotations.CreationTimestamp
import org.hibernate.annotations.UpdateTimestamp

@Entity
@Table(name = "jo_pick_order_record")
class JoPickOrderRecord {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
@Column(name = "pick_order_id")
var pickOrderId: Long? = null

@Column(name = "item_id")
var itemId: Long? = null
@Enumerated(EnumType.STRING)
@Column(name = "second_qr_scan_status")
var second_qr_scan_status: JoPickOrderStatus? = null
@Column(name = "second_qr_scan_by")
var second_qr_scan_by: Long? = null
@Column(name = "second_qr_scan_qty")
var second_qr_scan_qty: Int? = null
@Column(name = "handled_by")
var handledBy: Long? = null
@CreationTimestamp
@Column(name = "created")
var created: LocalDateTime? = null
@Column(name = "createdBy", length = 30)
var createdBy: String? = null
@Version
var version: Int = 0
@UpdateTimestamp
@Column(name = "modified")
var modified: LocalDateTime? = null
@Column(name = "modifiedBy", length = 30)
var modifiedBy: String? = null
@Column(name = "deleted")
var deleted: Boolean = false

@Column(name = "hide", nullable = false)
var hide: Boolean = false
// Default constructor for Hibernate
constructor()
// Constructor for creating new instances
constructor(
pickOrderId: Long? = null,
itemId: Long? = null,
second_qr_scan_status: JoPickOrderStatus? = null,
second_qr_scan_by: Long? = null,
second_qr_scan_qty: Int? = null,
handledBy: Long? = null,
createdBy: String? = null,
modifiedBy: String? = null
) {
this.pickOrderId = pickOrderId
this.itemId = itemId
this.second_qr_scan_status = second_qr_scan_status
this.second_qr_scan_by = second_qr_scan_by
this.second_qr_scan_qty = second_qr_scan_qty
this.handledBy = handledBy
this.createdBy = createdBy
this.modifiedBy = modifiedBy
}
}

+ 16
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JoPickOrderRecordRepository.kt 查看文件

@@ -0,0 +1,16 @@
package com.ffii.fpsms.modules.jobOrder.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.jobOrder.enums.JoPickOrderStatus
import com.ffii.fpsms.modules.master.entity.projections.SearchId
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository
import java.io.Serializable
import java.time.LocalDateTime

@Repository
interface JoPickOrderRecordRepository : JpaRepository<JoPickOrderRecord, Long> {
fun findByPickOrderId(pickOrderId: Long): List<JoPickOrderRecord>
//fun findByTicketNoStartingWith(ticketPrefix: String): List<JoPickOrderRecord>
}

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

@@ -0,0 +1,19 @@
package com.ffii.fpsms.modules.jobOrder.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.jobOrder.enums.JoPickOrderStatus
import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrder

import com.ffii.fpsms.modules.master.entity.projections.SearchId
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository
import java.io.Serializable
import java.time.LocalDateTime

@Repository
interface JoPickOrderRepository : JpaRepository<JoPickOrder, Long> {

fun findByPickOrderId(pickOrderId: Long): List<JoPickOrder>

}

+ 7
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/enums/JoPickOrderStatus.kt 查看文件

@@ -0,0 +1,7 @@
package com.ffii.fpsms.modules.jobOrder.enums

enum class JoPickOrderStatus(val value: String) {
pending("pending"),
scanned("scanned"),
completed("completed")
}

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

@@ -0,0 +1,447 @@
package com.ffii.fpsms.modules.jobOrder.service

import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrder
import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrderRepository
import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrderRecord
import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrderRecordRepository
import com.ffii.fpsms.modules.jobOrder.enums.JoPickOrderStatus
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.pickOrder.service.PickOrderService
import com.ffii.fpsms.modules.user.service.UserService
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
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

@Service
open class JoPickOrderService(
private val joPickOrderRepository: JoPickOrderRepository,
private val joPickOrderRecordRepository: JoPickOrderRecordRepository,
private val pickOrderService: PickOrderService,
private val userService: UserService,
private val pickOrderRepository: PickOrderRepository
) {
open fun save(record: JoPickOrder): JoPickOrder {
return joPickOrderRepository.save(record)
}
open fun saveRecord(record: JoPickOrderRecord): JoPickOrderRecord {
return joPickOrderRecordRepository.save(record)
}
// ✅ Update JoPickOrder status to released
open fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<JoPickOrder> {
val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId)
joPickOrders.forEach {
it.handledBy = userId
it.second_qr_scan_status = JoPickOrderStatus.pending // Set initial status
}
return joPickOrderRepository.saveAll(joPickOrders)
}
// ✅ Complete JoPickOrder
open fun completeJoPickOrdersForPickOrder(pickOrderId: Long): List<JoPickOrder> {
val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId)
joPickOrders.forEach {
it.second_qr_scan_status = JoPickOrderStatus.completed
}
return joPickOrderRepository.saveAll(joPickOrders)
}
// ✅ Update JoPickOrderRecord status to released
open fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List<JoPickOrderRecord> {
val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId)
joPickOrderRecords.forEach {
it.handledBy = userId
it.second_qr_scan_status = JoPickOrderStatus.pending // Set initial status
}
return joPickOrderRecordRepository.saveAll(joPickOrderRecords)
}
// ✅ Complete JoPickOrderRecord
open fun completeJoPickOrderRecordsForPickOrder(pickOrderId: Long): List<JoPickOrderRecord> {
val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId)
joPickOrderRecords.forEach {
it.second_qr_scan_status = JoPickOrderStatus.completed
}
return joPickOrderRecordRepository.saveAll(joPickOrderRecords)
}
// ✅ Find JoPickOrder records by pick order ID
open fun findByPickOrderId(pickOrderId: Long): List<JoPickOrder> {
return joPickOrderRepository.findByPickOrderId(pickOrderId)
}
// ✅ Find JoPickOrderRecord records by pick order ID
open fun findRecordsByPickOrderId(pickOrderId: Long): List<JoPickOrderRecord> {
return joPickOrderRecordRepository.findByPickOrderId(pickOrderId)
}
// ✅ Create JoPickOrder records for a pick order
@Transactional
open fun createJoPickOrdersForPickOrder(pickOrderId: Long, itemIds: List<Long>): List<JoPickOrder> {
val joPickOrders = itemIds.map { itemId ->
JoPickOrder(
pickOrderId = pickOrderId,
itemId = itemId,
second_qr_scan_status = JoPickOrderStatus.pending,
handledBy = null
)
}
return joPickOrderRepository.saveAll(joPickOrders)
}
// ✅ Create JoPickOrderRecord records for a pick order
@Transactional
open fun createJoPickOrderRecordsForPickOrder(pickOrderId: Long, itemIds: List<Long>): List<JoPickOrderRecord> {
val joPickOrderRecords = itemIds.map { itemId ->
JoPickOrderRecord(
pickOrderId = pickOrderId,
itemId = itemId,
second_qr_scan_status = JoPickOrderStatus.pending,
handledBy = null
)
}
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()}")
println("userId filter: $userId")
// Get all pick order IDs assigned to the user (both RELEASED and PENDING with joId)
val user = userService.find(userId).orElse(null)
if (user == null) {
println("❌ User not found: $userId")
return emptyMap()
}
val statusList = listOf(PickOrderStatus.PENDING, PickOrderStatus.RELEASED)
// Get all pick orders assigned to user with PENDING or RELEASED status that have joId
val allAssignedPickOrders = pickOrderRepository.findAllByAssignToIdAndStatusIn(
userId,
statusList
).filter { it.jobOrder != null } // Only pick orders with joId
println("🔍 DEBUG: Found ${allAssignedPickOrders.size} job order pick orders assigned to user $userId")
val visiblePickOrders = allAssignedPickOrders.filter { pickOrder ->
val joPickOrders = findByPickOrderId(pickOrder.id!!)
joPickOrders.none { it.hide } // Only show hide = false orders
}
// Filter based on assignment and status
val filteredPickOrders = if (visiblePickOrders.isNotEmpty()) {
// Check if there are any RELEASED orders assigned to this user (active work)
val assignedReleasedOrders = allAssignedPickOrders.filter {
it.status == PickOrderStatus.RELEASED && it.assignTo?.id == userId
}
if (assignedReleasedOrders.isNotEmpty()) {
// If there are assigned RELEASED orders, show only those
println("🔍 DEBUG: Found ${assignedReleasedOrders.size} assigned RELEASED job orders, showing only those")
assignedReleasedOrders
} else {
// If no assigned RELEASED orders, show only the latest COMPLETED order
val completedOrders = allAssignedPickOrders.filter { it.status == PickOrderStatus.COMPLETED }
if (completedOrders.isNotEmpty()) {
val latestCompleted = completedOrders.maxByOrNull { it.completeDate ?: it.modified ?: LocalDateTime.MIN }
println("🔍 DEBUG: No assigned RELEASED job orders, showing latest completed order: ${latestCompleted?.code}")
listOfNotNull(latestCompleted)
} else {
println("🔍 DEBUG: No job orders found")
emptyList()
}
}
} else {
emptyList()
}
val pickOrderIds = filteredPickOrders.map { it.id!! }
println("🎯 Job Order Pick order IDs to fetch: $pickOrderIds")
if (pickOrderIds.isEmpty()) {
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
// Use the same SQL query but transform the results into hierarchical structure
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,
po.type as pickOrderType,
po.status as pickOrderStatus,
po.assignTo as pickOrderAssignTo,
-- Job Order Information
jo.id as jobOrderId,
jo.code as jobOrderCode,
jo.name as jobOrderName,
-- 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 IN ('PENDING', 'RELEASED', '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)
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 job order hierarchical structure: $sql")
println("🔍 With parameters: userId = $userId, pickOrderIds = $pickOrderIdsStr")
// ✅ 修复:使用 PickOrderService 的现有方法
val results = pickOrderService.getAllPickOrderLotsWithDetailsHierarchical(userId)
// 如果结果为空,返回空结构
if (results.isEmpty()) {
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
val completedPickOrders = pickOrderRepository.findAllByAssignToIdAndStatusIn(
userId,
listOf(PickOrderStatus.COMPLETED)
).filter { it.jobOrder != null } // Only job order pick orders

println("Found ${completedPickOrders.size} completed job order pick orders for user $userId")

val jobOrderPickOrders = completedPickOrders.mapNotNull { pickOrder ->
// Get job order details
val jobOrder = pickOrder.jobOrder
if (jobOrder != null) {
val joPickOrders = findByPickOrderId(pickOrder.id!!)
val isHidden = joPickOrders.any { it.hide }
if (!isHidden) {
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
}
}

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

// Get completed job order pick order records (for third tab)
open fun getCompletedJobOrderPickOrderRecords(userId: Long): List<Map<String, Any?>> {
println("=== getCompletedJobOrderPickOrderRecords ===")
println("userId: $userId")
return try {
// Get all completed JoPickOrderRecord records for the user
val allRecords = joPickOrderRecordRepository.findAll()
val completedRecords = allRecords.filter {
it.second_qr_scan_status == JoPickOrderStatus.completed &&
it.handledBy == userId
}

println("Found ${completedRecords.size} completed job order pick order records for user $userId")

val recordDetails = completedRecords.mapNotNull { record ->
// Get pick order details
val pickOrder = pickOrderRepository.findById(record.pickOrderId!!).orElse(null)
if (pickOrder != null && pickOrder.jobOrder != null) {
val jobOrder = pickOrder.jobOrder
mapOf(
"recordId" to record.id,
"pickOrderId" to pickOrder.id,
"pickOrderCode" to pickOrder.code,
"jobOrderId" to jobOrder?.id,
"jobOrderCode" to jobOrder?.code,
"jobOrderName" to jobOrder?.bom?.name,
"itemId" to record.itemId,
"secondQrScanStatus" to record.second_qr_scan_status,
"secondQrScanBy" to record.second_qr_scan_by,
"secondQrScanQty" to record.second_qr_scan_qty,
"handledBy" to record.handledBy,
"created" to record.created,
"completeDate" to pickOrder.completeDate
)
} else {
println("❌ Record ${record.id} has no valid pick order or job order, skipping.")
null
}
}

println("Returning ${recordDetails.size} completed job order pick order records")
recordDetails
} catch (e: Exception) {
println("❌ Error in getCompletedJobOrderPickOrderRecords: ${e.message}")
e.printStackTrace()
emptyList()
}
}
}

+ 94
- 2
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt 查看文件

@@ -30,7 +30,18 @@ import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import kotlin.jvm.optionals.getOrNull


import com.ffii.fpsms.modules.jobOrder.service.JoPickOrderService
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
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
import com.ffii.fpsms.modules.stock.entity.StockOut
import com.ffii.fpsms.modules.stock.entity.StockOutLine
import com.ffii.fpsms.modules.stock.web.model.StockOutStatus
import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPolRequest
@Service
open class JobOrderService(
val jobOrderRepository: JobOrderRepository,
@@ -38,6 +49,13 @@ open class JobOrderService(
val userService: UserService,
val productionScheduleLineRepository: ProductionScheduleLineRepository,
val pickOrderService: PickOrderService,
val joPickOrderService: JoPickOrderService,
val pickOrderRepository: PickOrderRepository,
val pickOrderLineRepository: PickOrderLineRepository,
val suggestedPickLotService: SuggestedPickLotService,
val inventoryLotLineRepository: InventoryLotLineRepository,
val stockOutRepository: StockOutRepository,
val stockOutLineRepository: StockOutLIneRepository
) {
open fun allJobOrdersByPage(request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfo> {
val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10);
@@ -161,7 +179,81 @@ open class JobOrderService(
pickOrderLine = pols
)

pickOrderService.create(po)
val createdPickOrder = pickOrderService.create(po)
val consoCode = pickOrderService.assignConsoCode()
val pickOrderEntity = pickOrderRepository.findById(createdPickOrder.id!!).orElse(null)
if (pickOrderEntity != null) {
pickOrderEntity.consoCode = consoCode
pickOrderEntity.status = com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED
pickOrderRepository.saveAndFlush(pickOrderEntity)
// ✅ 添加 suggested pick lots 创建逻辑
val lines = pickOrderLineRepository.findAllByPickOrderId(pickOrderEntity.id!!)
if (lines.isNotEmpty()) {
val suggestions = suggestedPickLotService.suggestionForPickOrderLines(
SuggestedPickLotForPolRequest(pickOrderLines = lines)
)
val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList)
// ✅ Hold inventory quantities
val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn(
saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id }
)
saveSuggestedPickLots.forEach { lot ->
if (lot.suggestedLotLine != null && lot.suggestedLotLine?.id != null && lot.suggestedLotLine!!.id!! > 0) {
val lineIndex = inventoryLotLines.indexOf(lot.suggestedLotLine)
if (lineIndex >= 0) {
inventoryLotLines[lineIndex].holdQty =
(inventoryLotLines[lineIndex].holdQty ?: BigDecimal.ZERO).plus(lot.qty ?: BigDecimal.ZERO)
}
}
}
inventoryLotLineRepository.saveAll(inventoryLotLines)
// ✅ Create stock out record and pre-create stock out lines
val stockOut = StockOut().apply {
this.type = "job"
this.consoPickOrderCode = consoCode
this.status = StockOutStatus.PENDING.status
this.handler = SecurityUtils.getUser().getOrNull()?.id
}
val savedStockOut = stockOutRepository.saveAndFlush(stockOut)
// ✅ Pre-create stock out lines for suggested lots
saveSuggestedPickLots.forEach { lot ->
val polId = lot.pickOrderLine?.id
val illId = lot.suggestedLotLine?.id
if (polId != null && illId != null) {
val existingLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(polId, illId)
if (existingLines.isEmpty()) {
val pickOrderLine = pickOrderLineRepository.findById(polId).orElse(null)
val inventoryLotLine = inventoryLotLineRepository.findById(illId).orElse(null)
if (pickOrderLine != null && inventoryLotLine != null) {
val line = StockOutLine().apply {
this.stockOut = savedStockOut
this.pickOrderLine = pickOrderLine
this.inventoryLotLine = inventoryLotLine
this.item = pickOrderLine.item
this.status = StockOutLineStatus.PENDING.status
this.qty = 0.0
}
stockOutLineRepository.save(line)
}
}
}
}
}
}
val itemIds = pols.mapNotNull { it.itemId }
if (itemIds.isNotEmpty()) {
joPickOrderService.createJoPickOrdersForPickOrder(createdPickOrder.id!!, itemIds)
joPickOrderService.createJoPickOrderRecordsForPickOrder(createdPickOrder.id!!, itemIds)
}


return MessageResponse(
id = jo.id,


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

@@ -18,13 +18,14 @@ import org.springframework.web.bind.annotation.PostMapping
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
@RestController
@RequestMapping("/jo")
class JobOrderController(
private val jobOrderService: JobOrderService,
private val jobOrderBomMaterialService: JobOrderBomMaterialService,
private val jobOrderProcessService: JobOrderProcessService
private val jobOrderProcessService: JobOrderProcessService,
private val joPickOrderService: JoPickOrderService
) {

@GetMapping("/getRecordByPage")
@@ -58,4 +59,18 @@ class JobOrderController(

return jo
}
@GetMapping("/all-lots-hierarchical/{userId}")
fun getAllJobOrderLotsHierarchical(@PathVariable userId: Long): Map<String, Any?> {
return joPickOrderService.getAllJobOrderLotsWithDetailsHierarchical(userId)
}

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

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

+ 284
- 3
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt 查看文件

@@ -2777,7 +2777,122 @@ if (existingRecords.isNotEmpty()) {
println("❌ Pick order is not of type 'do': ${pickOrder.type?.value}")
return emptyList()
}
val allowedstatuses= listOf("assigned", "released", "picking", "completed")
val allowedstatuses= listOf("assigned", "released", "picking"
//, "completed"
)
if (pickOrder.status?.value !in allowedstatuses) {
println("❌ Pick order status is not in allowed states: ${pickOrder.status?.value}")
return emptyList()
}

val deliveryOrder = pickOrder.deliveryOrder
val shop = deliveryOrder?.shop
val supplier = deliveryOrder?.supplier
println(" Delivery order: ${deliveryOrder?.code}, Shop: ${shop?.name}, Supplier: ${supplier?.code}")
println("🔍 Shop ID: ${shop?.id}")
// ✅ Get truck information using repository with detailed debugging
val truck = if (shop?.id != null) {
try {
println("🔍 Querying truck repository for shopId: ${shop.id}")
// Get all trucks for this shop
val trucksForShop = truckRepository.findByShopIdAndDeletedFalse(shop.id)
println("🔍 Trucks for shop ${shop.id}: ${trucksForShop.size}")
trucksForShop.forEach { t ->
println(" - Truck ID: ${t.id}, TruckNo: ${t.truckNo}, ShopId: ${t.shop?.id}, Deleted: ${t.deleted}")
}
// Take the first truck (or null if none found)
val selectedTruck = trucksForShop.firstOrNull()
if (selectedTruck != null) {
println("✅ Selected truck: ID=${selectedTruck.id}, TruckNo=${selectedTruck.truckNo}, DepartureTime=${selectedTruck.departureTime}")
} else {
println("❌ No truck found for shopId ${shop.id}")
}
selectedTruck
} catch (e: Exception) {
println("⚠️ Error querying truck repository for shop ${shop.id}: ${e.message}")
e.printStackTrace()
null
}
} else {
println("⚠️ Shop ID is null")
null
}
val ticketNo = try {
val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId)
val ticketNo = doPickOrders.firstOrNull()?.ticketNo ?: ""
println("🔍 Found ticket number: $ticketNo for pick order $pickOrderId")
ticketNo
} catch (e: Exception) {
println("⚠️ Error getting ticket number for pick order $pickOrderId: ${e.message}")
""
}
val dpoStoreId = try {
val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId)
doPickOrders.firstOrNull()?.storeId ?: ""
} catch (e: Exception) {
println("⚠️ Error getting storeId for pick order $pickOrderId: ${e.message}")
""
}
val result = mapOf(
"pickOrderId" to (pickOrder.id ?: 0L),
"pickOrderCode" to (pickOrder.code ?: ""),
"pickOrderConsoCode" to (pickOrder.consoCode ?: ""),
"pickOrderTargetDate" to (pickOrder.targetDate?.toString() ?: ""),
"pickOrderStatus" to (pickOrder.status?.value ?: ""),
"deliveryOrderId" to (deliveryOrder?.id ?: 0L),
"deliveryNo" to (deliveryOrder?.code ?: ""),
"deliveryDate" to (deliveryOrder?.orderDate?.toString() ?: ""),
"shopId" to (shop?.id ?: 0L),
"shopCode" to (shop?.code ?: ""),
"shopName" to (shop?.name ?: ""),
"shopAddress" to buildShopAddress(shop),
"shopPoNo" to (supplier?.code ?: ""),
"numberOfCartons" to (pickOrder.pickOrderLines.size),
"truckNo" to (truck?.truckNo ?: ""), // ✅ Use entity property
"DepartureTime" to (truck?.departureTime?.toString() ?: ""),
"ticketNo" to ticketNo,
"storeId" to dpoStoreId,
"qrCodeData" to (pickOrder.id ?: 0L)
)
println("✅ FG Pick Orders by ID result count: 1")
return listOf(result)
} catch (e: Exception) {
println("❌ Error in getFgPickOrdersByPickOrderId: ${e.message}")
e.printStackTrace()
return emptyList()
}
}
open fun getnewFgPickOrdersByPickOrderId(pickOrderId: Long): List<Map<String, Any?>> {
try {
println("🔍 Starting getnewFgPickOrdersByPickOrderId method with pickOrderId: $pickOrderId")
val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null)
if (pickOrder == null) {
println("❌ Pick order not found with ID: $pickOrderId")
return emptyList()
}
if (doPickOrderRepository.findByPickOrderId(pickOrderId).firstOrNull()?.hide == true) {
println("🔍 Pick order $pickOrderId is hidden, returning empty list")
return emptyList()
}
println("🔍 Found pick order: ${pickOrder.code}, type: ${pickOrder.type?.value}, status: ${pickOrder.status?.value}")
if (pickOrder.type?.value != "do") {
println("❌ Pick order is not of type 'do': ${pickOrder.type?.value}")
return emptyList()
}
val allowedstatuses= listOf("assigned", "released", "picking","completed"
//, "completed"
)
if (pickOrder.status?.value !in allowedstatuses) {
println("❌ Pick order status is not in allowed states: ${pickOrder.status?.value}")
return emptyList()
@@ -3169,7 +3284,9 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A
println("❌ User not found: $userId")
return emptyMap()
}
val statusList = listOf(PickOrderStatus.PENDING, PickOrderStatus.RELEASED, PickOrderStatus.COMPLETED)
val statusList = listOf(PickOrderStatus.PENDING, PickOrderStatus.RELEASED,
//PickOrderStatus.COMPLETED
)
// Get all pick orders assigned to user with PENDING or RELEASED status that have doId
val allAssignedPickOrders = pickOrderRepository.findAllByAssignToAndStatusIn(
@@ -3749,7 +3866,7 @@ open fun getCompletedDoPickOrders(
for (pickOrder in completedPickOrders) {
// 获取该 pick order 的 FG pick orders
val fgPickOrders = getFgPickOrdersByPickOrderId(pickOrder.id!!)
val fgPickOrders = getnewFgPickOrdersByPickOrderId(pickOrder.id!!)
if (fgPickOrders.isNotEmpty()) {
val firstFgOrder = fgPickOrders[0] as Map<String, Any?> // ✅ 修复:转换为 Map
@@ -3801,4 +3918,168 @@ open fun getCompletedDoPickOrders(
emptyList()
}
}
// ... existing code ...

open fun getLotDetailsByPickOrderId(pickOrderId: Long): List<Map<String, Any>> {
println("=== Debug: getLotDetailsByPickOrderId ===")
println("pickOrderId: $pickOrderId")
// Get the pick order
val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null)
if (pickOrder == null) {
println("❌ Pick order not found: $pickOrderId")
return emptyList()
}
// ✅ 修复:使用正确的方法名
val pickOrderLines = pickOrderLineRepository.findAllByPickOrderId(pickOrderId)
if (pickOrderLines.isEmpty()) {
println("❌ No pick order lines found for pick order: $pickOrderId")
return emptyList()
}
// Build the SQL query similar to getAllPickOrderLotsWithDetailsWithoutAutoAssign
// but filtered by specific pickOrderId
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,
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,
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,
-- 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,
-- Add detailed debug fields for lotAvailability calculation
ill.status as debug_ill_status,
(il.expiryDate IS NOT NULL AND il.expiryDate < CURDATE()) as debug_is_expired,
sol.status as debug_sol_status,
(COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) as debug_remaining_stock,
(COALESCE(spl.qty, 0) - COALESCE(sol.qty, 0)) as debug_required_after_picked,
-- Lot availability status
CASE
-- Check if lot is expired
WHEN (il.expiryDate IS NOT NULL AND il.expiryDate < CURDATE()) THEN 'expired'
-- Check if lot has rejected stock out line for this pick order
WHEN sol.status = 'rejected' THEN 'rejected'
-- Check if lot is unavailable due to insufficient stock
WHEN (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) <= 0 THEN 'insufficient_stock'
-- Check if lot status is unavailable
WHEN ill.status = 'unavailable' THEN 'status_unavailable'
-- Default to available
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
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
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
WHERE po.deleted = false
AND po.id = :pickOrderId
AND pol.deleted = false
AND ill.deleted = false
AND il.deleted = false
AND (spl.pickOrderLineId IS NOT NULL OR sol.pickOrderLineId IS NOT NULL)
ORDER BY
CASE WHEN sol.status = 'rejected' THEN 0 ELSE 1 END, -- Show rejected lots first
COALESCE(r.index, 0) ASC,
po.code ASC,
i.code ASC,
il.expiryDate ASC,
il.lotNo ASC
""".trimIndent()
// ✅ 使用 jdbcDao 而不是 entityManager
println("🔍 Executing SQL for lot details by pick order: $sql")
println("🔍 With parameters: pickOrderId = $pickOrderId")
val results = jdbcDao.queryForList(sql, mapOf("pickOrderId" to pickOrderId))
println("✅ Total result count: ${results.size}")
return results
}



}

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

@@ -304,4 +304,8 @@ fun getCompletedDoPickOrders(
): List<Map<String, Any?>> {
return pickOrderService.getCompletedDoPickOrders(userId, pickOrderCode, shopName, deliveryNo, ticketNo)
}
@GetMapping("/lot-details-by-pick-order/{pickOrderId}")
fun getLotDetailsByPickOrderId(@PathVariable pickOrderId: Long): List<Map<String, Any>> {
return pickOrderService.getLotDetailsByPickOrderId(pickOrderId);
}
}

+ 1
- 1
src/main/resources/application-db-local.yml 查看文件

@@ -1,5 +1,5 @@
spring:
datasource:
jdbc-url: jdbc:mysql://127.0.0.1:3306/fpsmsdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8
jdbc-url: jdbc:mysql://127.0.0.1:3308/fpsmsdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8
username: root
password: secret

+ 45
- 0
src/main/resources/db/changelog/changes/202510927_01_enson/01_altertable_enson.sql 查看文件

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

--changeset enson:update

CREATE TABLE jo_pick_order (
id int AUTO_INCREMENT PRIMARY KEY,
pick_order_id int NOT NULL,
item_id int NOT NULL,
handled_by int,
second_qr_scan_status enum('pending', 'scanned', 'completed') DEFAULT 'pending',
second_qr_scan_by int NULL,
second_qr_scan_qty int NULL,
created datetime DEFAULT CURRENT_TIMESTAMP,
createdBy varchar(30),
version int DEFAULT 1,
modified datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
modifiedBy varchar(30),
deleted tinyint(1) DEFAULT 0,
FOREIGN KEY (pick_order_id) REFERENCES pick_order(id),
FOREIGN KEY (item_id) REFERENCES items(id),
FOREIGN KEY (handled_by) REFERENCES user(id),
FOREIGN KEY (second_qr_scan_by) REFERENCES user(id)
);

CREATE TABLE jo_pick_order_record (
id int AUTO_INCREMENT PRIMARY KEY,
pick_order_id int NOT NULL,
item_id int NOT NULL,
handled_by int,
second_qr_scan_status enum('pending', 'scanned', 'completed') DEFAULT 'pending',
second_qr_scan_by int NULL,
second_qr_scan_qty int NULL,
created datetime DEFAULT CURRENT_TIMESTAMP,
createdBy varchar(30),
version int DEFAULT 1,
modified datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
modifiedBy varchar(30),
deleted tinyint(1) DEFAULT 0,
FOREIGN KEY (pick_order_id) REFERENCES pick_order(id),
FOREIGN KEY (item_id) REFERENCES items(id),
FOREIGN KEY (handled_by) REFERENCES user(id),
FOREIGN KEY (second_qr_scan_by) REFERENCES user(id)
);

正在加载...
取消
保存