Browse Source

update

master
CANCERYS\kw093 1 month ago
parent
commit
e3fbd3e0db
27 changed files with 1396 additions and 6 deletions
  1. +2
    -0
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderLineRecord.kt
  2. +4
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecord.kt
  3. +3
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderCompletionService.kt
  4. +4
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt
  5. +2
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt
  6. +5
    -2
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  7. +4
    -1
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt
  8. +56
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcess.kt
  9. +106
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt
  10. +11
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLineRepository.kt
  11. +14
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessRepository.kt
  12. +39
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssue.kt
  13. +9
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssueRepository.kt
  14. +48
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt
  15. +19
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatus.kt
  16. +15
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatusConverter.kt
  17. +616
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt
  18. +170
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt
  19. +92
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt
  20. +3
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt
  21. +106
    -0
      src/main/resources/db/changelog/changes/20251026_02_enson/01_alter_table.sql
  22. +12
    -0
      src/main/resources/db/changelog/changes/20251026_02_enson/02_alter_table.sql
  23. +15
    -0
      src/main/resources/db/changelog/changes/20251026_02_enson/03_alter_table.sql
  24. +5
    -0
      src/main/resources/db/changelog/changes/20251028_01_enson/01_producutprocess.sql
  25. +12
    -0
      src/main/resources/db/changelog/changes/20251028_01_enson/02_producutprocess.sql
  26. +17
    -0
      src/main/resources/db/changelog/changes/20251098_01_enson/03_altertable_enson.sql
  27. +7
    -0
      src/main/resources/db/changelog/changes/20251103_01_enson/01_altertable_enson.sql

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderLineRecord.kt View File

