From e3fbd3e0db8bc4144ee50f991b42c4e9ab6bf904 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Mon, 3 Nov 2025 16:08:53 +0800 Subject: [PATCH] update --- .../entity/DoPickOrderLineRecord.kt | 2 + .../deliveryOrder/entity/DoPickOrderRecord.kt | 5 +- .../service/DoPickOrderCompletionService.kt | 4 +- .../service/DoPickOrderService.kt | 5 +- .../master/entity/BomProcessRepository.kt | 2 + .../pickOrder/service/PickOrderService.kt | 7 +- .../pickOrder/web/PickOrderController.kt | 5 +- .../productProcess/entity/ProductProcess.kt | 56 ++ .../entity/ProductProcessLine.kt | 106 +++ .../entity/ProductProcessLineRepository.kt | 11 + .../entity/ProductProcessRepository.kt | 14 + .../entity/ProductionProcessIssue.kt | 39 ++ .../ProductionProcessIssueRepository.kt | 9 + .../entity/projections/ProductProcessInfo.kt | 48 ++ .../enums/ProductProcessStatus.kt | 19 + .../enums/ProductProcessStatusConverter.kt | 15 + .../service/ProductProcessService.kt | 616 ++++++++++++++++++ .../web/ProductProcessController.kt | 170 +++++ .../web/model/SaveProductProcessRequest.kt | 92 +++ .../stock/service/StockOutLineService.kt | 3 + .../20251026_02_enson/01_alter_table.sql | 106 +++ .../20251026_02_enson/02_alter_table.sql | 12 + .../20251026_02_enson/03_alter_table.sql | 15 + .../20251028_01_enson/01_producutprocess.sql | 5 + .../20251028_01_enson/02_producutprocess.sql | 12 + .../20251098_01_enson/03_altertable_enson.sql | 17 + .../20251103_01_enson/01_altertable_enson.sql | 7 + 27 files changed, 1396 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcess.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLineRepository.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessRepository.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssue.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssueRepository.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatus.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatusConverter.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt create mode 100644 src/main/resources/db/changelog/changes/20251026_02_enson/01_alter_table.sql create mode 100644 src/main/resources/db/changelog/changes/20251026_02_enson/02_alter_table.sql create mode 100644 src/main/resources/db/changelog/changes/20251026_02_enson/03_alter_table.sql create mode 100644 src/main/resources/db/changelog/changes/20251028_01_enson/01_producutprocess.sql create mode 100644 src/main/resources/db/changelog/changes/20251028_01_enson/02_producutprocess.sql create mode 100644 src/main/resources/db/changelog/changes/20251098_01_enson/03_altertable_enson.sql create mode 100644 src/main/resources/db/changelog/changes/20251103_01_enson/01_altertable_enson.sql diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderLineRecord.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderLineRecord.kt index b54bdcb..8e71a87 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderLineRecord.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderLineRecord.kt @@ -18,6 +18,8 @@ import java.time.LocalDateTime @Table(name = "do_pick_order_line_record") open class DoPickOrderLineRecord: BaseEntity() { + @Column(name = "record_id") + open var recordId: Long? = null @Column(name = "do_pick_order_id", length = 100) open var doPickOrderId: Long? = null diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecord.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecord.kt index 24e6dd0..4594305 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecord.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecord.kt @@ -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 diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderCompletionService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderCompletionService.kt index 12a5d99..8ec3105 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderCompletionService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderCompletionService.kt @@ -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 } + */ } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt index 1665fdd..a3336c2 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt @@ -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, diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt index 2cd9f59..00c2cac 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt @@ -7,4 +7,6 @@ import java.util.Optional @Repository interface BomProcessRepository : AbstractRepository { fun findBySeqNoAndBomIdAndDeletedIsFalse(seqNo: Int, bomId: Long): BomProcess? + fun findByBomIdOrderBySeqNo(bomId: Long): List + fun findByBomId(bomId: Long): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt index 66df3ce..d54b39e 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt @@ -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> { println("=== Debug: getAllPickOrderLotsWithDetailsWithAutoAssign ===") println("today: ${LocalDate.now()}") diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt index 0fbb619..bc96934 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt @@ -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 { 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 { return pickOrderService.getPickOrdersByDateAndStore(storeId) diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcess.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcess.kt new file mode 100644 index 0000000..3d55b23 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcess.kt @@ -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() { + + @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 = mutableListOf() + + + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt new file mode 100644 index 0000000..4698df6 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt @@ -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() { + + + // ✅ 添加 @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?, + ) +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLineRepository.kt new file mode 100644 index 0000000..f0d572e --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLineRepository.kt @@ -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 { + fun findByProductProcess_Id(productProcessId: Long): List + fun findByProductProcess_IdAndHandler_Id(productProcessId: Long, handlerId: Long): List + fun findByHandler_IdAndStartTimeIsNotNullAndEndTimeIsNull(handlerId: Long): List +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessRepository.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessRepository.kt new file mode 100644 index 0000000..0bfe618 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessRepository.kt @@ -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, JpaSpecificationExecutor { + fun findByProductProcessCode(code: String): Optional + fun findByJobOrder_Id(jobOrderId: Long): List + fun findByProductProcessCodeStartingWith(prefix: String): List + fun findByIdAndDeletedIsFalse(id: Long): Optional +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssue.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssue.kt new file mode 100644 index 0000000..ecd8db1 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssue.kt @@ -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() { // ✅ 修复:改为正确的类名 + + @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 // 分钟 +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssueRepository.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssueRepository.kt new file mode 100644 index 0000000..5b3d633 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductionProcessIssueRepository.kt @@ -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 { + fun findByProductProcess_Id(productProcessId: Long): List +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt new file mode 100644 index 0000000..3d08230 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt @@ -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? +) + +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? +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatus.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatus.kt new file mode 100644 index 0000000..66de4a3 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatus.kt @@ -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") + } + } +} + + + diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatusConverter.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatusConverter.kt new file mode 100644 index 0000000..701d21c --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/enums/ProductProcessStatusConverter.kt @@ -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 { + override fun convertToDatabaseColumn(attribute: ProductProcessStatus?): String? { + return attribute?.value + } + + override fun convertToEntityAttribute(dbData: String?): ProductProcessStatus? { + return dbData?.let { ProductProcessStatus.fromValue(it) } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt new file mode 100644 index 0000000..12471fe --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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, + ) + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt new file mode 100644 index 0000000..720594f --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt @@ -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 { // ✅ 改为返回 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 { + 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 { // ✅ 改为 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 { // ✅ 改为返回 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 { + 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 { + 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) + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt new file mode 100644 index 0000000..67cd4a9 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt @@ -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 +) + +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? +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt index d92fdc0..a6e103b 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt @@ -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 diff --git a/src/main/resources/db/changelog/changes/20251026_02_enson/01_alter_table.sql b/src/main/resources/db/changelog/changes/20251026_02_enson/01_alter_table.sql new file mode 100644 index 0000000..60d8b1b --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251026_02_enson/01_alter_table.sql @@ -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; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20251026_02_enson/02_alter_table.sql b/src/main/resources/db/changelog/changes/20251026_02_enson/02_alter_table.sql new file mode 100644 index 0000000..70fb578 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251026_02_enson/02_alter_table.sql @@ -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); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20251026_02_enson/03_alter_table.sql b/src/main/resources/db/changelog/changes/20251026_02_enson/03_alter_table.sql new file mode 100644 index 0000000..404c612 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251026_02_enson/03_alter_table.sql @@ -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); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20251028_01_enson/01_producutprocess.sql b/src/main/resources/db/changelog/changes/20251028_01_enson/01_producutprocess.sql new file mode 100644 index 0000000..1beee81 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251028_01_enson/01_producutprocess.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset Enson:alter qc category table + +ALTER TABLE `productprocessline` +ADD COLUMN `handlerId` INT NULL AFTER `equipmentId`; diff --git a/src/main/resources/db/changelog/changes/20251028_01_enson/02_producutprocess.sql b/src/main/resources/db/changelog/changes/20251028_01_enson/02_producutprocess.sql new file mode 100644 index 0000000..15c510b --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251028_01_enson/02_producutprocess.sql @@ -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`; + + diff --git a/src/main/resources/db/changelog/changes/20251098_01_enson/03_altertable_enson.sql b/src/main/resources/db/changelog/changes/20251098_01_enson/03_altertable_enson.sql new file mode 100644 index 0000000..947861c --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251098_01_enson/03_altertable_enson.sql @@ -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`); diff --git a/src/main/resources/db/changelog/changes/20251103_01_enson/01_altertable_enson.sql b/src/main/resources/db/changelog/changes/20251103_01_enson/01_altertable_enson.sql new file mode 100644 index 0000000..7fab889 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251103_01_enson/01_altertable_enson.sql @@ -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`;