From c2c8b40fb3e5e64827851e5b41542e29c63bdb9e Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Sun, 18 Jan 2026 13:12:44 +0800 Subject: [PATCH] update qcitem --- .../ItemsQcCategoryMappingRepository.kt | 1 + .../modules/master/service/ItemUomService.kt | 23 +- .../master/web/models/DeleteResponse.kt | 1 + .../web/models/ItemQcCategoryMappingInfo.kt | 1 + .../web/models/QcCategoryWithItemCount.kt | 1 + .../web/models/QcCategoryWithQcItemCount.kt | 1 + .../entity/ProductProcessLine.kt | 3 +- .../entity/projections/ProductProcessInfo.kt | 11 +- .../service/ProductProcessService.kt | 319 +++++++++++++++++- .../web/ProductProcessController.kt | 4 + .../web/model/SaveProductProcessRequest.kt | 1 + 11 files changed, 345 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsQcCategoryMappingRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsQcCategoryMappingRepository.kt index 9685120..5b8fc02 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsQcCategoryMappingRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsQcCategoryMappingRepository.kt @@ -24,3 +24,4 @@ interface ItemsQcCategoryMappingRepository : AbstractRepository } + diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt index 3cb9979..7ef120b 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt @@ -138,7 +138,24 @@ open class ItemUomService( ) } } - + if (request.uomId == 784L) { + val targetItemUom = findTargetItemUom(request.itemId, request.targetUnit) + val targetUomId = targetItemUom?.uom?.id + + if (targetUomId == 18L) { + // Direct conversion: 1 KG = 1000 milliliters (assuming density = 1 g/ml for water-based products) + // Note: This is a general conversion, actual conversion may vary by product density + val convertedQty = request.qty.multiply(BigDecimal(1000)) + val milliliterUom = uomConversionService.findById(19L) + ?: throw IllegalArgumentException("UomConversion not found for id=19 (milliliter)") + + return ConvertUomByItemResponse( + newQty = convertedQty, + udfudesc = milliliterUom.udfudesc, + udfShortDesc = milliliterUom.udfShortDesc + ) + } + } // Find source ItemUom by itemId and uomId val sourceItemUom = itemUomRespository.findByItemIdAndUomIdAndDeletedIsFalse(request.itemId, request.uomId) ?: throw IllegalArgumentException("Source ItemUom not found for itemId=${request.itemId}, uomId=${request.uomId}") @@ -155,10 +172,10 @@ open class ItemUomService( val targetRatioD = targetItemUom.ratioD ?: one // Convert source qty to base: qty * ratioN / ratioD - val baseQty = request.qty.multiply(sourceRatioN).divide(sourceRatioD, 2, RoundingMode.HALF_UP) + val baseQty = request.qty.multiply(sourceRatioN).divide(sourceRatioD, 2, RoundingMode.UP) // Convert base to target: baseQty * ratioD / ratioN - val newQty = baseQty.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.HALF_UP) + val newQty = baseQty.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.UP) // Get UomConversion from target ItemUom val uomConversion = targetItemUom.uom diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/DeleteResponse.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/DeleteResponse.kt index 22487b3..70b5592 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/DeleteResponse.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/DeleteResponse.kt @@ -6,3 +6,4 @@ data class DeleteResponse( val canDelete: Boolean = true ) + diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/ItemQcCategoryMappingInfo.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/ItemQcCategoryMappingInfo.kt index aef1b2a..5aa16bd 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/ItemQcCategoryMappingInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/ItemQcCategoryMappingInfo.kt @@ -11,3 +11,4 @@ data class ItemQcCategoryMappingInfo( val type: String? ) + diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/QcCategoryWithItemCount.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/QcCategoryWithItemCount.kt index 03c3606..011abe4 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/QcCategoryWithItemCount.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/QcCategoryWithItemCount.kt @@ -8,3 +8,4 @@ data class QcCategoryWithItemCount( val itemCount: Long ) + diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/QcCategoryWithQcItemCount.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/QcCategoryWithQcItemCount.kt index d3bef46..8ac2b77 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/QcCategoryWithQcItemCount.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/QcCategoryWithQcItemCount.kt @@ -8,3 +8,4 @@ data class QcCategoryWithQcItemCount( val qcItemCount: Long ) + 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 index c2bd4ea..69d814d 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt @@ -105,7 +105,8 @@ open class ProductProcessLine : BaseEntity() { @Column(name = "startTime") open var startTime: LocalDateTime? = null - + @Column(name = "is_original") + open var isOriginal: Boolean? = null @Column(name = "endTime") open var endTime: LocalDateTime? = null @ManyToOne(fetch = FetchType.LAZY) 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 index aa6f331..6e95fcc 100644 --- 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 @@ -10,9 +10,9 @@ data class ProductProcessInfo( val id: Long?, val productProcessCode: String?, val status: ProductProcessStatus?, - @JsonFormat(pattern = "MM-dd HH:mm") + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") val startTime: LocalDateTime?, - @JsonFormat(pattern = "MM-dd HH:mm") + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") val endTime: LocalDateTime?, @JsonFormat(pattern = "yyyy-MM-dd") val date: LocalDate?, @@ -80,10 +80,11 @@ data class ProductProcessLineInfo( val durationInMinutes: Int?, val prepTimeInMinutes: Int?, val postProdTimeInMinutes: Int?, - @JsonFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") val startTime: LocalDateTime?, - @JsonFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") val endTime: LocalDateTime?, + val isOringinal: Boolean?, ) @@ -97,6 +98,8 @@ data class jobOrderLineInfo( val reqQty: Int?, val baseReqQty: Int?, + val stockReqQty: Int?, + val stockQty: Int?, val baseStockQty: Int?, 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 index ab76876..19eb134 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt @@ -140,6 +140,7 @@ open class ProductProcessService( this.name = bomProcess.process?.name this.description = bomProcess.description this.equipmentType = bomProcess.equipment?.name + this.isOriginal = true } productProcessLineRepository.save(line) println("➕ Service: Created line ${index + 1} - seq: ${line.seqNo}, name: ${line.name}") @@ -239,6 +240,7 @@ open class ProductProcessService( this.name = request.name this.description = request.description this.equipmentType = request.equipmentType + this.isOriginal = false } val saved = productProcessLineRepository.save(line) @@ -587,14 +589,14 @@ val sufficientStockQty = bomMaterials else -> "" } } - + return productProcesses.map { process -> val jobType = jobTypeRepository.findById(process.jobOrder?.jobTypeId?:0L).orElse(null) //val joPickOrders = joPickOrderRepository.findByJobOrderId(process.jobOrder?.id?:0L) println("jobType id ${process.jobOrder?.jobTypeId}") - + val newCreatedLineIds = findNewCreatedLineIds(process.id ?: 0L, bom?.id ?: 0L) ProductProcessInfo( id = process.id?:0, bomId = process.bom?.id?:0, @@ -667,7 +669,8 @@ val sufficientStockQty = bomMaterials outputFromProcessQty = line.outputFromProcessQty?:0, outputFromProcessUom = line.outputFromProcessUom?:"", startTime = line.startTime, - endTime = line.endTime + endTime = line.endTime, + isOringinal = line.isOriginal?:false ) }.sortedBy { it.seqNo }, @@ -762,8 +765,8 @@ val sufficientStockQty = bomMaterials val targetRatioN = targetItemUom.ratioN ?: BigDecimal.ONE val targetRatioD = targetItemUom.ratioD ?: BigDecimal.ONE - val baseQty = stockQtyValue.multiply(sourceRatioN).divide(sourceRatioD, 2, RoundingMode.HALF_UP) - val finalQty = baseQty.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.HALF_UP) + val baseQty = stockQtyValue.multiply(sourceRatioN).divide(sourceRatioD, 2, RoundingMode.UP) + val finalQty = baseQty.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.UP) println(" Calculation: baseQty = $stockQtyValue * $sourceRatioN / $sourceRatioD = $baseQty") println(" Calculation: finalQty = $baseQty * $targetRatioD / $targetRatioN = $finalQty") @@ -793,10 +796,83 @@ val sufficientStockQty = bomMaterials // Use the same baseUomName for stockBaseUom since it's the same base unit val baseStockUomName = baseUomName val baseStockShortUom = baseShortUom - + println("Final values - reqQty: ${line.reqQty?.toInt()}, baseReqQty: $baseReqQty, stockQty: $stockQty, baseStockQty: $baseStockQty") - + val reqStockQtyResult = if (reqUomId > 0 && stockUomId != null && stockUomId > 0 && reqUomId != stockUomId) { + try { + // Convert reqQty from reqUomId to stockUomId via baseUnit + // First convert to baseUnit + val baseReqQtyForStock = if (baseReqQtyResult != null) { + baseReqQtyResult.newQty + } else { + // If baseReqQty conversion failed, try again + try { + itemUomService.convertUomByItem( + ConvertUomByItemRequest( + itemId = itemId, + qty = line.reqQty ?: BigDecimal.ZERO, + uomId = reqUomId, + targetUnit = "baseUnit" + ) + ).newQty + } catch (e: Exception) { + println("Error converting reqQty to baseUnit for stock conversion: ${e.message}") + null + } + } + + if (baseReqQtyForStock != null) { + // Now convert from baseUnit to stockUomId + val sourceItemUom = itemUomService.findBaseUnitByItemId(itemId) + val targetItemUom = itemUomRepository.findByItemIdAndUomIdAndDeletedIsFalse(itemId, stockUomId) + + if (sourceItemUom != null && targetItemUom != null) { + val sourceRatioN = sourceItemUom.ratioN ?: BigDecimal.ONE + val sourceRatioD = sourceItemUom.ratioD ?: BigDecimal.ONE + val targetRatioN = targetItemUom.ratioN ?: BigDecimal.ONE + val targetRatioD = targetItemUom.ratioD ?: BigDecimal.ONE + + // Convert base to stock: baseQty * targetRatioD / targetRatioN + val stockQtyDecimal = baseReqQtyForStock.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.UP) + val stockQty = stockQtyDecimal.setScale(0, RoundingMode.UP) // Round up to integer + val stockUom = uomConversionRepository.findByIdAndDeletedFalse(stockUomId) + + println("Converting reqQty to stock unit: ${line.reqQty} (UOM id=$reqUomId) -> $stockQty (UOM id=$stockUomId)") + + ConvertUomByItemResponse( + newQty = stockQty, + udfudesc = stockUom?.udfudesc, + udfShortDesc = stockUom?.udfShortDesc + ) + } else { + println("WARNING: Cannot convert reqQty to stock unit - missing ItemUom (sourceItemUom=${sourceItemUom != null}, targetItemUom=${targetItemUom != null})") + null + } + } else { + null + } + } catch (e: Exception) { + println("Error converting reqQty to stock unit: ${e.message}") + e.printStackTrace() + null + } + } else if (reqUomId > 0 && stockUomId != null && stockUomId > 0 && reqUomId == stockUomId) { + // If reqUomId and stockUomId are the same, no conversion needed + val stockUom = uomConversionRepository.findByIdAndDeletedFalse(stockUomId) + ConvertUomByItemResponse( + newQty = line.reqQty ?: BigDecimal.ZERO, + udfudesc = stockUom?.udfudesc, + udfShortDesc = stockUom?.udfShortDesc + ) + } else { + println("Cannot convert reqQty to stock unit - reqUomId=$reqUomId, stockUomId=$stockUomId") + null + } + + val reqStockQty = reqStockQtyResult?.newQty?.toInt() ?: 0 + + println("Final values - reqQty: ${line.reqQty?.toInt()}, baseReqQty: $baseReqQty, stockQty: $stockQty, baseStockQty: $baseStockQty, reqStockQty: $reqStockQty") // Find BomProcessMaterial val bomProcessMaterial = bomMaterial?.id?.let { bomMaterialId -> bomProcessIds.firstNotNullOfOrNull { bomProcessId -> @@ -825,6 +901,7 @@ val sufficientStockQty = bomMaterials baseReqQty = baseReqQty, // Add this field if not exists stockQty = stockQty, + stockReqQty = reqStockQty, baseStockQty = baseStockQty, // Add this field if not exists reqUom = uomName ?: "", reqBaseUom = baseUomName ?: "", @@ -879,6 +956,7 @@ val sufficientStockQty = bomMaterials this.processingTime = bomProcess.durationInMinute this.setupTime = bomProcess.prepTimeInMinute this.changeoverTime = bomProcess.postProdTimeInMinute + this.isOriginal=true } productProcessLineRepository.save(productProcessLine) } @@ -1732,9 +1810,12 @@ val sufficientStockQty = bomMaterials this.defectDescription3 = sourceLine.defectDescription3 this.outputFromProcessQty = sourceLine.outputFromProcessQty this.outputFromProcessUom = sourceLine.outputFromProcessUom - // 不复制时间字段,新 line 应该没有开始和结束时间 + this.processingTime = sourceLine.processingTime + this.setupTime = sourceLine.setupTime + this.changeoverTime = sourceLine.changeoverTime this.startTime = null this.endTime = null + this.isOriginal = false } // 保存新 line(原 line 的 seqNo 保持不变,不需要更新) @@ -1754,13 +1835,18 @@ open fun getJobProcessStatus(): List { val productProcesses = productProcessRepository.findAllByDeletedIsFalse() .filter { it.status != ProductProcessStatus.COMPLETED } - return productProcesses.map { process -> + return productProcesses.mapNotNull { process -> val jobOrder = jobOrderRepository.findById(process.jobOrder?.id ?: 0L).orElse(null) + + // Filter out jobOrders in PLANNING status + if (jobOrder?.status == JobOrderStatus.PLANNING) { + return@mapNotNull null + } + val lines = productProcessLineRepository.findByProductProcess_Id(process.id ?: 0L) .sortedBy { it.seqNo } val bom=bomRepository.findById(process.bom?.id ?: 0L).orElse(null) - //val equipmentDetail = equipmentDetailRepository.findById(equipmentDetailId).orElse(null) // Calculate planEndTime based on first start time + remaining processing time val firstStartTime = lines.firstOrNull { it.startTime != null }?.startTime val calculatedPlanEndTime = if (firstStartTime != null) { @@ -1786,15 +1872,34 @@ open fun getJobProcessStatus(): List { jobOrderCode = jobOrder?.code ?: "", itemCode = process.item?.code ?: "", itemName = process.item?.name ?: "", + status = process.status?.value ?: "", planEndTime = calculatedPlanEndTime, processes = (0 until 6).map { index -> if (index < lines.size) { val line = lines[index] - val bomProcesses = bomProcessRepository.findByBomId(bom?.id ?: 0L).sortedBy { it.seqNo } val equipmentDetailId = line.equipmentDetailId + + // Use line's own data instead of indexing into bomProcesses + val equipmentCode = when { + equipmentDetailId != null -> { + equipmentDetailRepository.findById(equipmentDetailId).orElse(null)?.code ?: "" + } + line.equipment?.code != null -> { + line.equipment?.code ?: "" + } + else -> { + // Safely access bomProcess - it might be deleted + try { + line.bomProcess?.equipment?.code ?: "" + } catch (e: jakarta.persistence.EntityNotFoundException) { + // BomProcess was deleted, fallback to equipmentType + "" + }.takeIf { it.isNotEmpty() } ?: (line.equipmentType ?: "") + } + } + ProcessStatusInfo( - - equipmentCode = if(equipmentDetailId != null) equipmentDetailRepository.findById(equipmentDetailId).orElse(null)?.code ?: "" else bomProcesses[index].equipment?.code ?: "", + equipmentCode = equipmentCode, startTime = line.startTime, endTime = line.endTime, processingTime = line.processingTime, @@ -1817,6 +1922,194 @@ open fun getJobProcessStatus(): List { ) } } + private fun findNewCreatedLineIds(productProcessId: Long, bomId: Long): Set { + // 获取 BOM 的所有 bomProcess,创建一个映射:bomProcessId -> seqNo + val bomProcessMap = bomProcessRepository.findByBomId(bomId) + .associate { it.id to (it.seqNo ?: 0L) } + + if (bomProcessMap.isEmpty()) { + return emptySet() + } + + // 获取所有 line,按 seqNo 排序 + val allLines = productProcessLineRepository.findByProductProcess_Id(productProcessId) + .sortedBy { it.seqNo ?: 0L } + + println("=== findNewCreatedLineIds DEBUG START ===") + println("BOM bomProcessMap: $bomProcessMap") + println("All lines (sorted by seqNo):") + allLines.forEach { line -> + println(" id=${line.id}, seqNo=${line.seqNo}, bomProcessId=${line.bomProcess?.id}") + } + + // 创建一个集合来跟踪哪些 line 是新创建的 + val newCreatedLineIds = mutableSetOf() + + // 迭代检查,直到所有剩余的 line 都匹配 + var iteration = 0 + var hasChanges = true + while (hasChanges) { + iteration++ + hasChanges = false + + println("\n--- Iteration $iteration ---") + + // 获取剩余的 line(排除已标记为新创建的),按 seqNo 排序 + val remainingLines = allLines.filter { it.id !in newCreatedLineIds } + .sortedBy { it.seqNo ?: 0L } + + println("Remaining lines (excluding new created):") + remainingLines.forEach { line -> + println(" id=${line.id}, seqNo=${line.seqNo}, bomProcessId=${line.bomProcess?.id}") + } + + println("New created line IDs so far: $newCreatedLineIds") + + // 计算每个剩余 line 的期望 seqNo(应该是连续的 1, 2, 3...) + val expectedSeqNoMap = remainingLines.mapIndexed { index, line -> + line.id to (index + 1).toLong() + }.toMap() + + println("Expected seqNo map:") + expectedSeqNoMap.forEach { (lineId, expectedSeqNo) -> + println(" lineId=$lineId -> expectedSeqNo=$expectedSeqNo") + } + + // 检查每个剩余 line + for (line in remainingLines) { + val bomProcessId = line.bomProcess?.id + val expectedSeqNo = expectedSeqNoMap[line.id] ?: continue + + println("\nChecking line id=${line.id}, seqNo=${line.seqNo}, bomProcessId=$bomProcessId, expectedSeqNo=$expectedSeqNo") + + if (bomProcessId == null) { + println(" -> No bomProcessId, marking as new created") + newCreatedLineIds.add(line.id ?: 0L) + hasChanges = true + continue + } + + // 查找这个 bomProcessId 在 BOM 中的实际 seqNo + val bomProcessSeqNo = bomProcessMap[bomProcessId] + + println(" -> BOM bomProcessId=$bomProcessId has seqNo=$bomProcessSeqNo in BOM") + + if (bomProcessSeqNo == null) { + println(" -> bomProcessId not found in BOM, marking as new created") + newCreatedLineIds.add(line.id ?: 0L) + hasChanges = true + continue + } + + // 检查 line 的期望 seqNo 是否匹配 BOM 中 bomProcess 的 seqNo + if (expectedSeqNo != bomProcessSeqNo) { + println(" -> MISMATCH: expectedSeqNo=$expectedSeqNo != bomProcessSeqNo=$bomProcessSeqNo, marking as new created") + newCreatedLineIds.add(line.id ?: 0L) + hasChanges = true + } else { + println(" -> MATCH: expectedSeqNo=$expectedSeqNo == bomProcessSeqNo=$bomProcessSeqNo, keeping as original") + } + } + } + + println("\n=== Final Result ===") + println("New created line IDs: $newCreatedLineIds") + println("=== findNewCreatedLineIds DEBUG END ===\n") + + return newCreatedLineIds + } + + // 辅助函数:判断是否是原始创建的 line(使用缓存的结果) + private fun isOriginalLine(productProcessLine: ProductProcessLine, newCreatedLineIds: Set): Boolean { + val currentLineId = productProcessLine.id ?: 0L + return currentLineId !in newCreatedLineIds + } +open fun deleteProductProcessLine(productProcessLineId: Long): MessageResponse { + val productProcessLine = productProcessLineRepository.findById(productProcessLineId).orElse(null) + ?: return MessageResponse( + id = productProcessLineId, + code = "404", + name = "ProductProcess Line Not Found", + type = "error", + message = "ProductProcess Line with ID $productProcessLineId not found", + errorPosition = null, + ) + + val productProcessId = productProcessLine.productProcess?.id ?: return MessageResponse( + id = productProcessLineId, + code = "400", + name = "Invalid ProductProcess", + type = "error", + message = "ProductProcess Line has no associated ProductProcess", + errorPosition = null, + ) + + // 检查 JobOrder 状态 + val productProcess = productProcessRepository.findById(productProcessId).orElse(null) + val jobOrder = productProcess?.jobOrder + if (jobOrder?.status != JobOrderStatus.PLANNING) { + return MessageResponse( + id = productProcessLineId, + code = "400", + name = "JobOrder Not In Planning", + type = "error", + message = "Cannot delete line when JobOrder is not in planning status", + errorPosition = null, + ) + } + + // 检查是否是原始 line(使用 ID 判断) + val bomId = productProcess?.bom?.id ?: return MessageResponse( + id = productProcessLineId, + code = "400", + name = "Invalid BOM", + type = "error", + message = "ProductProcess has no associated BOM", + errorPosition = null, + ) + + // 获取新创建的 line IDs + val newCreatedLineIds = findNewCreatedLineIds(productProcessId, bomId) + + // 检查是否是原始 line + if (productProcessLine.isOriginal == true) { + return MessageResponse( + id = productProcessLineId, + code = "400", + name = "Cannot Delete Original Line", + type = "error", + message = "Cannot delete original process line. Only newly created lines can be deleted.", + errorPosition = null, + ) + } + + val deletedSeqNo = productProcessLine.seqNo ?: 0L + + // 删除 line + productProcessLineRepository.delete(productProcessLine) + + // 获取所有剩余的 lines,按 seqNo 排序,然后调整 seqNo + val remainingLines = productProcessLineRepository.findByProductProcess_Id(productProcessId) + .sortedBy { it.seqNo ?: 0L } + + // 更新所有 seqNo > deletedSeqNo 的 lines,将它们的 seqNo 减 1 + remainingLines.filter { + it.seqNo != null && + it.seqNo!! > deletedSeqNo + }.forEach { line -> + line.seqNo = (line.seqNo ?: 0) - 1 + productProcessLineRepository.save(line) + } + + return MessageResponse( + id = productProcessLineId, + code = "200", + name = "ProductProcess Line Deleted", + type = "success", + message = "ProductProcess Line deleted successfully", + errorPosition = null, + ) +} } 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 index 6ecf095..0f6d22b 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt @@ -229,4 +229,8 @@ class ProductProcessController( fun getJobProcessStatus(): List { return productProcessService.getJobProcessStatus() } + @PostMapping("/Demo/ProcessLine/delete/{lineId}") + fun deleteProductProcessLine(@PathVariable lineId: Long): MessageResponse { + return productProcessService.deleteProductProcessLine(lineId) + } } \ 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 index f3de9f6..b251851 100644 --- 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 @@ -235,5 +235,6 @@ data class JobProcessStatusResponse( val itemCode: String, val itemName: String, val planEndTime: LocalDateTime?, + val status: String, val processes: List ) \ No newline at end of file