@@ -18,6 +18,8 @@ import java.time.LocalDateTime
@Table(name = "do_pick_order_line_record")
open class DoPickOrderLineRecord: BaseEntity<Long>() {

@Column(name = "record_id")
open var recordId: Long? = null
@Column(name = "do_pick_order_id", length = 100)
open var doPickOrderId: Long? = null



+ 4
- 1
src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecord.kt View File

@@ -14,7 +14,8 @@ class DoPickOrderRecord {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
@Column(name = "record_id")
var recordId: Long? = null
@Column(name = "store_id", length = 10)
var storeId: String? = null
@@ -95,6 +96,7 @@ var handlerName: String? = null
// Constructor for creating new instances
constructor(
recordId: Long?,
storeId: String,
ticketNo: String,
ticketStatus: DoPickOrderStatus,
@@ -117,6 +119,7 @@ var handlerName: String? = null
deliveryOrderCode: String? = null,
loadingSequence: Int? = null,
) {
this.recordId= recordId
this.storeId = storeId
this.ticketNo = ticketNo
this.pickOrderId = pickOrderId


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

@@ -39,7 +39,7 @@ open class DoPickOrderCompletionService(
return savedDoPickOrders
}
/*
@Transactional
fun removeDoPickOrdersForPickOrder(pickOrderId: Long): Int {
val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId)
@@ -48,6 +48,7 @@ open class DoPickOrderCompletionService(
doPickOrders.forEach { doPickOrder ->
// ✅ 第一步:复制 do_pick_order 到 do_pick_order_record
val doPickOrderRecord = DoPickOrderRecord(
storeId = doPickOrder.storeId ?: "",
ticketNo = doPickOrder.ticketNo ?: "",
ticketStatus = DoPickOrderStatus.completed,
@@ -99,4 +100,5 @@ open class DoPickOrderCompletionService(
return deletedCount
}
*/
}

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

@@ -176,6 +176,7 @@ open class DoPickOrderService(
doPickOrders.forEach { doPickOrder ->
// ✅ 第一步:复制 do_pick_order 到 do_pick_order_record
val doPickOrderRecord = DoPickOrderRecord(
recordId = doPickOrder.id,
storeId = doPickOrder.storeId?: "",
ticketNo = doPickOrder.ticketNo?: "",
ticketStatus = DoPickOrderStatus.completed, // 设置为completed状态
@@ -203,7 +204,8 @@ open class DoPickOrderService(
// ✅ 第二步:复制 do_pick_order_line 到 do_pick_order_line_record
val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrder.id!!)
doPickOrderLines.forEach { line ->
val doPickOrderLineRecord = DoPickOrderLineRecord() // ✅ 使用默认构造函数
val doPickOrderLineRecord = DoPickOrderLineRecord()
doPickOrderLineRecord.recordId = line.id // ✅ 使用默认构造函数
doPickOrderLineRecord.doPickOrderId = savedRecord.id // ✅ 设置属性
doPickOrderLineRecord.pickOrderId = line.pickOrderId
doPickOrderLineRecord.doOrderId = line.doOrderId
@@ -540,6 +542,7 @@ open class DoPickOrderService(

// 创建 do_pick_order_record
val doPickOrderRecord = DoPickOrderRecord(
recordId =doPickOrder.id,
storeId = storeId,
ticketNo = ticketNumber,
ticketStatus = DoPickOrderStatus.pending,


+ 2
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt View File

@@ -7,4 +7,6 @@ import java.util.Optional
@Repository
interface BomProcessRepository : AbstractRepository<BomProcess, Long> {
fun findBySeqNoAndBomIdAndDeletedIsFalse(seqNo: Int, bomId: Long): BomProcess?
fun findByBomIdOrderBySeqNo(bomId: Long): List<BomProcess>
fun findByBomId(bomId: Long): List<BomProcess>
}

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

@@ -1284,6 +1284,7 @@ open class PickOrderService(

// 2) 先复制 do_pick_order -> do_pick_order_record
val dpoRecord = DoPickOrderRecord(
recordId =dpo.id,
storeId = dpo.storeId ?: "",
ticketNo = dpo.ticketNo ?: "",
ticketStatus = DoPickOrderStatus.completed,
@@ -1310,6 +1311,7 @@ open class PickOrderService(
val lines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(dpo.id!!)
val lineRecords = lines.map { l: DoPickOrderLine ->
DoPickOrderLineRecord().apply {
this.recordId=l.id
this.doPickOrderId = savedHeader.id
this.pickOrderId = l.pickOrderId
this.doOrderId = l.doOrderId
@@ -2399,7 +2401,7 @@ open class PickOrderService(
)
}
}
/*
@Transactional(rollbackFor = [java.lang.Exception::class])
open fun autoAssignAndReleasePickOrderByStore(userId: Long, storeId: String): MessageResponse {
try {
@@ -2622,6 +2624,7 @@ open class PickOrderService(
println("🔍 DEBUG: Processing line - Store ID: $determinedStoreId")

val doPickOrderRecord = DoPickOrderRecord(
id=dopickorder.id,
storeId = determinedStoreId,
ticketNo = nextTicketNumber,
ticketStatus = DoPickOrderStatus.released,
@@ -2670,7 +2673,7 @@ open class PickOrderService(
)
}
}
*/
open fun getAllPickOrderLotsWithDetailsWithAutoAssign(userId: Long): List<Map<String, Any>> {
println("=== Debug: getAllPickOrderLotsWithDetailsWithAutoAssign ===")
println("today: ${LocalDate.now()}")


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

@@ -234,6 +234,7 @@ class PickOrderController(
fun autoAssignAndReleasePickOrder(@PathVariable userId: Long): MessageResponse {
return pickOrderService.autoAssignAndReleasePickOrder(userId)
}
/*
@PostMapping("/auto-assign-release-by-store")
fun autoAssignReleaseByStore(
@RequestParam userId: Long,
@@ -241,6 +242,7 @@ class PickOrderController(
): MessageResponse {
return pickOrderService.autoAssignAndReleasePickOrderByStore(userId, storeId)
}
*/
@GetMapping("/check-pick-completion/{userId}")
fun checkPickOrderCompletion(@PathVariable userId: Long): MessageResponse {
return pickOrderService.checkPickOrderCompletion(userId)
@@ -273,7 +275,7 @@ class PickOrderController(
fun getAllPickOrderLotsHierarchical(@PathVariable userId: Long): Map<String, Any?> {
return pickOrderService.getAllPickOrderLotsWithDetailsHierarchical(userId)
}
@PostMapping("/auto-assign-release-by-ticket")
/* @PostMapping("/auto-assign-release-by-ticket")
fun autoAssignAndReleasePickOrderByTicket(
@RequestParam storeId: String,
@RequestParam ticketNo: String,
@@ -281,6 +283,7 @@ fun autoAssignAndReleasePickOrderByTicket(
): MessageResponse {
return pickOrderService.autoAssignAndReleasePickOrderByStoreAndTicket(storeId, ticketNo, userId)
}
*/
@GetMapping("/orders-by-store/{storeId}")
fun getPickOrdersByStore(@PathVariable storeId: String): Map<String, Any?> {
return pickOrderService.getPickOrdersByDateAndStore(storeId)


+ 56
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcess.kt View File

@@ -0,0 +1,56 @@
package com.ffii.fpsms.modules.productProcess.entity
import com.fasterxml.jackson.annotation.JsonManagedReference
import com.ffii.core.entity.BaseEntity
import com.ffii.fpsms.modules.jobOrder.entity.JobOrder
import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.modules.productProcess.enums.ProductProcessStatus
import com.ffii.fpsms.modules.productProcess.enums.ProductProcessStatusConverter
import jakarta.persistence.*
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Size
import java.time.LocalDate
import java.time.LocalDateTime
import com.ffii.fpsms.modules.master.entity.Bom
@Entity
@Table(name = "productprocess")
open class ProductProcess : BaseEntity<Long>() {
@Size(max = 50)
@NotNull
@Column(name = "productprocesscode", nullable = false, unique = true, length = 50)
open var productProcessCode: String? = null
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "jobOrderId")
open var jobOrder: JobOrder? = null

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "itemsId")
open var item: Items? = null

@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bomId", nullable = false)
open var bom: Bom? = null
@NotNull
@Column(name = "status", nullable = false, length = 20)
@Convert(converter = ProductProcessStatusConverter::class)
open var status: ProductProcessStatus = ProductProcessStatus.PENDING
@Column(name = "startTime")
open var startTime: LocalDateTime? = null
@Column(name = "endTime")
open var endTime: LocalDateTime? = null
@Column(name = "date")
open var date: LocalDate? = null
@JsonManagedReference
@OneToMany(mappedBy = "productProcess", cascade = [CascadeType.ALL], orphanRemoval = true)
open var productionScheduleLines: MutableList<ProductProcessLine> = mutableListOf()



}

+ 106
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt View File

@@ -0,0 +1,106 @@
package com.ffii.fpsms.modules.productProcess.entity
import com.fasterxml.jackson.annotation.JsonBackReference
import com.ffii.core.entity.BaseEntity
import com.ffii.fpsms.modules.master.entity.Equipment
import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.modules.user.entity.User
import jakarta.persistence.*
import jakarta.validation.constraints.Size

import java.time.LocalDateTime
import com.ffii.fpsms.modules.master.entity.BomProcess
@Entity
@Table(name = "productprocessline")
open class ProductProcessLine : BaseEntity<Long>() {

// ✅ 添加 @ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "operatorId")
open var operator: User? = null
// ✅ 添加 @ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "equipmentId")
open var equipment: Equipment? = null
@Size(max = 100)
@Column(name = "name", length = 100)
open var name: String? = null
@Column(name = "description")
open var description: String? = null
@Size(max = 100)
@Column(name = "equipment_name", length = 100)
open var equipmentType: String? = null
// ✅ 添加 @ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "byproductId")
open var byproduct: Items? = null
@Column(name = "status")
open var status: String? = null
@Size(max = 100)
@Column(name = "byproductName", length = 100)
open var byproductName: String? = null
@Column(name = "byproductQty", precision = 16, scale = 2)
open var byproductQty: Int? = null
@Size(max = 20)
@Column(name = "byproductUom", length = 20)
open var byproductUom: String? = null
@Column(name = "scrapQty", precision = 16, scale = 2)
open var scrapQty: Int? = null
@Size(max = 20)
@Column(name = "scrapUom", length = 20)
open var scrapUom: String? = null
@Column(name = "defectQty", precision = 16, scale = 2)
open var defectQty: Int? = null
@Size(max = 20)
@Column(name = "defectUom", length = 20)
open var defectUom: String? = null
@Column(name = "outputFromProcessQty", precision = 16, scale = 2)
open var outputFromProcessQty: Int? = null
@Size(max = 20)
@Column(name = "outputFromProcessUom", length = 20)
open var outputFromProcessUom: String? = null
@Column(name = "startTime")
open var startTime: LocalDateTime? = null
@Column(name = "endTime")
open var endTime: LocalDateTime? = null
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bomProcessId")
open var bomProcess: BomProcess? = null
@Column(name = "seqNo")
open var seqNo: Long? = null
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "handlerId")
open var handler: User? = null
@JsonBackReference
@ManyToOne
@JoinColumn(name = "productprocessId", nullable = false)
open var productProcess: ProductProcess = ProductProcess()
data class ProductProcessLineDetail(
val id: Long?,
val name: String?,
val description: String?,
val equipmentType: String?,
val status: String?,
val byproduct: Items?,
val byproductName: String?,
val byproductQty: Int?,
val byproductUom: String?,
val scrapQty: Int?,
val scrapUom: String?,
)
}

+ 11
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLineRepository.kt View File

@@ -0,0 +1,11 @@
package com.ffii.fpsms.modules.productProcess.entity

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface ProductProcessLineRepository : JpaRepository<ProductProcessLine, Long> {
fun findByProductProcess_Id(productProcessId: Long): List<ProductProcessLine>
fun findByProductProcess_IdAndHandler_Id(productProcessId: Long, handlerId: Long): List<ProductProcessLine>
fun findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId: Long): List<ProductProcessLine>
}

+ 14
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessRepository.kt View File

@@ -0,0 +1,14 @@
package com.ffii.fpsms.modules.productProcess.entity

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.JpaSpecificationExecutor
import org.springframework.stereotype.Repository
import java.util.*

@Repository
interface ProductProcessRepository : JpaRepository<ProductProcess, Long>, JpaSpecificationExecutor<ProductProcess> {
fun findByProductProcessCode(code: String): Optional<ProductProcess>
fun findByJobOrder_Id(jobOrderId: Long): List<ProductProcess>
fun findByProductProcessCodeStartingWith(prefix: String): List<ProductProcess>
fun findByIdAndDeletedIsFalse(id: Long): Optional<ProductProcess>
}

+ 39
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssue.kt View File

@@ -0,0 +1,39 @@
package com.ffii.fpsms.modules.productProcess.entity

import com.ffii.core.entity.BaseEntity
import com.ffii.fpsms.modules.user.entity.User
import jakarta.persistence.*
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Size
import java.time.LocalDateTime

@Entity
@Table(name = "productionprocessissue") // ✅ 修复:改为正确的表名
open class ProductionProcessIssue : BaseEntity<Long>() { // ✅ 修复:改为正确的类名
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "productprocessid", nullable = false)
open var productProcess: ProductProcess? = null
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "operatorId")
open var operator: User? = null
@Size(max = 100)
@Column(name = "operatorName", length = 100)
open var operatorName: String? = null
@NotNull
@Column(name = "reason", nullable = false)
open var reason: String? = null
@NotNull
@Column(name = "stopTime", nullable = false)
open var stopTime: LocalDateTime? = null
@Column(name = "resumeTime")
open var resumeTime: LocalDateTime? = null
@Column(name = "totalTime")
open var totalTime: Int? = null // 分钟
}

+ 9
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssueRepository.kt View File

@@ -0,0 +1,9 @@
package com.ffii.fpsms.modules.productProcess.entity

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface ProductionProcessIssueRepository : JpaRepository<ProductionProcessIssue, Long> {
fun findByProductProcess_Id(productProcessId: Long): List<ProductionProcessIssue>
}

+ 48
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt View File

@@ -0,0 +1,48 @@
package com.ffii.fpsms.modules.productProcess.entity.projections

import java.time.LocalDateTime
import java.time.LocalDate
import com.ffii.fpsms.modules.productProcess.enums.ProductProcessStatus
import com.fasterxml.jackson.annotation.JsonFormat


data class ProductProcessInfo(
val id: Long?,
val productProcessCode: String?,
val status: ProductProcessStatus?,
@JsonFormat(pattern = "yyyy-MM-dd")
val startTime: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd")
val endTime: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd")
val date: LocalDate?,
val bomId: Long?,
val jobOrderId: Long?,
val productProcessLines: List<ProductProcessLineInfo>?
)

data class ProductProcessLineInfo(
val id:Long?,
val bomprocessId: Long?,
val operatorId: Long?,
val equipmentId: Long?,
val handlerId: Long?,
val seqNo: Long?,
val name: String?,
val description: String?,
val equipment_name: String?,
val status: String?,
val byproductId: Long?,
val byproductName: String?,
val byproductQty: Int?,
val byproductUom: String?,
val scrapQty: Int?,
val defectQty: Int?,
val defectUom: String?,
val outputFromProcessQty: Int?,
val outputFromProcessUom: String?,
@JsonFormat(pattern = "yyyy-MM-dd")
val startTime: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd")
val endTime: LocalDateTime?
)

+ 19
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatus.kt View File

@@ -0,0 +1,19 @@
package com.ffii.fpsms.modules.productProcess.enums

enum class ProductProcessStatus(val value: String) {
PENDING("pending"),
IN_PROGRESS("in_progress"),
STOPPED("stopped"),
COMPLETED("completed"),
CANCELLED("cancelled");
companion object {
fun fromValue(value: String): ProductProcessStatus {
return values().find { it.value == value }
?: throw IllegalArgumentException("Unknown status: $value")
}
}
}




+ 15
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatusConverter.kt View File

@@ -0,0 +1,15 @@
package com.ffii.fpsms.modules.productProcess.enums

import jakarta.persistence.AttributeConverter
import jakarta.persistence.Converter

@Converter(autoApply = true)
class ProductProcessStatusConverter : AttributeConverter<ProductProcessStatus, String> {
override fun convertToDatabaseColumn(attribute: ProductProcessStatus?): String? {
return attribute?.value
}

override fun convertToEntityAttribute(dbData: String?): ProductProcessStatus? {
return dbData?.let { ProductProcessStatus.fromValue(it) }
}
}

+ 616
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt View File

@@ -0,0 +1,616 @@
package com.ffii.fpsms.modules.productProcess.service

import com.ffii.fpsms.modules.productProcess.entity.*
import com.ffii.fpsms.modules.productProcess.entity.projections.ProductProcessInfo
import com.ffii.fpsms.modules.productProcess.entity.projections.ProductProcessLineInfo
import com.ffii.fpsms.modules.productProcess.enums.ProductProcessStatus
import com.ffii.fpsms.modules.productProcess.web.model.*
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit
import com.ffii.fpsms.modules.user.entity.UserRepository
import com.ffii.fpsms.modules.user.entity.User
import java.time.format.DateTimeFormatter
import com.ffii.fpsms.modules.master.entity.BomRepository
import com.ffii.fpsms.modules.master.entity.BomProcessRepository
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.entity.ProcessRepository
import com.ffii.fpsms.modules.master.entity.EquipmentRepository
import java.math.BigDecimal
import com.ffii.fpsms.modules.master.entity.Process
import com.ffii.fpsms.modules.master.entity.Equipment
import com.ffii.fpsms.modules.master.entity.BomProcess
import com.ffii.fpsms.modules.master.entity.Bom
import com.ffii.fpsms.modules.master.web.models.MessageResponse
@Service
@Transactional
open class ProductProcessService(
private val productProcessRepository: ProductProcessRepository,
private val productProcessLineRepository: ProductProcessLineRepository,
private val productionProcessIssueRepository: ProductionProcessIssueRepository,
private val bomRepository: BomRepository,
private val bomProcessRepository: BomProcessRepository,
private val jobOrderRepository: JobOrderRepository,
private val itemsRepository: ItemsRepository,
private val userRepository: UserRepository,
private val processRepository: ProcessRepository,
private val equipmentRepository: EquipmentRepository,
) {
open fun findAll(pageable: Pageable): Page<ProductProcess> {
println("📋 Service: Finding all ProductProcess with page: ${pageable.pageNumber}, size: ${pageable.pageSize}")
val result = productProcessRepository.findAll(pageable)
println("✅ Service: Found ${result.totalElements} records")
return result
}
open fun findById(id: Long): ProductProcess {
println("🔍 Service: Finding ProductProcess by ID: $id")

return productProcessRepository.findById(id)
.orElseThrow { IllegalArgumentException("ProductProcess not found with id: $id") }
}
open fun findByCode(code: String): ProductProcess {
println("🔍 Service: Finding ProductProcess by code: $code")
return productProcessRepository.findByProductProcessCode(code)
.orElseThrow { IllegalArgumentException("ProductProcess not found with code: $code") }
}
open fun create(request: SaveProductProcessRequest): SaveProductProcessResponse {
println("🔧 Service: Creating ProductProcess with bomId: ${request.bomId}")
// 1. 查询 BOM
val bom = bomRepository.findById(request.bomId)
.orElseThrow { IllegalArgumentException("BOM not found with id: ${request.bomId}") }

println("📦 Service: BOM found - name: ${bom.name}, item: ${bom.item?.name ?: "N/A"}")

// 2. 生成 productProcessCode(格式:PP-20251026-001)
//val datePrefix = (request.date ?: LocalDate.now()).format(DateTimeFormatter.ofPattern("yyyyMMdd"))
val datePrefix = ( LocalDate.now()).format(DateTimeFormatter.ofPattern("yyyyMMdd"))
val productProcessCode = generateProductProcessCode(datePrefix)
println("🔢 Service: Generated code: $productProcessCode")
// 3. 创建 ProductProcess
val productProcess = ProductProcess().apply {
this.productProcessCode = productProcessCode
this.bom = bom
this.item = bom.item
this.jobOrder = request.jobOrderId?.let { jobOrderRepository.findById(it).orElse(null) }
this.status = ProductProcessStatus.PENDING
//this.date = request.date ?: LocalDate.now()
this.date = LocalDate.now()
}
val savedProcess = productProcessRepository.save(productProcess)
println("✅ Service: ProductProcess created with ID: ${savedProcess.id}")
// 4. 查询 BOM 的所有工序步骤
val bomProcesses = bomProcessRepository.findByBomIdOrderBySeqNo(request.bomId)
println("🔍 Service: Found ${bomProcesses.size} BOM processes")
// 5. 为每个 BOM Process 创建 ProductProcessLine
bomProcesses.forEachIndexed { index, bomProcess -> // ✅ 修复 forEach
val line = ProductProcessLine().apply {
this.productProcess = savedProcess
this.bomProcess = bomProcess
this.seqNo = bomProcess.seqNo
this.name = bomProcess.process?.name
this.description = bomProcess.description
this.equipmentType = bomProcess.equipment?.name
}
productProcessLineRepository.save(line)
println("➕ Service: Created line ${index + 1} - seq: ${line.seqNo}, name: ${line.name}")
}
println("✅ Service: All ${bomProcesses.size} lines created automatically")
return SaveProductProcessResponse(
id = savedProcess.id!!,
productProcessCode = productProcessCode,
linesCreated = bomProcesses.size
)
}
private fun generateProductProcessCode(datePrefix: String): String {
val searchPattern = "PP-$datePrefix-"
val existingCodes = productProcessRepository.findByProductProcessCodeStartingWith(searchPattern)
val nextNumber = (existingCodes.size + 1).toString().padStart(3, '0')
return "$searchPattern$nextNumber"
}

// ✅ 添加:查询工序的所有步骤
open fun getLines(productProcessId: Long): List<ProductProcessLine> {
println("📋 Service: Getting lines for process ID: $productProcessId")
val lines = productProcessLineRepository.findByProductProcess_Id(productProcessId)
println("✅ Service: Found ${lines.size} lines")
return lines
}
open fun startProcess(id: Long, operatorId: Long): ProductProcess {
println("▶️ Service: Starting ProductProcess ID: $id with operatorId: $operatorId")
val productProcess = findById(id)
productProcess.status = ProductProcessStatus.IN_PROGRESS
productProcess.startTime = LocalDateTime.now()
val saved = productProcessRepository.save(productProcess)
println("✅ Service: Process started, status: ${saved.status}")
return saved
}
open fun stopProcess(id: Long, request: StopProcessRequest): ProductionProcessIssue {
println("⏸️ Service: Stopping ProductProcess ID: $id")
println("📦 Service: Stop reason: ${request.reason}")
val productProcess = findById(id)
productProcess.status = ProductProcessStatus.STOPPED
val issue = ProductionProcessIssue().apply {
this.productProcess = productProcess
this.operatorName = request.operatorName
this.reason = request.reason
this.stopTime = LocalDateTime.now()
}
productProcessRepository.save(productProcess)
val savedIssue = productionProcessIssueRepository.save(issue)
println("✅ Service: Process stopped, issue ID: ${savedIssue.id}")
return savedIssue
}
open fun resumeProcess(id: Long, issueId: Long): ProductionProcessIssue {
println("▶️ Service: Resuming ProductProcess ID: $id, issueId: $issueId")
val productProcess = findById(id)
productProcess.status = ProductProcessStatus.IN_PROGRESS
val issue = productionProcessIssueRepository.findById(issueId)
.orElseThrow { IllegalArgumentException("Issue not found") }
issue.resumeTime = LocalDateTime.now()
issue.totalTime = ChronoUnit.MINUTES.between(issue.stopTime, issue.resumeTime).toInt()
productProcessRepository.save(productProcess)
val savedIssue = productionProcessIssueRepository.save(issue)
println("✅ Service: Process resumed, total stop time: ${savedIssue.totalTime} minutes")
return savedIssue
}
open fun completeProcess(id: Long): ProductProcess {
println("✔️ Service: Completing ProductProcess ID: $id")
val productProcess = findById(id)
productProcess.status = ProductProcessStatus.COMPLETED
productProcess.endTime = LocalDateTime.now()
val saved = productProcessRepository.save(productProcess)
println("✅ Service: Process completed")
return saved
}
open fun addLine(productProcessId: Long, request: SaveProductProcessLineRequest): SaveProductProcessLineResponse {
println("➕ Service: Adding line to ProductProcess ID: $productProcessId")
println("📦 Service: Line data - name: ${request.name}, equipmentType: ${request.equipmentType}")
val productProcess = findById(productProcessId)
val line = ProductProcessLine().apply {
this.productProcess = productProcess
this.name = request.name
this.description = request.description
this.equipmentType = request.equipmentType
}
val saved = productProcessLineRepository.save(line)
println("✅ Service: Line added with ID: ${saved.id}")
return SaveProductProcessLineResponse(saved.id!!)
}
open fun updateLineOutput(lineId: Long, request: UpdateLineOutputRequest): ProductProcessLine {
println("📊 Service: Updating line output for line ID: $lineId")
val line = productProcessLineRepository.findById(lineId)
.orElseThrow { IllegalArgumentException("Line not found") }
line.apply {
outputFromProcessQty = request.outputQty
outputFromProcessUom = request.outputUom
defectQty = request.defectQty
defectUom = request.defectUom
scrapQty = request.scrapQty
scrapUom = request.scrapUom
byproductName = request.byproductName
byproductQty = request.byproductQty
byproductUom = request.byproductUom
endTime = LocalDateTime.now()
}
val saved = productProcessLineRepository.save(line)
println("✅ Service: Line output updated")
return saved
}
open fun getIssues(productProcessId: Long): List<ProductionProcessIssue> {
println("📋 Service: Getting issues for ProductProcess ID: $productProcessId")
val issues = productionProcessIssueRepository.findByProductProcess_Id(productProcessId)
println("✅ Service: Found ${issues.size} issues")
return issues
}
open fun findByJobOrderId(jobOrderId: Long): List<ProductProcess> {
println("🔍 Service: Finding ProductProcess by jobOrderId: $jobOrderId")
val result = productProcessRepository.findByJobOrder_Id(jobOrderId)
println("✅ Service: Found ${result.size} processes")
return result
}
open fun findByJobOrderIdWithLines(jobOrderId: Long): List<ProductProcessWithLinesResponse> {
println("🔍 Service: Finding ProductProcess with lines by jobOrderId: $jobOrderId")
val processes = productProcessRepository.findByJobOrder_Id(jobOrderId)
val result = processes.map { process ->
val lines = productProcessLineRepository.findByProductProcess_Id(process.id!!)
ProductProcessWithLinesResponse(
id = process.id!!,
productProcessCode = process.productProcessCode,
status = process.status.value,
startTime = process.startTime,
endTime = process.endTime,
date = process.date,
lines = lines.map { line ->
ProductProcessLineResponse(
id = line.id!!,
seqNo = line.seqNo,
name = line.name,
description = line.description,
equipmentType = line.equipmentType,
startTime = line.startTime,
endTime = line.endTime,
outputFromProcessQty = line.outputFromProcessQty,
outputFromProcessUom = line.outputFromProcessUom,
defectQty = line.defectQty,
scrapQty = line.scrapQty,
byproductName = line.byproductName,
byproductQty = line.byproductQty
)
}
)
}
println("✅ Service: Returning ${result.size} processes with ${result.sumOf { it.lines.size }} total lines")
return result
}
open fun findAllAsDto(pageable: Pageable): Page<ProductProcessSimpleResponse> {
println("📋 Service: Finding all ProductProcess as DTO with page: ${pageable.pageNumber}, size: ${pageable.pageSize}")
val entityPage = productProcessRepository.findAll(pageable)
val dtoList = entityPage.content.map { entity ->
ProductProcessSimpleResponse(
id = entity.id!!,
productProcessCode = entity.productProcessCode,
status = entity.status.value,
startTime = entity.startTime,
endTime = entity.endTime,
date = entity.date,
bomId = entity.bom?.id,
jobOrderId = entity.jobOrder?.id
)
}
println("✅ Service: Converted ${dtoList.size} entities to DTOs")
return org.springframework.data.domain.PageImpl(dtoList, pageable, entityPage.totalElements)
}
open fun findByIdAsDto(id: Long): ProductProcessSimpleResponse {
println("🔍 Service: Finding ProductProcess by ID: $id as DTO")
val entity = productProcessRepository.findById(id)
.orElseThrow { IllegalArgumentException("ProductProcess not found with id: $id") }
return ProductProcessSimpleResponse(
id = entity.id!!,
productProcessCode = entity.productProcessCode,
status = entity.status.value,
startTime = entity.startTime,
endTime = entity.endTime,
date = entity.date,
bomId = entity.bom?.id,
jobOrderId = entity.jobOrder?.id
)
}
open fun stopProcessAsDto(id: Long, request: StopProcessRequest): ProductionProcessIssueResponse {
val issue = stopProcess(id, request)
return ProductionProcessIssueResponse(
id = issue.id!!,
productProcessId = issue.productProcess?.id,
operatorId = issue.operator?.id,
operatorName = issue.operatorName,
reason = issue.reason!!,
stopTime = issue.stopTime!!,
resumeTime = issue.resumeTime,
totalTime = issue.totalTime
)
}
open fun resumeProcessAsDto(id: Long, issueId: Long): ProductionProcessIssueResponse {
val issue = resumeProcess(id, issueId)
return ProductionProcessIssueResponse(
id = issue.id!!,
productProcessId = issue.productProcess?.id,
operatorId = issue.operator?.id,
operatorName = issue.operatorName,
reason = issue.reason!!,
stopTime = issue.stopTime!!,
resumeTime = issue.resumeTime,
totalTime = issue.totalTime
)
}
open fun getIssuesAsDto(productProcessId: Long): List<ProductionProcessIssueResponse> {
val issues = getIssues(productProcessId)
return issues.map { issue ->
ProductionProcessIssueResponse(
id = issue.id!!,
productProcessId = issue.productProcess?.id,
operatorId = issue.operator?.id,
operatorName = issue.operatorName,
reason = issue.reason!!,
stopTime = issue.stopTime!!,
resumeTime = issue.resumeTime,
totalTime = issue.totalTime
)
}
}
open fun startProcessAsDto(id: Long, operatorId: Long): ProductProcessSimpleResponse {
val entity = startProcess(id, operatorId)
return ProductProcessSimpleResponse(
id = entity.id!!,
productProcessCode = entity.productProcessCode,
status = entity.status.value,
startTime = entity.startTime,
endTime = entity.endTime,
date = entity.date,
bomId = entity.bom?.id,
jobOrderId = entity.jobOrder?.id
)
}
open fun completeProcessAsDto(id: Long): ProductProcessSimpleResponse {
val entity = completeProcess(id)
return ProductProcessSimpleResponse(
id = entity.id!!,
productProcessCode = entity.productProcessCode,
status = entity.status.value,
startTime = entity.startTime,
endTime = entity.endTime,
date = entity.date,
bomId = entity.bom?.id,
jobOrderId = entity.jobOrder?.id
)
}
open fun updateLineOutputAsDto(lineId: Long, request: UpdateLineOutputRequest): ProductProcessLineResponse {
val entity = updateLineOutput(lineId, request)
return ProductProcessLineResponse(
id = entity.id!!,
seqNo = entity.seqNo,
name = entity.name,
description = entity.description,
equipmentType = entity.equipmentType,
startTime = entity.startTime,
endTime = entity.endTime,
outputFromProcessQty = entity.outputFromProcessQty,
outputFromProcessUom = entity.outputFromProcessUom,
defectQty = entity.defectQty,
scrapQty = entity.scrapQty,
byproductName = entity.byproductName,
byproductQty = entity.byproductQty
)
}
open fun getLinesAsDto(productProcessId: Long): List<ProductProcessLineResponse> {
val lines = getLines(productProcessId)
return lines.map { line ->
ProductProcessLineResponse(
id = line.id!!,
seqNo = line.seqNo,
name = line.name,
description = line.description,
equipmentType = line.equipmentType,
startTime = line.startTime,
endTime = line.endTime,
outputFromProcessQty = line.outputFromProcessQty,
outputFromProcessUom = line.outputFromProcessUom,
defectQty = line.defectQty,
scrapQty = line.scrapQty,
byproductName = line.byproductName,
byproductQty = line.byproductQty
)
}
}
open fun startLine(lineId: Long, userId: Long): ProductProcessLine {
println("▶️ Service: Starting ProductProcessLine ID: $lineId with userId: $userId")
val line = productProcessLineRepository.findById(lineId)
.orElseThrow { IllegalArgumentException("Line not found") }
// ✅ 修复:使用 UserRepository 获取 User 对象
val user = userRepository.findById(userId)
.orElseThrow { IllegalArgumentException("User not found with id: $userId") }
line.handler = user
line.startTime = LocalDateTime.now()
val saved = productProcessLineRepository.save(line)
println("✅ Service: Line started, handlerId: ${saved.handler?.id}")
return saved
}
open fun findPendingLinesByHandlerId(handlerId: Long): List<ProductProcessLine> {
println("🔍 Service: Finding pending lines for handlerId: $handlerId")
val lines = productProcessLineRepository.findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId)
println("✅ Service: Found ${lines.size} pending lines")
return lines
}
open fun productProcessDetailfindbyjoid(joid: Long): List<ProductProcessInfo> {
val productProcesses = productProcessRepository.findByJobOrder_Id(joid)
//val processId = productProcess.id ?: 0
return productProcesses.map { process ->
ProductProcessInfo(
id = process.id?:0,
bomId = process.bom?.id?:0,
jobOrderId = process.jobOrder?.id?:0,
productProcessCode = process.productProcessCode?:"",
status = process.status?:ProductProcessStatus.PENDING,
startTime = process.startTime?:LocalDateTime.now(),
endTime = process.endTime?:LocalDateTime.now(),
date = process.date?:LocalDate.now(),
productProcessLines = productProcessLineRepository.findByProductProcess_Id(process.id?:0).map { line ->
ProductProcessLineInfo(
id = line.id?:0,
bomprocessId = line.bomProcess?.id?:0,
operatorId = line.operator?.id?:0,
equipmentId = line.equipment?.id?:0,
handlerId = line.handler?.id?:0,
seqNo = line.seqNo?:0,
name = line.name?:"",
description = line.description?:"",
equipment_name = line.equipment?.name?:"",
status = line.status?:"",
byproductId = line.byproduct?.id?:0,
byproductName = line.byproduct?.name?:"",
byproductQty = line.byproductQty?:0,
byproductUom = line.byproductUom?:"",
scrapQty = line.scrapQty?:0,
defectQty = line.defectQty?:0,
defectUom = line.defectUom?:"",
outputFromProcessQty = line.outputFromProcessQty?:0,
outputFromProcessUom = line.outputFromProcessUom?:"",
startTime = line.startTime?:LocalDateTime.now(),
endTime = line.endTime?:LocalDateTime.now()
)
}
)
}
}
open fun productProcessDetailfindbyidtest(id: Long): ProductProcessInfo {
val zero = BigDecimal.ZERO
val productProcess = productProcessRepository.findById(id).orElse(null)
val processId = productProcess.id ?: 0

val productProcessLines = productProcessLineRepository.findByProductProcess_Id(processId)
return ProductProcessInfo(
id = productProcess.id?:0,
bomId = productProcess.bom?.id?:0,
jobOrderId = productProcess.jobOrder?.id?:0,
productProcessCode = productProcess.productProcessCode?:"",
status = productProcess.status?:ProductProcessStatus.PENDING,
startTime = productProcess.startTime?:LocalDateTime.now(),
endTime = productProcess.endTime?:LocalDateTime.now(),
date = productProcess.date?:LocalDate.now(),
productProcessLines = productProcessLines.map { line ->
ProductProcessLineInfo(
id = line.id?:0,
bomprocessId = line.bomProcess?.id?:0,
operatorId = line.operator?.id?:0,
equipmentId = line.equipment?.id?:0,
handlerId = line.handler?.id?:0,
seqNo = line.seqNo?:0,
name = line.name?:"",
description = line.description?:"",
equipment_name = line.equipment?.name?:"",
status = line.status?:"",
byproductId = line.byproduct?.id?:0,
byproductName = line.byproduct?.name?:"",
byproductQty = line.byproductQty?:0,
byproductUom = line.byproductUom?:"",
scrapQty = line.scrapQty?:0,
defectQty = line.defectQty?:0,
defectUom = line.defectUom?:"",
outputFromProcessQty = line.outputFromProcessQty?:0,
outputFromProcessUom = line.outputFromProcessUom?:"",
startTime = line.startTime?:LocalDateTime.now(),
endTime = line.endTime?:LocalDateTime.now()
)
}
)
}
open fun createProductProcessByJobOrderId(jobOrderId: Long): MessageResponse {
val jobOrder = jobOrderRepository.findById(jobOrderId).orElse(null)
val bom = bomRepository.findById(jobOrder?.bom?.id ?: 0L).orElse(null)
// val bom = jobOrder.bom.let { bomRepository.findById(it).orElse(null) }
val bomProcess = bomProcessRepository.findByBomId(jobOrder?.bom?.id?:0L)
val item = itemsRepository.findById(bom?.item?.id ?: 0L).orElse(null)
val datePrefix = ( LocalDate.now()).format(DateTimeFormatter.ofPattern("yyyyMMdd"))
val productProcessCode = generateProductProcessCode(datePrefix)
val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")

val productProcess = ProductProcess().apply {
this.jobOrder = jobOrder
this.productProcessCode =productProcessCode
this.item = item
this.status = ProductProcessStatus.PENDING
this.date = jobOrder?.planEnd?.toLocalDate()
this.bom = bom
}

productProcessRepository.save(productProcess)
bomProcess.forEach { bomProcess ->
val process = processRepository.findById(bomProcess?.process?.id?:0L).orElse(null)
val equipment = equipmentRepository.findById(bomProcess?.equipment?.id?:0L).orElse(null)
val productProcessLine = ProductProcessLine().apply {
this.productProcess = productProcess
this.bomProcess = bomProcess
// this.equipment = equipment
this.seqNo = bomProcess.seqNo?:0
this.name = process?.name?:""
this.description = bomProcess.description?:""
this.equipmentType = equipment?.name?:""
this.status = "Pending"
}
productProcessLineRepository.save(productProcessLine)
}

return MessageResponse(
id = null,
code = null,
name = null,
type = null,
message = null,
errorPosition = null,
)
}
open fun UpdateProductProcessLineOperatorIdOrEquipmentId(request: UpdateProductProcessLineOperatorIdOrEquipmentIdRequest): MessageResponse {
val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null)
val equipmentId = request.equipmentId
val user = userRepository.findById(request.operatorId).orElse(null)
val bomProcess= bomProcessRepository.findById(productProcessLine?.bomProcess?.id?:0L).orElse(null)
val bomProcessEquipment=bomProcess?.equipment
if(equipmentId != null &&( equipmentId==bomProcessEquipment?.id)) {

productProcessLine?.equipment = bomProcessEquipment

productProcessLineRepository.save(productProcessLine)
}
else
{

println("not match")
}
if(user != null) {
productProcessLine.operator = user
productProcessLineRepository.save(productProcessLine)
}


return MessageResponse(
id = null,
code = null,
name = null,
type = null,
message = null,
errorPosition = null,
)
}
}

+ 170
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt View File

@@ -0,0 +1,170 @@
package com.ffii.fpsms.modules.productProcess.web

import com.ffii.fpsms.modules.productProcess.entity.ProductProcess
import com.ffii.fpsms.modules.productProcess.entity.ProductProcessLine
import com.ffii.fpsms.modules.productProcess.service.ProductProcessService
import com.ffii.fpsms.modules.productProcess.web.model.*
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.web.bind.annotation.*
import com.ffii.fpsms.modules.productProcess.entity.projections.ProductProcessInfo
import com.ffii.fpsms.modules.master.web.models.MessageResponse
@RestController
@RequestMapping("/product-process")
class ProductProcessController(
private val productProcessService: ProductProcessService
) {

@GetMapping
fun findAll(pageable: Pageable): Page<ProductProcessSimpleResponse> { // ✅ 改为返回 DTO
println("📋 Controller: GET /product-process - Finding all")
val result = productProcessService.findAllAsDto(pageable) // ✅ 使用新方法
println("✅ Controller: Returning ${result.totalElements} records")
return result
}

@GetMapping("/{id}")
fun findById(@PathVariable id: Long): ProductProcessSimpleResponse { // ✅ 改为返回 DTO
println("🔍 Controller: GET /product-process/$id")
val result = productProcessService.findByIdAsDto(id) // ✅ 使用新方法
println("✅ Controller: Found: ${result.productProcessCode}")
return result
}

@GetMapping("/code/{code}")
fun findByCode(@PathVariable code: String): ProductProcess {
println("🔍 Controller: GET /product-process/code/$code")
val result = productProcessService.findByCode(code)
println("✅ Controller: Found ID: ${result.id}")
return result
}

@PostMapping
fun create(@RequestBody request: SaveProductProcessRequest): SaveProductProcessResponse {
println("💾 Controller: POST /product-process - Creating ProductProcess")
//println("📦 Controller: Request - bomId: ${request.bomId}, jobOrderId: ${request.jobOrderId}, date: ${request.date}") // ✅ 修复
val result = productProcessService.create(request)
println("✅ Controller: Created ProductProcess - ID: ${result.id}, Code: ${result.productProcessCode}, Lines: ${result.linesCreated}") // ✅ 改进日志
return result
}

@PostMapping("/{id}/start")
fun startProcess(@PathVariable id: Long, @RequestParam operatorId: Long): ProductProcessSimpleResponse {
println("▶️ Controller: POST /product-process/$id/start - operatorId: $operatorId")
val result = productProcessService.startProcessAsDto(id, operatorId) // ✅ 改用 AsDto 方法
println("✅ Controller: Process started")
return result
}

@PostMapping("/{id}/stop")
fun stopProcess(
@PathVariable id: Long,
@RequestBody request: StopProcessRequest
): ProductionProcessIssueResponse { // ✅ 改为 Issue Response
println("⏸️ Controller: POST /product-process/$id/stop")
println("📦 Controller: Reason: ${request.reason}")
val result = productProcessService.stopProcessAsDto(id, request) // ✅ 使用返回 DTO 的方法
println("✅ Controller: Process stopped, issue ID: ${result.id}")
return result
}

@PostMapping("/{id}/resume/{issueId}")
fun resumeProcess(
@PathVariable id: Long,
@PathVariable issueId: Long
): ProductionProcessIssueResponse { // ✅ 改为 Issue Response
println("▶️ Controller: POST /product-process/$id/resume/$issueId")
val result = productProcessService.resumeProcessAsDto(id, issueId) // ✅ 使用返回 DTO 的方法
println("✅ Controller: Process resumed, stop duration: ${result.totalTime} min")
return result
}

@PostMapping("/{id}/complete")
fun completeProcess(@PathVariable id: Long): ProductProcessSimpleResponse {
println("✔️ Controller: POST /product-process/$id/complete")
val result = productProcessService.completeProcessAsDto(id) // ✅ 改用 AsDto 方法
println("✅ Controller: Process completed")
return result
}

@PostMapping("/{id}/lines")
fun addLine(
@PathVariable id: Long,
@RequestBody request: SaveProductProcessLineRequest
): SaveProductProcessLineResponse {
println("➕ Controller: POST /product-process/$id/lines")
println("📦 Controller: Line - name: ${request.name}, equipment: ${request.equipmentType}")
val result = productProcessService.addLine(id, request)
println("✅ Controller: Line added with ID: ${result.id}")
return result
}

@PutMapping("/lines/{lineId}/output")
fun updateLineOutput(
@PathVariable lineId: Long,
@RequestBody request: UpdateLineOutputRequest
): ProductProcessLineResponse { // ✅ 改为 ProductProcessLineResponse
println("📊 Controller: PUT /lines/$lineId/output")
val result = productProcessService.updateLineOutputAsDto(lineId, request)
println("✅ Controller: Line output updated")
return result
}

@GetMapping("/{id}/lines")
fun getLines(@PathVariable id: Long): List<ProductProcessLineResponse> {
println("📋 Controller: GET /product-process/$id/lines")
val result = productProcessService.getLinesAsDto(id)
println("✅ Controller: Found ${result.size} lines")
return result
}

@GetMapping("/{id}/issues")
fun getIssues(@PathVariable id: Long): List<ProductionProcessIssueResponse> { // ✅ 改为 Issue Response List
println("📋 Controller: GET /product-process/$id/issues")
val result = productProcessService.getIssuesAsDto(id) // ✅ 使用返回 DTO 的方法
println("✅ Controller: Found ${result.size} issues")
return result
}

@GetMapping("/by-job-order/{jobOrderId}")
fun findByJobOrderId(@PathVariable jobOrderId: Long): List<ProductProcessWithLinesResponse> { // ✅ 改为返回 DTO
println("🔍 Controller: GET /product-process/by-job-order/$jobOrderId")
val result = productProcessService.findByJobOrderIdWithLines(jobOrderId) // ✅ 直接使用 with-lines 方法
println("✅ Controller: Found ${result.size} processes for Job Order $jobOrderId")
return result
}

// ✅ 额外添加:获取带 lines 的完整数据
@GetMapping("/by-job-order/{jobOrderId}/with-lines")
fun findByJobOrderIdWithLines(@PathVariable jobOrderId: Long): List<ProductProcessWithLinesResponse> {
println("🔍 Controller: GET /product-process/by-job-order/$jobOrderId/with-lines")
val result = productProcessService.findByJobOrderIdWithLines(jobOrderId)
println("✅ Controller: Found ${result.size} processes with lines for Job Order $jobOrderId")
return result
}

@PostMapping("/lines/{lineId}/start")
fun startLine(@PathVariable lineId: Long, @RequestParam userId: Long): ProductProcessLineResponse {
println("▶️ Controller: POST /product-process/lines/$lineId/start - userId: $userId")
val entity = productProcessService.startLine(lineId, userId)
// ✅ 修复:返回正确的 DTO
return productProcessService.getLinesAsDto(entity.productProcess?.id!!).find { it.id == entity.id!! }!!
}

@GetMapping("/demo/{id}")
fun demo(@PathVariable id: Long): ProductProcessInfo {
return productProcessService.productProcessDetailfindbyidtest(id)
}
@GetMapping("/demo/joid/{joid}")
fun demojoid(@PathVariable joid: Long): List<ProductProcessInfo> {
return productProcessService.productProcessDetailfindbyjoid(joid)
}
@PostMapping("/demo/joid/{joid}")
fun democreate(@PathVariable joid: Long): MessageResponse {
return productProcessService.createProductProcessByJobOrderId(joid)
}
@PostMapping("/Demo/update")
fun demoupdate(@RequestBody request: UpdateProductProcessLineOperatorIdOrEquipmentIdRequest): MessageResponse {
return productProcessService.UpdateProductProcessLineOperatorIdOrEquipmentId(request)
}
}

+ 92
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt View File

@@ -0,0 +1,92 @@
package com.ffii.fpsms.modules.productProcess.web.model

import java.time.LocalDate
import java.time.LocalDateTime
data class SaveProductProcessRequest(
val bomId: Long,
val jobOrderId: Long?,
)
data class UpdateProductProcessLineOperatorIdOrEquipmentIdRequest(
val productProcessLineId: Long,
val operatorId: Long?,
val equipmentId: Long?
)
data class ProductionProcessIssueResponse(
val id: Long,
val productProcessId: Long?,
val operatorId: Long?,
val operatorName: String?,
val reason: String,
val stopTime: LocalDateTime,
val resumeTime: LocalDateTime?,
val totalTime: Int? // 分钟
)
data class SaveProductProcessResponse(
val id: Long,
val productProcessCode: String? = null, // ✅ 添加
val linesCreated: Int = 0 // ✅ 添加
)
data class ProductProcessWithLinesResponse(
val id: Long,
val productProcessCode: String?,
val status: String,
val startTime: LocalDateTime?,
val endTime: LocalDateTime?,
val date: LocalDate?,
val lines: List<ProductProcessLineResponse>
)

data class ProductProcessSimpleResponse(
val id: Long,
val productProcessCode: String?,
val status: String,
val startTime: LocalDateTime?,
val endTime: LocalDateTime?,
val date: LocalDate?,
val bomId: Long?,
val jobOrderId: Long?
)
data class ProductProcessLineResponse(
val id: Long,
val seqNo: Long?,
val name: String?,
val description: String?,
val equipmentType: String?,
val startTime: LocalDateTime?,
val endTime: LocalDateTime?,
val outputFromProcessQty: Int?,
val outputFromProcessUom: String?,
val defectQty: Int?,
val scrapQty: Int?,
val byproductName: String?,
val byproductQty: Int?
)
data class SaveProductProcessLineRequest(
val name: String?,
val description: String?,
val equipmentType: String?,
val operatorId: Long?,
val equipmentId: Long?
)

data class SaveProductProcessLineResponse(
val id: Long
)

data class StopProcessRequest(
val operatorId: Long?,
val operatorName: String?,
val reason: String
)

data class UpdateLineOutputRequest(
val outputQty: Int?,
val outputUom: String?,
val defectQty: Int?,
val defectUom: String?,
val scrapQty: Int?,
val scrapUom: String?,
val byproductName: String?,
val byproductQty: Int?,
val byproductUom: String?
)

+ 3
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt View File

@@ -368,12 +368,14 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long {
// 3) 全部完成 → 更新 do_pick_order 为 completed,并可复制到 record 表(可选)
val dpo = doPickOrderRepository.findById(dpoId).orElse(null) ?: return@forEach

dpo.ticketStatus = DoPickOrderStatus.completed
dpo.ticketCompleteDateTime = LocalDateTime.now()
doPickOrderRepository.save(dpo)
// 4) 可选:复制 header 到 record 表,复制 lines 到 line_record,再删除原有行/头(与你在 PickOrderService.completeStockOut 的做法保持一致)
val dpoRecord = DoPickOrderRecord(
recordId =dpoId,
storeId = dpo.storeId ?: "",
ticketNo = dpo.ticketNo ?: "",
ticketStatus = DoPickOrderStatus.completed,
@@ -398,6 +400,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long {
val lineRecords = allLines.map { l ->
DoPickOrderLineRecord().apply {
this.recordId = l.id
this.doPickOrderId = savedHeader.id
this.pickOrderId = l.pickOrderId
this.doOrderId = l.doOrderId


+ 106
- 0
src/main/resources/db/changelog/changes/20251026_02_enson/01_alter_table.sql View File

@@ -0,0 +1,106 @@
-- liquibase formatted sql
-- changeset enson:alter_pick_execution_issue_category_20251023

CREATE TABLE productprocess (
id INT AUTO_INCREMENT PRIMARY KEY,
productprocesscode VARCHAR(50) UNIQUE NOT NULL COMMENT '生产工序编号',
jobOrderId INT COMMENT '工单ID',
itemsId INT COMMENT '产品ID',
-- 状态
status VARCHAR(20) DEFAULT 'pending' ,
-- 时间
startTime DATETIME ,
endTime DATETIME ,
date DATE ,
-- 审计字段
created DATETIME,
createdBy VARCHAR(30),
version INT DEFAULT 0,
modified DATETIME,
modifiedBy VARCHAR(30),
deleted TINYINT(1) DEFAULT 0,
FOREIGN KEY (jobOrderId) REFERENCES job_order(id),
FOREIGN KEY (itemsId) REFERENCES items(id),
INDEX idx_status (status),
INDEX idx_date (date),
INDEX idx_job_order (jobOrderId)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


CREATE TABLE productprocessline (
id INT AUTO_INCREMENT PRIMARY KEY,
productprocessid INT NOT NULL ,
-- 操作员和设备
operatorId INT ,
equipmentId INT,
-- 步骤信息
name VARCHAR(100) ,
description varchar(255) ,
equipment VARCHAR(100) ,
-- 副产品信息
byproductId INT ,
byproductName VARCHAR(100) ,
byproductQty DECIMAL(16,2) ,
byproductUom VARCHAR(20) ,
-- 废品/次品/产出
scrapQty DECIMAL(16,2) ,
scrapUom VARCHAR(20) ,
defectQty DECIMAL(16,2) ,
defectUom VARCHAR(20) ,
outputFromProcessQty DECIMAL(16,2) ,
outputFromProcessUom VARCHAR(20) ,
-- 时间
startTime DATETIME ,
endTime DATETIME ,
-- 审计字段
created DATETIME,
createdBy VARCHAR(30),
version INT DEFAULT 0,
modified DATETIME,
modifiedBy VARCHAR(30),
deleted TINYINT(1) DEFAULT 0,
FOREIGN KEY (productprocessid) REFERENCES productprocess(id),
FOREIGN KEY (operatorId) REFERENCES user(id),
FOREIGN KEY (equipmentId) REFERENCES equipment(id),
FOREIGN KEY (byproductId) REFERENCES items(id),
INDEX idx_productprocess (productprocessid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE productionprocessissue (
id INT AUTO_INCREMENT PRIMARY KEY,
productprocessid INT NOT NULL ,
operatorId INT ,
operatorName VARCHAR(100) ,
-- 暂停信息
reason varchar(255) NOT NULL ,
stopTime DATETIME NOT NULL ,
resumeTime DATETIME ,
totalTime INT ,
-- 审计字段
created DATETIME,
createdBy VARCHAR(30),
version INT DEFAULT 0,
modified DATETIME,
modifiedBy VARCHAR(30),
deleted TINYINT(1) DEFAULT 0,
FOREIGN KEY (productprocessid) REFERENCES productprocess(id),
FOREIGN KEY (operatorId) REFERENCES user(id),
INDEX idx_productprocess (productprocessid),
INDEX idx_stop_time (stopTime)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

+ 12
- 0
src/main/resources/db/changelog/changes/20251026_02_enson/02_alter_table.sql View File

@@ -0,0 +1,12 @@
-- liquibase formatted sql
-- changeset enson:alter_pick_execution_issue_category_20251023

ALTER TABLE productprocess
ADD COLUMN bomId INT COMMENT 'BOM ID';

ALTER TABLE productprocess
ADD CONSTRAINT fk_productprocess_bom
FOREIGN KEY (bomId) REFERENCES bom(id);

-- 添加索引
CREATE INDEX idx_productprocess_bom ON productprocess(bomId);

+ 15
- 0
src/main/resources/db/changelog/changes/20251026_02_enson/03_alter_table.sql View File

@@ -0,0 +1,15 @@
-- liquibase formatted sql
-- changeset enson:alter_pick_execution_issue_category_20251023

ALTER TABLE productprocessline
ADD COLUMN bomProcessId INT ,
ADD COLUMN seqNo INT;

ALTER TABLE productprocessline
ADD CONSTRAINT fk_productprocessline_bomprocess
FOREIGN KEY (bomProcessId) REFERENCES bom_process(id);

-- 添加索引

CREATE INDEX idx_productprocessline_bomprocess ON productprocessline(bomProcessId);
CREATE INDEX idx_productprocessline_seqno ON productprocessline(seqNo);

+ 5
- 0
src/main/resources/db/changelog/changes/20251028_01_enson/01_producutprocess.sql View File

@@ -0,0 +1,5 @@
-- liquibase formatted sql
-- changeset Enson:alter qc category table

ALTER TABLE `productprocessline`
ADD COLUMN `handlerId` INT NULL AFTER `equipmentId`;

+ 12
- 0
src/main/resources/db/changelog/changes/20251028_01_enson/02_producutprocess.sql View File

@@ -0,0 +1,12 @@
-- liquibase formatted sql
-- changeset Enson:alter qc category table
ALTER TABLE `productprocessline`
CHANGE COLUMN `equipment` `equipment_name` VARCHAR(255);
ALTER TABLE `productprocessline`
ADD COLUMN `status` VARCHAR(255) DEFAULT 'pending' AFTER `equipment_name`;

ALTER TABLE `productprocessline`
MODIFY COLUMN `bomprocessId` INT AFTER `productprocessId`,
MODIFY COLUMN `seqNo` INT AFTER `handlerId`;



+ 17
- 0
src/main/resources/db/changelog/changes/20251098_01_enson/03_altertable_enson.sql View File

@@ -0,0 +1,17 @@
-- liquibase formatted sql
-- changeset enson:altertable_enson

ALTER TABLE `fpsmsdb`.`pick_execution_issue`
DROP FOREIGN KEY `FK_PICK_EXECUTION_ISSUE_ON_PICK_ORDER`,
DROP FOREIGN KEY `FK_PICK_EXECUTION_ISSUE_ON_PICK_ORDER_LINE`;
ALTER TABLE `fpsmsdb`.`pick_execution_issue`
CHANGE COLUMN `pick_order_id` `pick_order_id` INT NULL ,
CHANGE COLUMN `pick_order_code` `pick_order_code` VARCHAR(50) NULL ,
CHANGE COLUMN `pick_order_line_id` `pick_order_line_id` INT NULL ;
ALTER TABLE `fpsmsdb`.`pick_execution_issue`
ADD CONSTRAINT `FK_PICK_EXECUTION_ISSUE_ON_PICK_ORDER`
FOREIGN KEY (`pick_order_id`)
REFERENCES `fpsmsdb`.`pick_order` (`id`),
ADD CONSTRAINT `FK_PICK_EXECUTION_ISSUE_ON_PICK_ORDER_LINE`
FOREIGN KEY (`pick_order_line_id`)
REFERENCES `fpsmsdb`.`pick_order_line` (`id`);

+ 7
- 0
src/main/resources/db/changelog/changes/20251103_01_enson/01_altertable_enson.sql View File

@@ -0,0 +1,7 @@
-- liquibase formatted sql
-- changeset enson:altertable_enson

ALTER TABLE `fpsmsdb`.`do_pick_order_record`
ADD COLUMN `record_id` INT NOT NULL After `id`;
ALTER TABLE `fpsmsdb`.`do_pick_order_line_record`
ADD COLUMN `record_id` INT NOT NULL After `id`;

Loading…
Cancel
Save