From 215008f1a9f8c63c9928b8e0a0cdad70196d827d Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Fri, 6 Feb 2026 11:02:09 +0800 Subject: [PATCH] updateupd --- .../service/ProductProcessService.kt | 1791 ++++++++++------- 1 file changed, 1008 insertions(+), 783 deletions(-) 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 6841cfa..8e288a1 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 @@ -80,30 +80,30 @@ open class ProductProcessService( private val uomConversionRepository: UomConversionRepository, private val itemUomService: ItemUomService ) { - + 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}") } @@ -112,10 +112,10 @@ open class ProductProcessService( // 2. 生成 productProcessCode(格式:PP-20251026-001) //val datePrefix = (request.date ?: LocalDate.now()).format(DateTimeFormatter.ofPattern("yyyyMMdd")) - val datePrefix = ( 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 @@ -124,16 +124,16 @@ open class ProductProcessService( this.jobOrder = request.jobOrderId?.let { jobOrderRepository.findById(it).orElse(null) } this.status = ProductProcessStatus.PENDING //this.date = request.date ?: LocalDate.now() - this.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 { @@ -148,23 +148,23 @@ open class ProductProcessService( 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 { @@ -173,7 +173,7 @@ open class ProductProcessService( 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) @@ -183,45 +183,45 @@ open class ProductProcessService( 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) @@ -231,13 +231,13 @@ open class ProductProcessService( 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 @@ -245,18 +245,18 @@ open class ProductProcessService( this.equipmentType = request.equipmentType this.isOriginal = false } - + 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 @@ -269,29 +269,30 @@ open class ProductProcessService( 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( @@ -320,14 +321,15 @@ open class ProductProcessService( } ) } - + 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!!, @@ -340,16 +342,16 @@ open class ProductProcessService( 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, @@ -361,6 +363,7 @@ open class ProductProcessService( jobOrderId = entity.jobOrder?.id ) } + open fun stopProcessAsDto(id: Long, request: StopProcessRequest): ProductionProcessIssueResponse { val issue = stopProcess(id, request) return ProductionProcessIssueResponse( @@ -374,7 +377,7 @@ open class ProductProcessService( totalTime = issue.totalTime ) } - + open fun resumeProcessAsDto(id: Long, issueId: Long): ProductionProcessIssueResponse { val issue = resumeProcess(id, issueId) return ProductionProcessIssueResponse( @@ -388,7 +391,7 @@ open class ProductProcessService( totalTime = issue.totalTime ) } - + open fun getIssuesAsDto(productProcessId: Long): List { val issues = getIssues(productProcessId) return issues.map { issue -> @@ -404,6 +407,7 @@ open class ProductProcessService( ) } } + open fun startProcessAsDto(id: Long, operatorId: Long): ProductProcessSimpleResponse { val entity = startProcess(id, operatorId) return ProductProcessSimpleResponse( @@ -417,7 +421,7 @@ open class ProductProcessService( jobOrderId = entity.jobOrder?.id ) } - + open fun completeProcessAsDto(id: Long): ProductProcessSimpleResponse { val entity = completeProcess(id) return ProductProcessSimpleResponse( @@ -431,7 +435,7 @@ open class ProductProcessService( jobOrderId = entity.jobOrder?.id ) } - + open fun updateLineOutputAsDto(lineId: Long, request: UpdateLineOutputRequest): ProductProcessLineResponse { val entity = updateLineOutput(lineId, request) return ProductProcessLineResponse( @@ -450,7 +454,7 @@ open class ProductProcessService( byproductQty = entity.byproductQty ) } - + open fun getLinesAsDto(productProcessId: Long): List { val lines = getLines(productProcessId) return lines.map { line -> @@ -471,22 +475,24 @@ open class ProductProcessService( ) } } + 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) @@ -497,75 +503,75 @@ open class ProductProcessService( open fun productProcessDetailfindbyjoid(joid: Long): List { val productProcesses = productProcessRepository.findByJobOrder_Id(joid) val jobOrder = jobOrderRepository.findById(joid).orElse(null) - val bom = bomRepository.findById(jobOrder?.bom?.id?:0L).orElse(null) - val bomProcess = bomProcessRepository.findByBomId(jobOrder?.bom?.id?:0L) + val bom = bomRepository.findById(jobOrder?.bom?.id ?: 0L).orElse(null) + val bomProcess = bomProcessRepository.findByBomId(jobOrder?.bom?.id ?: 0L) // get bom materials - val bomMaterials = jobOrderBomMaterialRepository.findAllByJobOrderId(jobOrder?.id?:0) + val bomMaterials = jobOrderBomMaterialRepository.findAllByJobOrderId(jobOrder?.id ?: 0) val bomProcessIds = bomProcess.mapNotNull { it.id } val itemIds = bomMaterials.mapNotNull { it.item?.id } - + // calculate each item's available stock // Around line 504-524, update the stockQtyMap calculation -val stockQtyMap = bomMaterials.mapNotNull { material -> - val itemId = material.item?.id ?: return@mapNotNull null - val availableLots = inventoryLotLineRepository - .findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE) - val stockQty = availableLots.sumOf { lot -> - (lot.inQty ?: BigDecimal.ZERO) - .minus(lot.outQty ?: BigDecimal.ZERO) - .minus(lot.holdQty ?: BigDecimal.ZERO) - } - // Get the stockUom from the first available lot line - // stockItemUomId is the ItemUom id, we need to get the UomConversion id from it - val stockItemUom = availableLots.firstOrNull()?.stockUom - var stockUomId = stockItemUom?.uom?.id - - // Fallback: If no lots exist, get stockUom from item's stockUnit ItemUom - if (stockUomId == null && availableLots.isEmpty()) { - val stockUnitItemUom = itemUomService.findStockUnitByItemId(itemId) - stockUomId = stockUnitItemUom?.uom?.id - if (stockUomId != null) { - println("DEBUG itemId=$itemId: Using fallback stockUnit ItemUom - stockUom.uom.id=$stockUomId") - } else { - println("WARNING: No stockUom found for itemId=$itemId in available lots and no stockUnit ItemUom configured") - } - } else { - if (stockItemUom == null) { - println("WARNING: No stockUom found for itemId=$itemId in available lots") - } - if (stockUomId == null && stockItemUom != null) { - println("WARNING: stockItemUom.id=${stockItemUom.id} exists but has no uom (UomConversion)") - } - } - println("DEBUG itemId=$itemId: stockItemUom.id=${stockItemUom?.id}, stockUom.uom.id=$stockUomId, availableLots count=${availableLots.size}") - itemId to Pair(stockQty, stockUomId) -}.toMap() - + val stockQtyMap = bomMaterials.mapNotNull { material -> + val itemId = material.item?.id ?: return@mapNotNull null + val availableLots = inventoryLotLineRepository + .findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE) + val stockQty = availableLots.sumOf { lot -> + (lot.inQty ?: BigDecimal.ZERO) + .minus(lot.outQty ?: BigDecimal.ZERO) + .minus(lot.holdQty ?: BigDecimal.ZERO) + } + // Get the stockUom from the first available lot line + // stockItemUomId is the ItemUom id, we need to get the UomConversion id from it + val stockItemUom = availableLots.firstOrNull()?.stockUom + var stockUomId = stockItemUom?.uom?.id + + // Fallback: If no lots exist, get stockUom from item's stockUnit ItemUom + if (stockUomId == null && availableLots.isEmpty()) { + val stockUnitItemUom = itemUomService.findStockUnitByItemId(itemId) + stockUomId = stockUnitItemUom?.uom?.id + if (stockUomId != null) { + println("DEBUG itemId=$itemId: Using fallback stockUnit ItemUom - stockUom.uom.id=$stockUomId") + } else { + println("WARNING: No stockUom found for itemId=$itemId in available lots and no stockUnit ItemUom configured") + } + } else { + if (stockItemUom == null) { + println("WARNING: No stockUom found for itemId=$itemId in available lots") + } + if (stockUomId == null && stockItemUom != null) { + println("WARNING: stockItemUom.id=${stockItemUom.id} exists but has no uom (UomConversion)") + } + } + println("DEBUG itemId=$itemId: stockItemUom.id=${stockItemUom?.id}, stockUom.uom.id=$stockUomId, availableLots count=${availableLots.size}") + itemId to Pair(stockQty, stockUomId) + }.toMap() + // calculate statistics val totalStockQty = stockQtyMap.values.sumOf { (qty, _) -> qty.toInt() } -val insufficientStockQty = bomMaterials - .filter { material -> - val itemId = material.item?.id ?: 0L - val (stockQty, _) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) - stockQty < (material.reqQty ?: BigDecimal.ZERO) - } - .sumOf { material -> - val itemId = material.item?.id ?: 0L - val (stockQty, _) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) - stockQty.toInt() - } -val sufficientStockQty = bomMaterials - .filter { material -> - val itemId = material.item?.id ?: 0L - val (stockQty, _) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) - stockQty >= (material.reqQty ?: BigDecimal.ZERO) - } - .sumOf { material -> - val itemId = material.item?.id ?: 0L - val (stockQty, _) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) - stockQty.toInt() - } + val insufficientStockQty = bomMaterials + .filter { material -> + val itemId = material.item?.id ?: 0L + val (stockQty, _) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) + stockQty < (material.reqQty ?: BigDecimal.ZERO) + } + .sumOf { material -> + val itemId = material.item?.id ?: 0L + val (stockQty, _) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) + stockQty.toInt() + } + val sufficientStockQty = bomMaterials + .filter { material -> + val itemId = material.item?.id ?: 0L + val (stockQty, _) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) + stockQty >= (material.reqQty ?: BigDecimal.ZERO) + } + .sumOf { material -> + val itemId = material.item?.id ?: 0L + val (stockQty, _) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) + stockQty.toInt() + } // 获取 productionPriority val itemId = jobOrder?.bom?.item?.id val planEndDate = jobOrder?.planEnd?.toLocalDate() @@ -578,6 +584,7 @@ val sufficientStockQty = bomMaterials else -> "" } } + fun calculateAllergicSubstanceScore(value: Int?): String { return when (value) { 0 -> "No" @@ -585,6 +592,7 @@ val sufficientStockQty = bomMaterials else -> "N/A" } } + fun calculateFloatScore(value: Int?): String { return when (value) { 0 -> "沉" @@ -592,149 +600,155 @@ val sufficientStockQty = bomMaterials else -> "" } } - + return productProcesses.map { process -> - val jobType = jobTypeRepository.findById(process.jobOrder?.jobTypeId?:0L).orElse(null) + 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, - jobOrderId = process.jobOrder?.id?:0, - jobOrderCode = jobOrder?.code?:"", - jobOrderStatus = jobOrder?.status?.value?:"", - jobType = jobType?.name?:"", - bomDescription = bom?.description?:"", - itemId = bom?.item?.id?:0, - itemCode = bom?.item?.code?:"", - itemName = bom?.item?.name?:"", - timeSequence = bom?.timeSequence?:0, - complexity = bom?.complexity?:0, + id = process.id ?: 0, + bomId = process.bom?.id ?: 0, + jobOrderId = process.jobOrder?.id ?: 0, + jobOrderCode = jobOrder?.code ?: "", + jobOrderStatus = jobOrder?.status?.value ?: "", + jobType = jobType?.name ?: "", + bomDescription = bom?.description ?: "", + itemId = bom?.item?.id ?: 0, + itemCode = bom?.item?.code ?: "", + itemName = bom?.item?.name ?: "", + timeSequence = bom?.timeSequence ?: 0, + complexity = bom?.complexity ?: 0, bomBaseQty = bom.outputQty ?: BigDecimal.ZERO, - isDark = calculateColourScore(bom?.isDark?:0), - isDense = bom?.isDense?:0, - isFloat = calculateFloatScore(bom?.isFloat?:0), - scrapRate = bom?.scrapRate?:-1, + isDark = calculateColourScore(bom?.isDark ?: 0), + isDense = bom?.isDense ?: 0, + isFloat = calculateFloatScore(bom?.isFloat ?: 0), + scrapRate = bom?.scrapRate ?: -1, allergicSubstance = calculateAllergicSubstanceScore(bom?.allergicSubstances), - outputQtyUom = bom?.outputQtyUom?:"", + outputQtyUom = bom?.outputQtyUom ?: "", //matchStatus = joPickOrders?.matchStatus?.value?:"", //outputQty = bom?.outputQty?.toInt()?:0, - outputQty = jobOrder?.reqQty?.toInt()?:0, - productProcessCode = process.productProcessCode?:"", - status = process.status?:ProductProcessStatus.PENDING, - startTime = process.startTime?:LocalDateTime.now(), - endTime = process.endTime?:LocalDateTime.now(), - date = process.date?:LocalDate.now(), - productionPriority = process.productionPriority?:50, - submitedBagRecord = process.submitedBagRecord?:false, + outputQty = jobOrder?.reqQty?.toInt() ?: 0, + productProcessCode = process.productProcessCode ?: "", + status = process.status ?: ProductProcessStatus.PENDING, + startTime = process.startTime ?: LocalDateTime.now(), + endTime = process.endTime ?: LocalDateTime.now(), + date = process.date ?: LocalDate.now(), + productionPriority = process.productionPriority ?: 50, + submitedBagRecord = process.submitedBagRecord ?: false, totalStockQty = totalStockQty, insufficientStockQty = insufficientStockQty, sufficientStockQty = sufficientStockQty, - productProcessLines = productProcessLineRepository.findByProductProcess_Id(process.id?:0).map { line -> - val equipmentDetail =equipmentDetailRepository.findById(line.equipmentDetailId?:0L).orElse(null) - println("equipmentDetail ${equipmentDetail?.code}") - println("equipmentDetail id${line.equipmentDetailId}") - println("line id${line.id}") - ProductProcessLineInfo( - id = line.id?:0, - processId = process.id?:0, - bomprocessId = line.bomProcess?.id?:0, - operatorId = line.operator?.id?:0, - operatorName = line.operator?.name?:"", - equipmentId = line.equipment?.id?:0, - equipmentDetailId = line.equipmentDetailId?:0, - handlerId = line.handler?.id?:0, - seqNo = line.seqNo?:0, - name = line.name?:"", - description = line.description?:"", - equipment_name = line.equipmentType?:"", - equipmentDetailCode = equipmentDetail?.code?:"", - status = line.status?:"", - durationInMinutes = line.processingTime?:0, - prepTimeInMinutes = line.setupTime?:0, - postProdTimeInMinutes = line.changeoverTime?:0, - 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?:"", - defectDescription = line.defectDescription?:"", - defectQty2 = line.defectQty2?:0, - defectUom2 = line.defectUom2?:"", - defectDescription2 = line.defectDescription2?:"", - defectQty3 = line.defectQty3?:0, - defectUom3 = line.defectUom3?:"", - defectDescription3 = line.defectDescription3?:"", - outputFromProcessQty = line.outputFromProcessQty?:0, - outputFromProcessUom = line.outputFromProcessUom?:"", - startTime = line.startTime, - endTime = line.endTime, - isOringinal = line.isOriginal?:false - - ) - }.sortedBy { it.seqNo }, + productProcessLines = productProcessLineRepository.findByProductProcess_Id(process.id ?: 0) + .map { line -> + val equipmentDetail = + equipmentDetailRepository.findById(line.equipmentDetailId ?: 0L).orElse(null) + println("equipmentDetail ${equipmentDetail?.code}") + println("equipmentDetail id${line.equipmentDetailId}") + println("line id${line.id}") + ProductProcessLineInfo( + id = line.id ?: 0, + processId = process.id ?: 0, + bomprocessId = line.bomProcess?.id ?: 0, + operatorId = line.operator?.id ?: 0, + operatorName = line.operator?.name ?: "", + equipmentId = line.equipment?.id ?: 0, + equipmentDetailId = line.equipmentDetailId ?: 0, + handlerId = line.handler?.id ?: 0, + seqNo = line.seqNo ?: 0, + name = line.name ?: "", + description = line.description ?: "", + equipment_name = line.equipmentType ?: "", + equipmentDetailCode = equipmentDetail?.code ?: "", + status = line.status ?: "", + durationInMinutes = line.processingTime ?: 0, + prepTimeInMinutes = line.setupTime ?: 0, + postProdTimeInMinutes = line.changeoverTime ?: 0, + 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 ?: "", + defectDescription = line.defectDescription ?: "", + defectQty2 = line.defectQty2 ?: 0, + defectUom2 = line.defectUom2 ?: "", + defectDescription2 = line.defectDescription2 ?: "", + defectQty3 = line.defectQty3 ?: 0, + defectUom3 = line.defectUom3 ?: "", + defectDescription3 = line.defectDescription3 ?: "", + outputFromProcessQty = line.outputFromProcessQty ?: 0, + outputFromProcessUom = line.outputFromProcessUom ?: "", + startTime = line.startTime, + endTime = line.endTime, + isOringinal = line.isOriginal ?: false + + ) + }.sortedBy { it.seqNo }, jobOrderLines = bomMaterials.map { line -> val itemId = line.item?.id ?: 0L val (stockQtyValue, stockUomId) = stockQtyMap[itemId] ?: (BigDecimal.ZERO to null) val stockQty = stockQtyValue.toInt() - + // Find BomMaterial val bomMaterial = bom?.id?.let { bomId -> bomMaterialRepository.findByBomIdAndItemId(bomId, itemId) } - + // ✅ 使用 JobOrderBomMaterial.reqQty(已按比例调整) // line.reqQty = bm.saleQty * proportion(库存单位,已按比例调整) val actualReqQty = line.reqQty ?: BigDecimal.ZERO - + // ✅ 获取 req UOM - 对于 reqQty,使用 bomMaterial.uom(BOM 的 UOM) // ✅ 对于 stockReqQty,使用 line.uom(库存单位,已按比例调整) val reqUomId = bomMaterial?.uom?.id ?: line.uom?.id ?: 0L // BOM 的 UOM val stockReqUomId = line.uom?.id ?: bomMaterial?.stockUnit?.toLong() ?: 0L // 库存单位 UOM - + val reqUom = reqUomId.takeIf { it > 0 }?.let { uomConversionRepository.findByIdAndDeletedFalse(it) } val uomName = reqUom?.udfudesc val shortUom = reqUom?.udfShortDesc - - val stockReqUom = stockReqUomId.takeIf { it > 0 }?.let { uomConversionRepository.findByIdAndDeletedFalse(it) } + + val stockReqUom = + stockReqUomId.takeIf { it > 0 }?.let { uomConversionRepository.findByIdAndDeletedFalse(it) } val stockUomName = stockReqUom?.udfudesc - - val stockUom = stockUomId?.takeIf { it > 0 }?.let { uomConversionRepository.findByIdAndDeletedFalse(it) } + + val stockUom = + stockUomId?.takeIf { it > 0 }?.let { uomConversionRepository.findByIdAndDeletedFalse(it) } val stockUomNameForStock = stockUom?.udfudesc - + println("=== Quantity Calculation for Item: ${line.item?.code} (id=$itemId) ===") println("JobOrderBomMaterial reqQty (adjusted, stock unit): $actualReqQty in UOM: ${stockReqUom?.udfudesc} (id=$stockReqUomId)") println("BomMaterial qty (base): ${bomMaterial?.qty}, stockQty (base): ${bomMaterial?.stockQty}") println("Original stockQty: $stockQtyValue in UOM: $stockUomNameForStock (id=$stockUomId)") - + val jobOrder = jobOrderRepository.findById(joid).orElse(null) val jobOrderReqQty = jobOrder?.reqQty val bomOutputQty = jobOrder?.bom?.outputQty - val proportion = if (jobOrderReqQty != null && bomOutputQty != null && bomOutputQty > BigDecimal.ZERO) { - jobOrderReqQty.divide(bomOutputQty, 5, RoundingMode.HALF_UP) - } else { - BigDecimal.ONE - } + val proportion = + if (jobOrderReqQty != null && bomOutputQty != null && bomOutputQty > BigDecimal.ZERO) { + jobOrderReqQty.divide(bomOutputQty, 5, RoundingMode.UP) + } else { + BigDecimal.ONE + } // ✅ reqQty 使用 bomMaterial.qty * proportion(BOM 单位) val reqQtyInBomUnit = (bomMaterial?.qty?.times(proportion) ?: BigDecimal.ZERO) - + // ✅ stockReqQty 使用 bomMaterial.stockQty * proportion(库存单位,已按比例调整) val stockReqQtyInStockUnit = (bomMaterial?.stockQty?.times(proportion) ?: BigDecimal.ZERO) - + // ✅ Convert reqQty (BOM unit) to base unit val baseReqQtyResult = if (reqUomId > 0 && reqQtyInBomUnit > BigDecimal.ZERO) { try { - val sourceItemUom = itemUomRepository.findFirstByItemIdAndUomIdAndDeletedIsFalse(itemId, reqUomId) + val sourceItemUom = + itemUomRepository.findFirstByItemIdAndUomIdAndDeletedIsFalse(itemId, reqUomId) val targetItemUom = itemUomService.findBaseUnitByItemId(itemId) - + println("Converting reqQty (BOM unit): $reqQtyInBomUnit from UOM id=$reqUomId to baseUnit") - + try { val result = itemUomService.convertUomByItem( ConvertUomByItemRequest( @@ -763,8 +777,8 @@ val sufficientStockQty = bomMaterials println("reqUomId is 0 or reqQtyInBomUnit is 0, skipping baseReqQty conversion") null } - - val baseReqQty = baseReqQtyResult?.newQty?.toInt() ?: 0 + + val baseReqQty = baseReqQtyResult?.newQty?.toLong() ?: 0L val baseUnitItemUom = itemUomService.findBaseUnitByItemId(itemId) val baseUomName = baseUnitItemUom?.uom?.udfudesc ?: baseReqQtyResult?.udfudesc val baseShortUom = baseUnitItemUom?.uom?.udfShortDesc ?: baseReqQtyResult?.udfShortDesc @@ -788,11 +802,11 @@ val sufficientStockQty = bomMaterials } else { null } - - val baseStockQty = baseStockQtyResult?.newQty?.toInt() ?: 0 + + val baseStockQty = baseStockQtyResult?.newQty?.toLong() ?: 0L val baseStockUomName = baseUomName val baseStockShortUom = baseShortUom - + // ✅ 计算 baseStockReqQty(stockReqQty 转换为 base unit) val baseStockReqQtyResult = if (stockReqUomId > 0 && stockReqQtyInStockUnit > BigDecimal.ZERO) { try { @@ -812,58 +826,58 @@ val sufficientStockQty = bomMaterials } else { null } - + val baseStockReqQty = baseStockReqQtyResult?.newQty?.toInt() ?: 0 - + println("Final values - reqQty (BOM unit): $reqQtyInBomUnit, stockReqQty (stock unit): $stockReqQtyInStockUnit, baseReqQty: $baseReqQty, baseStockReqQty: $baseStockReqQty, stockQty: $stockQty, baseStockQty: $baseStockQty") - + // Find BomProcessMaterial val bomProcessMaterial = bomMaterial?.id?.let { bomMaterialId -> bomProcessIds.firstNotNullOfOrNull { bomProcessId -> bomProcessMaterialRepository.findByBomProcessIdAndBomMaterialId(bomProcessId, bomMaterialId) } } - + // Calculate availableStatus using base quantities val availableStatus = if (baseStockQty >= baseStockReqQty) { "available" } else { "insufficient" } - + println("Available status: $availableStatus (baseStockQty=$baseStockQty >= baseStockReqQty=$baseStockReqQty)") println("=== End Quantity Calculation ===\n") - + val decimalUomIds = setOf(784L, 786L) // 需要保留小数的 UOM ID(如千克) - + jobOrderLineInfo( id = line.id ?: 0, itemId = itemId, itemCode = line.item?.code ?: "", itemName = line.item?.name ?: "", - + // ✅ reqQty:使用 bomMaterial.qty * proportion(BOM 单位,已按比例调整) reqQty = if (reqUomId in decimalUomIds) { reqQtyInBomUnit.toDouble() } else { - reqQtyInBomUnit.setScale(0, RoundingMode.HALF_UP).toDouble() + reqQtyInBomUnit.setScale(0, RoundingMode.UP).toDouble() }, baseReqQty = baseReqQty, - + stockQty = stockQty, // ✅ stockReqQty:使用 bomMaterial.stockQty * proportion(库存单位,已按比例调整) stockReqQty = if (stockReqUomId in decimalUomIds) { stockReqQtyInStockUnit.toDouble() } else { - stockReqQtyInStockUnit.setScale(0, RoundingMode.HALF_UP).toDouble() + stockReqQtyInStockUnit.setScale(0, RoundingMode.UP).toDouble() }, baseStockQty = baseStockQty, - + reqUom = uomName ?: "", reqBaseUom = baseUomName ?: "", stockUom = stockUomName ?: "", stockBaseUom = baseUomName ?: "", - + type = line.item?.type ?: "", availableStatus = availableStatus, bomProcessId = bomProcessMaterial?.bomProcess?.id ?: 0, @@ -874,19 +888,19 @@ val sufficientStockQty = bomMaterials } } - open fun createProductProcessByJobOrderId(jobOrderId: Long,productionPriority: Int?): MessageResponse { + open fun createProductProcessByJobOrderId(jobOrderId: Long, productionPriority: Int?): 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 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 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.productProcessCode = productProcessCode this.item = item this.status = ProductProcessStatus.PENDING this.date = jobOrder?.planEnd?.toLocalDate() @@ -897,21 +911,21 @@ val sufficientStockQty = bomMaterials 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 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?.code?:"" + // this.equipment = equipment + this.seqNo = bomProcess.seqNo ?: 0 + this.name = process?.name ?: "" + this.description = bomProcess.description ?: "" + this.equipmentType = equipment?.code ?: "" this.status = "Pending" this.processingTime = bomProcess.durationInMinute this.setupTime = bomProcess.prepTimeInMinute this.changeoverTime = bomProcess.postProdTimeInMinute - this.isOriginal=true + this.isOriginal = true } productProcessLineRepository.save(productProcessLine) } @@ -925,10 +939,11 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun UpdateProductProcessLineOperatorIdOrEquipmentId(request: UpdateProductProcessLineOperatorIdOrEquipmentIdRequest): MessageResponse { val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null) - + val equipmentTypeSubTypeEquipmentNo = request.equipmentTypeSubTypeEquipmentNo println("equipmentTypeSubTypeEquipmentNo ${equipmentTypeSubTypeEquipmentNo}") val staffNo = request.staffNo @@ -944,7 +959,7 @@ val sufficientStockQty = bomMaterials errorPosition = "staffNo" ) } - val user = userRepository.findByStaffNo(staffNo?:"").orElse(null) + val user = userRepository.findByStaffNo(staffNo ?: "").orElse(null) println("user ${user?.id}") if (user == null) { println("User not found with staffNo: $staffNo") @@ -957,12 +972,12 @@ val sufficientStockQty = bomMaterials errorPosition = "staffNo" ) } - val equipmentDetail = equipmentDetailRepository.findByCode(equipmentTypeSubTypeEquipmentNo?:"") + val equipmentDetail = equipmentDetailRepository.findByCode(equipmentTypeSubTypeEquipmentNo ?: "") println("equipmentDetail ${equipmentDetail?.id}") val equipmentId = equipmentDetail?.equipmentTypeId println("equipmentId ${equipmentId}") - val bomProcess= bomProcessRepository.findById(productProcessLine?.bomProcess?.id?:0L).orElse(null) - val bomProcessEquipment=bomProcess?.equipment + val bomProcess = bomProcessRepository.findById(productProcessLine?.bomProcess?.id ?: 0L).orElse(null) + val bomProcessEquipment = bomProcess?.equipment println("bomProcessEquipment ${bomProcessEquipment?.id}") if (equipmentId != null && user != null) { // 检查 equipmentId 是否与 bomProcessEquipment 匹配 @@ -972,7 +987,7 @@ val sufficientStockQty = bomMaterials println("user ${user?.id}") println("bomProcess ${bomProcess?.id}") println("not match equipment id${equipmentId} and ${bomProcessEquipment?.id}") - + // 返回错误响应 return MessageResponse( id = request.productProcessLineId, @@ -984,16 +999,16 @@ val sufficientStockQty = bomMaterials ) } } - if(equipmentId != null &&( equipmentId==bomProcessEquipment?.id)) { - + if (equipmentId != null && (equipmentId == bomProcessEquipment?.id)) { + productProcessLine?.equipment = bomProcessEquipment productProcessLine?.equipmentDetailId = equipmentDetail?.id productProcessLineRepository.save(productProcessLine) } - - productProcessLine.operator = user - productProcessLineRepository.save(productProcessLine) - + + productProcessLine.operator = user + productProcessLineRepository.save(productProcessLine) + return MessageResponse( id = null, @@ -1008,96 +1023,97 @@ val sufficientStockQty = bomMaterials open fun NewUpdateProductProcessLineOperatorIdOrEquipmentIdAndEquipmentDetail(request: NewUpdateProductProcessLineOperatorIdOrEquipmentIdAndEquipmentDetailRequest): MessageResponse { val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null) val equipmentDetail = equipmentDetailRepository.findByEquipmentCode(request.equipmentCode) - - val user = userRepository.findByStaffNo(request.staffNo?:"").orElse(null) - val bomProcess= bomProcessRepository.findById(productProcessLine?.bomProcess?.id?:0L).orElse(null) - val bomProcessEquipment=bomProcess?.equipment + + val user = userRepository.findByStaffNo(request.staffNo ?: "").orElse(null) + val bomProcess = bomProcessRepository.findById(productProcessLine?.bomProcess?.id ?: 0L).orElse(null) + val bomProcessEquipment = bomProcess?.equipment // ===== 校验区 ===== - // 情况:设备有 + 人空 → 不通过 - if (equipmentDetail != null && user == null) { - return MessageResponse( - id = request.productProcessLineId, - code = "400", - name = "User Required", - type = "error", - message = "Staff No is required", - errorPosition = "staffNo" - ) - } + // 情况:设备有 + 人空 → 不通过 + if (equipmentDetail != null && user == null) { + return MessageResponse( + id = request.productProcessLineId, + code = "400", + name = "User Required", + type = "error", + message = "Staff No is required", + errorPosition = "staffNo" + ) + } - // 情况:设备有 + 人有 + 设备不匹配 → 不通过 - if (equipmentDetail != null && user != null && - equipmentDetail.equipmentTypeId != bomProcessEquipment?.id - ) { - println("not match equipment id${equipmentDetail.equipmentTypeId} and ${bomProcessEquipment?.id}") - return MessageResponse( - id = request.productProcessLineId, - code = "400", - name = "Equipment Validation Failed", - type = "error", - message = "Input Equipment is not match with process", - errorPosition = "equipmentId" - ) - } + // 情况:设备有 + 人有 + 设备不匹配 → 不通过 + if (equipmentDetail != null && user != null && + equipmentDetail.equipmentTypeId != bomProcessEquipment?.id + ) { + println("not match equipment id${equipmentDetail.equipmentTypeId} and ${bomProcessEquipment?.id}") + return MessageResponse( + id = request.productProcessLineId, + code = "400", + name = "Equipment Validation Failed", + type = "error", + message = "Input Equipment is not match with process", + errorPosition = "equipmentId" + ) + } - // 情况:设备空 + 人空 → 不通过(如你不需要这个,可以去掉或改成通过) - if (equipmentDetail == null && user == null) { - return MessageResponse( - id = request.productProcessLineId, - code = "400", - name = "User Required", - type = "error", - message = "Staff No is required", - errorPosition = "staffNo" - ) - } - if (user != null && productProcessLine.operator != null) { - val existingOperatorId = productProcessLine.operator?.id - val newOperatorId = user.id - - // 如果不是同一个用户,拒绝更新 - if (existingOperatorId != null && existingOperatorId != newOperatorId) { + // 情况:设备空 + 人空 → 不通过(如你不需要这个,可以去掉或改成通过) + if (equipmentDetail == null && user == null) { return MessageResponse( id = request.productProcessLineId, - code = "409", - name = "Conflict", + code = "400", + name = "User Required", type = "error", - message = "This process line is already assigned to another operator", - errorPosition = "operator" + message = "Staff No is required", + errorPosition = "staffNo" ) } - } - // ===== 通过校验,开始更新 ===== + if (user != null && productProcessLine.operator != null) { + val existingOperatorId = productProcessLine.operator?.id + val newOperatorId = user.id - // 设备有(且已通过匹配校验) → 更新 equipment / equipmentDetailId - if (equipmentDetail != null) { - val equipment = equipmentRepository.findById(equipmentDetail.equipmentTypeId!!).orElse(null) - productProcessLine?.equipment = equipment - productProcessLine?.equipmentDetailId = equipmentDetail.id - } + // 如果不是同一个用户,拒绝更新 + if (existingOperatorId != null && existingOperatorId != newOperatorId) { + return MessageResponse( + id = request.productProcessLineId, + code = "409", + name = "Conflict", + type = "error", + message = "This process line is already assigned to another operator", + errorPosition = "operator" + ) + } + } + // ===== 通过校验,开始更新 ===== + + // 设备有(且已通过匹配校验) → 更新 equipment / equipmentDetailId + if (equipmentDetail != null) { + val equipment = equipmentRepository.findById(equipmentDetail.equipmentTypeId!!).orElse(null) + productProcessLine?.equipment = equipment + productProcessLine?.equipmentDetailId = equipmentDetail.id + } + + // 人有(设备可以有也可以没有) → 更新 operator + if (user != null) { + productProcessLine?.operator = user + } + + productProcessLineRepository.save(productProcessLine) - // 人有(设备可以有也可以没有) → 更新 operator - if (user != null) { - productProcessLine?.operator = user + return MessageResponse( + id = null, + code = null, + name = null, + type = null, + message = null, + errorPosition = null, + ) } - productProcessLineRepository.save(productProcessLine) - - return MessageResponse( - id = null, - code = null, - name = null, - type = null, - message = null, - errorPosition = null, - ) -} open fun UpdateProductProcessLineHandlerId(request: UpdateProductProcessLineHandlerIdRequest): MessageResponse { val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null) val handlerId = request.handlerId - val user = userRepository.findById(handlerId?:0L).orElse(null) - if(user != null) { + val user = userRepository.findById(handlerId ?: 0L).orElse(null) + if (user != null) { productProcessLine.handler = user productProcessLineRepository.save(productProcessLine) } @@ -1112,9 +1128,11 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun getJobOrderProcessLineDetail(productProcessLineId: Long): JobOrderProcessLineDetailResponse { val productProcessLine = productProcessLineRepository.findById(productProcessLineId).orElse(null) - val productProcess = productProcessRepository.findById(productProcessLine?.productProcess?.id?:0L).orElse(null) + val productProcess = + productProcessRepository.findById(productProcessLine?.productProcess?.id ?: 0L).orElse(null) val bomProcessId = productProcessLine?.bomProcess?.id println("bomProcessId ${bomProcessId}") val bomProcess: BomProcess? = bomProcessId?.let { @@ -1122,8 +1140,9 @@ val sufficientStockQty = bomMaterials } println("bomProcess ${bomProcess?.id}") println("bomProcess durationInMinute ${bomProcess?.durationInMinute}") - val productProcessIssue = productionProcessIssueRepository.findByProductProcessLineId(productProcessLineId).filter{it.status == "Paused"}.firstOrNull() - + val productProcessIssue = productionProcessIssueRepository.findByProductProcessLineId(productProcessLineId) + .filter { it.status == "Paused" }.firstOrNull() + // ✅ 计算所有已恢复的暂停记录的总暂停时间(毫秒) val allIssues = productionProcessIssueRepository.findByProductProcessLineId(productProcessLineId) val totalPausedTimeMs = allIssues @@ -1135,46 +1154,46 @@ val sufficientStockQty = bomMaterials } return JobOrderProcessLineDetailResponse( - id = productProcessLine.id?:0, - productProcessId = productProcessLine.productProcess ?.id?:0, - bomProcessId = productProcessLine.bomProcess?.id?:0, - operatorId = productProcessLine.operator?.id?:0, - operatorName = productProcessLine.operator?.name?:"", - handlerId = productProcessLine.handler?.id?:0, + id = productProcessLine.id ?: 0, + productProcessId = productProcessLine.productProcess?.id ?: 0, + bomProcessId = productProcessLine.bomProcess?.id ?: 0, + operatorId = productProcessLine.operator?.id ?: 0, + operatorName = productProcessLine.operator?.name ?: "", + handlerId = productProcessLine.handler?.id ?: 0, durationInMinutes = bomProcess?.durationInMinute, - productProcessIssueId = productProcessIssue?.id?:0, - productProcessIssueStatus = productProcessIssue?.status?:"", - - seqNo = productProcessLine.seqNo?:0, - name = productProcessLine.name?:"", - description = productProcessLine.description?:"", + productProcessIssueId = productProcessIssue?.id ?: 0, + productProcessIssueStatus = productProcessIssue?.status ?: "", + + seqNo = productProcessLine.seqNo ?: 0, + name = productProcessLine.name ?: "", + description = productProcessLine.description ?: "", equipmentType = productProcessLine.equipmentType, - equipmentId = productProcessLine.equipment?.id?:0, - startTime = productProcessLine.startTime?:LocalDateTime.now(), - endTime = productProcessLine.endTime?:LocalDateTime.now(), + equipmentId = productProcessLine.equipment?.id ?: 0, + startTime = productProcessLine.startTime ?: LocalDateTime.now(), + endTime = productProcessLine.endTime ?: LocalDateTime.now(), stopTime = productProcessIssue?.stopTime, - submitedBagRecord = productProcess.submitedBagRecord?:false, + submitedBagRecord = productProcess.submitedBagRecord ?: false, // ✅ 添加总暂停时间(毫秒) totalPausedTimeMs = totalPausedTimeMs.toLong(), - status = productProcessLine.status?:"", - outputFromProcessQty = productProcessLine.outputFromProcessQty?:0, - outputFromProcessUom = productProcessLine.outputFromProcessUom?:"", - defectQty = productProcessLine.defectQty?:0, - defectUom = productProcessLine.defectUom?:"", - defectDescription = productProcessLine.defectDescription?:"", - defectQty2 = productProcessLine.defectQty2?:0, - defectUom2 = productProcessLine.defectUom2?:"", - defectDescription2 = productProcessLine.defectDescription2?:"", - defectQty3 = productProcessLine.defectQty3?:0, - defectUom3 = productProcessLine.defectUom3?:"", - defectDescription3 = productProcessLine.defectDescription3?:"", - scrapQty = productProcessLine.scrapQty?:0, - scrapUom = productProcessLine.scrapUom?:"", - byproductId = productProcessLine.byproduct?.id?:0, - byproductName = productProcessLine.byproduct?.name?:"", - byproductQty = productProcessLine.byproductQty?:0, - byproductUom = productProcessLine.byproductUom?:"" + status = productProcessLine.status ?: "", + outputFromProcessQty = productProcessLine.outputFromProcessQty ?: 0, + outputFromProcessUom = productProcessLine.outputFromProcessUom ?: "", + defectQty = productProcessLine.defectQty ?: 0, + defectUom = productProcessLine.defectUom ?: "", + defectDescription = productProcessLine.defectDescription ?: "", + defectQty2 = productProcessLine.defectQty2 ?: 0, + defectUom2 = productProcessLine.defectUom2 ?: "", + defectDescription2 = productProcessLine.defectDescription2 ?: "", + defectQty3 = productProcessLine.defectQty3 ?: 0, + defectUom3 = productProcessLine.defectUom3 ?: "", + defectDescription3 = productProcessLine.defectDescription3 ?: "", + scrapQty = productProcessLine.scrapQty ?: 0, + scrapUom = productProcessLine.scrapUom ?: "", + byproductId = productProcessLine.byproduct?.id ?: 0, + byproductName = productProcessLine.byproduct?.name ?: "", + byproductQty = productProcessLine.byproductQty ?: 0, + byproductUom = productProcessLine.byproductUom ?: "" ) } @@ -1182,13 +1201,13 @@ val sufficientStockQty = bomMaterials println(" Service: Updating ProductProcessLine Status: $productProcessLineId") val productProcessLine = productProcessLineRepository.findById(productProcessLineId).orElse(null) println(" Service: ProductProcessLine: $productProcessLine") - productProcessLine.status = status - // productProcessLine.endTime = LocalDateTime.now() - productProcessLineRepository.save(productProcessLine) - println(" Service: ProductProcessLine Status Updated: ${productProcessLine.status}") - CompleteProductProcessStatusIfAllLinesCompleted(productProcessLine.productProcess?.id?:0) - - + productProcessLine.status = status + // productProcessLine.endTime = LocalDateTime.now() + productProcessLineRepository.save(productProcessLine) + println(" Service: ProductProcessLine Status Updated: ${productProcessLine.status}") + CompleteProductProcessStatusIfAllLinesCompleted(productProcessLine.productProcess?.id ?: 0) + + return MessageResponse( id = productProcessLineId, code = "200", @@ -1198,9 +1217,11 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun passProductProcessLine(productProcessLineId: Long): MessageResponse { val productProcessLine = productProcessLineRepository.findById(productProcessLineId).orElse(null) - val productProcess = productProcessRepository.findById(productProcessLine?.productProcess?.id?:0L).orElse(null) + val productProcess = + productProcessRepository.findById(productProcessLine?.productProcess?.id ?: 0L).orElse(null) // 如果 startTime 为空,才设置它(保留已存在的 startTime) println(" Service: ProductProcessLine StartTime: ${productProcessLine.startTime}") println(" Service: ProductProcess StartTime: ${productProcess.startTime}") @@ -1234,21 +1255,21 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun CompleteProductProcessStatusIfAllLinesCompleted(productProcessId: Long): MessageResponse { val productProcess = productProcessRepository.findById(productProcessId).orElse(null) println(" Service: ProductProcess: $productProcess") val productProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcessId) println(" Service: ProductProcessLines: $productProcessLines") - if(productProcessLines.all { it.status == "Completed" || it.status == "Pass" }) { + if (productProcessLines.all { it.status == "Completed" || it.status == "Pass" }) { productProcess.status = ProductProcessStatus.COMPLETED if (productProcess.endTime == null) { productProcess.endTime = LocalDateTime.now() } productProcessRepository.save(productProcess) println(" Service: ProductProcess Status Updated: ${productProcess.status}") - } - else { + } else { println(" Service: ProductProcess Lines are not completed") } return MessageResponse( @@ -1279,56 +1300,56 @@ val sufficientStockQty = bomMaterials val byproductQty = request.byproductQty val byproductUom = request.byproductUom val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null) - if(outputFromProcessQty != null) { - productProcessLine.outputFromProcessQty = outputFromProcessQty + if (outputFromProcessQty != null) { + productProcessLine.outputFromProcessQty = outputFromProcessQty } - if(outputFromProcessUom != null) { - productProcessLine.outputFromProcessUom = outputFromProcessUom + if (outputFromProcessUom != null) { + productProcessLine.outputFromProcessUom = outputFromProcessUom } - if(defectQty != null) { - productProcessLine.defectQty = defectQty + if (defectQty != null) { + productProcessLine.defectQty = defectQty } - if(defectUom != null) { - productProcessLine.defectUom = defectUom + if (defectUom != null) { + productProcessLine.defectUom = defectUom } - if(defectDescription != null) { - productProcessLine.defectDescription = defectDescription + if (defectDescription != null) { + productProcessLine.defectDescription = defectDescription } - - if(defect2Qty != null) { - productProcessLine.defectQty2 = defect2Qty + + if (defect2Qty != null) { + productProcessLine.defectQty2 = defect2Qty } - if(defect2Uom != null) { - productProcessLine.defectUom2 = defect2Uom + if (defect2Uom != null) { + productProcessLine.defectUom2 = defect2Uom } - if(defect2Description != null) { - productProcessLine.defectDescription2 = defect2Description + if (defect2Description != null) { + productProcessLine.defectDescription2 = defect2Description } - if(defect3Qty != null) { - productProcessLine.defectQty3 = defect3Qty + if (defect3Qty != null) { + productProcessLine.defectQty3 = defect3Qty } - if(defect3Uom != null) { - productProcessLine.defectUom3 = defect3Uom + if (defect3Uom != null) { + productProcessLine.defectUom3 = defect3Uom } - if(defect3Description != null) { - productProcessLine.defectDescription3 = defect3Description + if (defect3Description != null) { + productProcessLine.defectDescription3 = defect3Description } - if(scrapQty != null) { - productProcessLine.scrapQty = scrapQty + if (scrapQty != null) { + productProcessLine.scrapQty = scrapQty } - if(scrapUom != null) { - productProcessLine.scrapUom = scrapUom + if (scrapUom != null) { + productProcessLine.scrapUom = scrapUom } - if(byproductName != null) { - productProcessLine.byproductName = byproductName + if (byproductName != null) { + productProcessLine.byproductName = byproductName } - if(byproductQty != null) { - productProcessLine.byproductQty = byproductQty + if (byproductQty != null) { + productProcessLine.byproductQty = byproductQty } - if(byproductUom != null) { - productProcessLine.byproductUom = byproductUom + if (byproductUom != null) { + productProcessLine.byproductUom = byproductUom } productProcessLineRepository.save(productProcessLine) CompleteProductProcessLine(request.productProcessLineId) @@ -1347,15 +1368,18 @@ val sufficientStockQty = bomMaterials val productProcessIds = productProcesses.map { it.id } return productProcesses.map { productProcesses -> - val productProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcesses.id?:0L) - val jobOrder = jobOrderRepository.findById(productProcesses.jobOrder?.id?:0L).orElse(null) - val FinishedProductProcessLineCount = productProcessLineRepository.findByProductProcess_Id(productProcesses.id?:0L).count { it.status == "Completed" } + val productProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcesses.id ?: 0L) + val jobOrder = jobOrderRepository.findById(productProcesses.jobOrder?.id ?: 0L).orElse(null) + val FinishedProductProcessLineCount = + productProcessLineRepository.findByProductProcess_Id(productProcesses.id ?: 0L) + .count { it.status == "Completed" } val stockInLine = jobOrder?.stockInLines?.firstOrNull() val stockInLineId = stockInLine?.id - val pickOrder = pickOrderRepository.findAllByJobOrder_Id(jobOrder?.id?:0L).firstOrNull() - val joPickOrdersList = joPickOrderRepository.findByPickOrderId(pickOrder?.id?:0L) - val itemUom = itemUomRepository.findByItemIdAndStockUnitIsTrueAndDeletedIsFalse(productProcesses.item?.id?:0L) - val bomUom = uomConversionRepository.findById(itemUom?.uom?.id?:0L).orElse(null) + val pickOrder = pickOrderRepository.findAllByJobOrder_Id(jobOrder?.id ?: 0L).firstOrNull() + val joPickOrdersList = joPickOrderRepository.findByPickOrderId(pickOrder?.id ?: 0L) + val itemUom = + itemUomRepository.findByItemIdAndStockUnitIsTrueAndDeletedIsFalse(productProcesses.item?.id ?: 0L) + val bomUom = uomConversionRepository.findById(itemUom?.uom?.id ?: 0L).orElse(null) //val silHandlerId = stockInLine?.escalationLog?.firstOrNull { it.status == "pending" }?.handler?.id val timeNeedToComplete = bomProcessRepository.findByBomId(productProcesses.bom?.id ?: 0L) .filter { !it.deleted } @@ -1366,14 +1390,15 @@ val sufficientStockQty = bomMaterials status = productProcesses.status.value, startTime = productProcesses.startTime, endTime = productProcesses.endTime, - matchStatus = if (joPickOrdersList.isNotEmpty() && - joPickOrdersList.all { it.matchStatus == JoPickOrderStatus.completed }) { - "completed" - } else if (joPickOrdersList.any { it.matchStatus == JoPickOrderStatus.scanned }) { - "scanned" - } else { - "pending" - }, + matchStatus = if (joPickOrdersList.isNotEmpty() && + joPickOrdersList.all { it.matchStatus == JoPickOrderStatus.completed } + ) { + "completed" + } else if (joPickOrdersList.any { it.matchStatus == JoPickOrderStatus.scanned }) { + "scanned" + } else { + "pending" + }, RequiredQty = jobOrder?.reqQty?.toInt() ?: 0, Uom = bomUom?.udfudesc, productionPriority = productProcesses.productionPriority, @@ -1397,7 +1422,7 @@ val sufficientStockQty = bomMaterials operatorId = line.operator?.id, operatorName = line.operator?.name ?: "", equipmentId = line.equipment?.id, - equipmentName = line.equipmentType?: "", + equipmentName = line.equipmentType ?: "", startTime = line.startTime, endTime = line.endTime, status = line.status ?: "" @@ -1439,6 +1464,7 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun updateProductProcessStartTime(productProcessId: Long): MessageResponse { val productProcess = productProcessRepository.findById(productProcessId).orElse(null) productProcess.startTime = LocalDateTime.now() @@ -1452,6 +1478,7 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun updateProductProcessEndTime(productProcessId: Long): MessageResponse { val productProcess = productProcessRepository.findById(productProcessId).orElse(null) productProcess.endTime = LocalDateTime.now() @@ -1465,7 +1492,8 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } - open fun updateProductProcessStatus(productProcessId: Long, status: ProductProcessStatus): MessageResponse { + + open fun updateProductProcessStatus(productProcessId: Long, status: ProductProcessStatus): MessageResponse { val productProcess = productProcessRepository.findById(productProcessId).orElse(null) productProcess.status = status productProcessRepository.save(productProcess) @@ -1478,17 +1506,18 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun StartProductProcessLine(productProcessLineId: Long): MessageResponse { updateProductProcessLineStartTime(productProcessLineId) //updateProductProcessLineStatus(productProcessLineId, "InProgress") val productProcessLine = productProcessLineRepository.findById(productProcessLineId).orElse(null) - val allproductProcessLines=productProcessLineRepository.findByProductProcess_Id(productProcessLine.productProcess?.id?:0L) - if(allproductProcessLines.all { it.status == "Pending" }) { - updateProductProcessLineStatus(productProcessLineId, "InProgress") - updateProductProcessStartTime(productProcessLine.productProcess.id) - updateProductProcessStatus(productProcessLine.productProcess.id, ProductProcessStatus.IN_PROGRESS) - } - else{ + val allproductProcessLines = + productProcessLineRepository.findByProductProcess_Id(productProcessLine.productProcess?.id ?: 0L) + if (allproductProcessLines.all { it.status == "Pending" }) { + updateProductProcessLineStatus(productProcessLineId, "InProgress") + updateProductProcessStartTime(productProcessLine.productProcess.id) + updateProductProcessStatus(productProcessLine.productProcess.id, ProductProcessStatus.IN_PROGRESS) + } else { updateProductProcessLineStatus(productProcessLineId, "InProgress") println(" Service: ProductProcess Lines are not Pending") println(" Service: ProductProcess Lines: ${allproductProcessLines.map { it.status }}") @@ -1503,39 +1532,40 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun CompleteProductProcessLine(productProcessLineId: Long): MessageResponse { updateProductProcessLineEndTime(productProcessLineId) updateProductProcessLineStatus(productProcessLineId, "Completed") val productProcessLine = productProcessLineRepository.findById(productProcessLineId).orElse(null) val productProcessId = productProcessLine?.productProcess?.id ?: 0L - val allproductProcessLines=productProcessLineRepository.findByProductProcess_Id(productProcessId) - if(productProcessLine.startTime == null) { + val allproductProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcessId) + if (productProcessLine.startTime == null) { productProcessLine.startTime = LocalDateTime.now() productProcessLineRepository.save(productProcessLine) } - if(allproductProcessLines.all { it.status == "Completed"|| it.status == "Pass" }) { - updateProductProcessEndTime(productProcessId) - updateProductProcessStatus(productProcessId, ProductProcessStatus.COMPLETED) - val productProcess = productProcessRepository.findById(productProcessId).orElse(null) - val jobOrder = jobOrderRepository.findById(productProcess?.jobOrder?.id?:0L).orElse(null) - if(jobOrder != null) { - jobOrder.status = JobOrderStatus.STORING - jobOrderRepository.save(jobOrder) - - stockInLineService.create( - SaveStockInLineRequest( - itemId = productProcess?.item?.id?:0L, - acceptedQty = jobOrder?.reqQty?:BigDecimal.ZERO, - productLotNo = jobOrder?.code, - productionDate = LocalDate.now(), - jobOrderId = jobOrder.id, - acceptQty =jobOrder?.reqQty?:BigDecimal.ZERO , - //acceptQty = null, - expiryDate=null, - status="pending", + if (allproductProcessLines.all { it.status == "Completed" || it.status == "Pass" }) { + updateProductProcessEndTime(productProcessId) + updateProductProcessStatus(productProcessId, ProductProcessStatus.COMPLETED) + val productProcess = productProcessRepository.findById(productProcessId).orElse(null) + val jobOrder = jobOrderRepository.findById(productProcess?.jobOrder?.id ?: 0L).orElse(null) + if (jobOrder != null) { + jobOrder.status = JobOrderStatus.STORING + jobOrderRepository.save(jobOrder) + + stockInLineService.create( + SaveStockInLineRequest( + itemId = productProcess?.item?.id ?: 0L, + acceptedQty = jobOrder?.reqQty ?: BigDecimal.ZERO, + productLotNo = jobOrder?.code, + productionDate = LocalDate.now(), + jobOrderId = jobOrder.id, + acceptQty = jobOrder?.reqQty ?: BigDecimal.ZERO, + //acceptQty = null, + expiryDate = null, + status = "pending", + ) ) - ) - + } } return MessageResponse( @@ -1547,12 +1577,13 @@ val sufficientStockQty = bomMaterials errorPosition = null, ) } + open fun ifAllLinesCompletedOrPassed(productProcessId: Long): MessageResponse { // 获取所有 product process lines val allproductProcessLines = productProcessLineRepository.findByProductProcess_Id(productProcessId) // 检查是否所有 lines 都是 "Completed" 或 "Pass" - if(allproductProcessLines.all { it.status == "Completed" || it.status == "Pass" }) { + if (allproductProcessLines.all { it.status == "Completed" || it.status == "Pass" }) { // 更新 product process 的 endTime 和状态 updateProductProcessEndTime(productProcessId) updateProductProcessStatus(productProcessId, ProductProcessStatus.COMPLETED) @@ -1560,7 +1591,7 @@ val sufficientStockQty = bomMaterials val productProcess = productProcessRepository.findById(productProcessId).orElse(null) val jobOrder = jobOrderRepository.findById(productProcess?.jobOrder?.id ?: 0L).orElse(null) - if(jobOrder != null) { + if (jobOrder != null) { jobOrder.status = JobOrderStatus.STORING jobOrderRepository.save(jobOrder) @@ -1590,18 +1621,18 @@ val sufficientStockQty = bomMaterials } open fun SaveProductProcessIssueTime(request: SaveProductProcessIssueTimeRequest): MessageResponse { - + val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null) - val productProcess = productProcessRepository.findById(productProcessLine.productProcess?.id?:0L).orElse(null) + val productProcess = productProcessRepository.findById(productProcessLine.productProcess?.id ?: 0L).orElse(null) //val productProcessLines=productProcessLineRepository.findByProductProcess_Id(productProcessId) - - - val startTime=productProcessLine?.startTime - - val stopTime=LocalDateTime.now() - - val operatorId=productProcessLine.operator?.id - val Operator=userRepository.findById(operatorId).orElse(null) + + + val startTime = productProcessLine?.startTime + + val stopTime = LocalDateTime.now() + + val operatorId = productProcessLine.operator?.id + val Operator = userRepository.findById(operatorId).orElse(null) val reason = request.reason val productProcessIssue = ProductionProcessIssue().apply { this.productProcess = productProcess @@ -1628,319 +1659,330 @@ val sufficientStockQty = bomMaterials } - open fun SaveProductProcessResumeTime(productProcessIssueId: Long): MessageResponse { - println(" Service: Saving ProductProcess Resume Time: $productProcessIssueId") - val productProcessLineIssue = productionProcessIssueRepository.findById(productProcessIssueId).orElse(null) - val productProcessLine = productProcessLineRepository.findById(productProcessLineIssue.productProcessLineId?:0L).orElse(null) - val resumeTime = LocalDateTime.now() - println(" Service: Resume Time: $resumeTime") - productProcessLineIssue?.resumeTime = resumeTime - println(" Service: Resume Time: $resumeTime") - val totalTime = resumeTime.toEpochSecond(ZoneOffset.UTC) - (productProcessLineIssue.stopTime?.toEpochSecond(ZoneOffset.UTC) ?: 0L) - productProcessLineIssue?.totalTime = totalTime.toInt() - productProcessLineIssue?.status = "Resumed" - productionProcessIssueRepository.save(productProcessLineIssue) - productProcessLine.status = "InProgress" - productProcessLineRepository.save(productProcessLine) - - return MessageResponse( - id = 0, - code = "200", - name = "ProductProcess ResumeTime Updated", - type = "success", - message = "ProductProcess ResumeTime Updated", - errorPosition = null, - ) - - } - open fun UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTime(request: UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTimeRequest): MessageResponse { - val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null) - val processingTime = request.processingTime - val setupTime = request.setupTime - val changeoverTime = request.changeoverTime - if(processingTime != null) { - productProcessLine.processingTime = processingTime + open fun SaveProductProcessResumeTime(productProcessIssueId: Long): MessageResponse { + println(" Service: Saving ProductProcess Resume Time: $productProcessIssueId") + val productProcessLineIssue = productionProcessIssueRepository.findById(productProcessIssueId).orElse(null) + val productProcessLine = + productProcessLineRepository.findById(productProcessLineIssue.productProcessLineId ?: 0L).orElse(null) + val resumeTime = LocalDateTime.now() + println(" Service: Resume Time: $resumeTime") + productProcessLineIssue?.resumeTime = resumeTime + println(" Service: Resume Time: $resumeTime") + val totalTime = + resumeTime.toEpochSecond(ZoneOffset.UTC) - (productProcessLineIssue.stopTime?.toEpochSecond(ZoneOffset.UTC) + ?: 0L) + productProcessLineIssue?.totalTime = totalTime.toInt() + productProcessLineIssue?.status = "Resumed" + productionProcessIssueRepository.save(productProcessLineIssue) + productProcessLine.status = "InProgress" productProcessLineRepository.save(productProcessLine) + + return MessageResponse( + id = 0, + code = "200", + name = "ProductProcess ResumeTime Updated", + type = "success", + message = "ProductProcess ResumeTime Updated", + errorPosition = null, + ) + } - if(setupTime != null) { - productProcessLine.setupTime = setupTime - productProcessLineRepository.save(productProcessLine) + + open fun UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTime(request: UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTimeRequest): MessageResponse { + val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null) + val processingTime = request.processingTime + val setupTime = request.setupTime + val changeoverTime = request.changeoverTime + if (processingTime != null) { + productProcessLine.processingTime = processingTime + productProcessLineRepository.save(productProcessLine) + } + if (setupTime != null) { + productProcessLine.setupTime = setupTime + productProcessLineRepository.save(productProcessLine) + } + if (changeoverTime != null) { + productProcessLine.changeoverTime = changeoverTime + productProcessLineRepository.save(productProcessLine) + } + return MessageResponse( + id = request.productProcessLineId, + code = "200", + name = "ProductProcess ProcessingTimeSetupTimeChangeoverTime Updated", + type = "success", + message = "ProductProcess ProcessingTimeSetupTimeChangeoverTime Updated", + errorPosition = null, + ) } - if(changeoverTime != null) { - productProcessLine.changeoverTime = changeoverTime - productProcessLineRepository.save(productProcessLine) + + open fun UpdateProductProcessPriority(productProcessId: Long, productionPriority: Int): MessageResponse { + val productProcess = productProcessRepository.findById(productProcessId).orElse(null) + productProcess.productionPriority = productionPriority + productProcessRepository.save(productProcess) + return MessageResponse( + id = productProcessId, + code = "200", + name = "ProductProcess Priority Updated", + type = "success", + message = "ProductProcess Priority Updated", + errorPosition = null, + ) } - return MessageResponse( - id = request.productProcessLineId, - code = "200", - name = "ProductProcess ProcessingTimeSetupTimeChangeoverTime Updated", - type = "success", - message = "ProductProcess ProcessingTimeSetupTimeChangeoverTime Updated", - errorPosition = null, - ) - } - open fun UpdateProductProcessPriority(productProcessId: Long, productionPriority: Int): MessageResponse { - val productProcess = productProcessRepository.findById(productProcessId).orElse(null) - productProcess.productionPriority = productionPriority - productProcessRepository.save(productProcess) - return MessageResponse( - id = productProcessId, - code = "200", - name = "ProductProcess Priority Updated", - type = "success", - message = "ProductProcess Priority Updated", - errorPosition = null, - ) - } - open fun createNewProductProcessLine(productProcessLineId: Long): MessageResponse { - val sourceLine = productProcessLineRepository.findById(productProcessLineId).orElse(null) - ?: return MessageResponse( + + open fun createNewProductProcessLine(productProcessLineId: Long): MessageResponse { + val sourceLine = 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 = sourceLine.productProcess?.id ?: return MessageResponse( id = productProcessLineId, - code = "404", - name = "ProductProcess Line Not Found", + code = "400", + name = "Invalid ProductProcess", type = "error", - message = "ProductProcess Line with ID $productProcessLineId not found", + message = "ProductProcess Line has no associated ProductProcess", + errorPosition = null, + ) + + val originalSeqNo = sourceLine.seqNo ?: return MessageResponse( + id = productProcessLineId, + code = "400", + name = "Invalid SeqNo", + type = "error", + message = "ProductProcess Line has no seqNo", + errorPosition = null, + ) + + // 先获取同一 productProcess 的所有 lines(在更新前) + val allLines = productProcessLineRepository.findByProductProcess_Id(productProcessId) + + // 更新所有其他 seqNo >= originalSeqNo + 1 的 lines(不包括原 line) + allLines.filter { + it.id != sourceLine.id && + it.seqNo != null && + it.seqNo!! >= originalSeqNo + 1 + }.forEach { line -> + line.seqNo = (line.seqNo ?: 0) + 1 + productProcessLineRepository.save(line) + } + + // 创建新的 line,复制所有字段 + val newLine = ProductProcessLine().apply { + // 复制基本字段 + this.productProcess = sourceLine.productProcess + this.bomProcess = sourceLine.bomProcess + this.operator = sourceLine.operator + this.equipment = sourceLine.equipment + this.equipmentDetailId = sourceLine.equipmentDetailId + this.handler = sourceLine.handler + this.seqNo = originalSeqNo + 1 // 新 line 的 seqNo = originalSeqNo + 1 + this.name = sourceLine.name + this.description = sourceLine.description + this.equipmentType = sourceLine.equipmentType + this.status = "Pending" // 新创建的 line 状态总是设为 Pending + this.byproduct = sourceLine.byproduct + this.byproductName = sourceLine.byproductName + this.byproductQty = sourceLine.byproductQty + this.byproductUom = sourceLine.byproductUom + this.scrapQty = sourceLine.scrapQty + this.scrapUom = sourceLine.scrapUom + this.defectQty = sourceLine.defectQty + this.defectUom = sourceLine.defectUom + this.defectDescription = sourceLine.defectDescription + this.defectQty2 = sourceLine.defectQty2 + this.defectUom2 = sourceLine.defectUom2 + this.defectDescription2 = sourceLine.defectDescription2 + this.defectQty3 = sourceLine.defectQty3 + this.defectUom3 = sourceLine.defectUom3 + this.defectDescription3 = sourceLine.defectDescription3 + this.outputFromProcessQty = sourceLine.outputFromProcessQty + this.outputFromProcessUom = sourceLine.outputFromProcessUom + this.processingTime = sourceLine.processingTime + this.setupTime = sourceLine.setupTime + this.changeoverTime = sourceLine.changeoverTime + this.startTime = null + this.endTime = null + this.isOriginal = false + } + + // 保存新 line(原 line 的 seqNo 保持不变,不需要更新) + val savedNewLine = productProcessLineRepository.save(newLine) + + return MessageResponse( + id = savedNewLine.id ?: productProcessLineId, + code = "200", + name = "ProductProcess Line Created", + type = "success", + message = "ProductProcess Line Created successfully with seqNo ${originalSeqNo + 1}", errorPosition = null, ) - - val productProcessId = sourceLine.productProcess?.id ?: return MessageResponse( - id = productProcessLineId, - code = "400", - name = "Invalid ProductProcess", - type = "error", - message = "ProductProcess Line has no associated ProductProcess", - errorPosition = null, - ) - - val originalSeqNo = sourceLine.seqNo ?: return MessageResponse( - id = productProcessLineId, - code = "400", - name = "Invalid SeqNo", - type = "error", - message = "ProductProcess Line has no seqNo", - errorPosition = null, - ) - - // 先获取同一 productProcess 的所有 lines(在更新前) - val allLines = productProcessLineRepository.findByProductProcess_Id(productProcessId) - - // 更新所有其他 seqNo >= originalSeqNo + 1 的 lines(不包括原 line) - allLines.filter { - it.id != sourceLine.id && - it.seqNo != null && - it.seqNo!! >= originalSeqNo + 1 - }.forEach { line -> - line.seqNo = (line.seqNo ?: 0) + 1 - productProcessLineRepository.save(line) - } - - // 创建新的 line,复制所有字段 - val newLine = ProductProcessLine().apply { - // 复制基本字段 - this.productProcess = sourceLine.productProcess - this.bomProcess = sourceLine.bomProcess - this.operator = sourceLine.operator - this.equipment = sourceLine.equipment - this.equipmentDetailId = sourceLine.equipmentDetailId - this.handler = sourceLine.handler - this.seqNo = originalSeqNo + 1 // 新 line 的 seqNo = originalSeqNo + 1 - this.name = sourceLine.name - this.description = sourceLine.description - this.equipmentType = sourceLine.equipmentType - this.status = "Pending" // 新创建的 line 状态总是设为 Pending - this.byproduct = sourceLine.byproduct - this.byproductName = sourceLine.byproductName - this.byproductQty = sourceLine.byproductQty - this.byproductUom = sourceLine.byproductUom - this.scrapQty = sourceLine.scrapQty - this.scrapUom = sourceLine.scrapUom - this.defectQty = sourceLine.defectQty - this.defectUom = sourceLine.defectUom - this.defectDescription = sourceLine.defectDescription - this.defectQty2 = sourceLine.defectQty2 - this.defectUom2 = sourceLine.defectUom2 - this.defectDescription2 = sourceLine.defectDescription2 - this.defectQty3 = sourceLine.defectQty3 - this.defectUom3 = sourceLine.defectUom3 - this.defectDescription3 = sourceLine.defectDescription3 - this.outputFromProcessQty = sourceLine.outputFromProcessQty - this.outputFromProcessUom = sourceLine.outputFromProcessUom - this.processingTime = sourceLine.processingTime - this.setupTime = sourceLine.setupTime - this.changeoverTime = sourceLine.changeoverTime - this.startTime = null - this.endTime = null - this.isOriginal = false } - - // 保存新 line(原 line 的 seqNo 保持不变,不需要更新) - val savedNewLine = productProcessLineRepository.save(newLine) - - return MessageResponse( - id = savedNewLine.id ?: productProcessLineId, - code = "200", - name = "ProductProcess Line Created", - type = "success", - message = "ProductProcess Line Created successfully with seqNo ${originalSeqNo + 1}", - errorPosition = null, - ) -} open fun getJobProcessStatus(date: LocalDate?): List { val productProcesses = productProcessRepository.findAllByDeletedIsFalse() .let { list -> if (date == null) list else list.filter { it.date == date } } - - return productProcesses.mapNotNull { process -> - val jobOrder = jobOrderRepository.findById(process.jobOrder?.id ?: 0L).orElse(null) - // 仍然保留:PLANNING 不显示(如你也要显示,删掉这段) - if (jobOrder?.status == JobOrderStatus.PLANNING) return@mapNotNull null + return productProcesses.mapNotNull { process -> + val jobOrder = jobOrderRepository.findById(process.jobOrder?.id ?: 0L).orElse(null) - val lines = productProcessLineRepository.findByProductProcess_Id(process.id ?: 0L) - .sortedBy { it.seqNo } + // 仍然保留:PLANNING 不显示(如你也要显示,删掉这段) + if (jobOrder?.status == JobOrderStatus.PLANNING) return@mapNotNull null - val firstStartTime = lines.firstOrNull { it.startTime != null }?.startTime - val calculatedPlanEndTime = if (firstStartTime != null) { - var totalRemainingMinutes = 0L - lines.forEach { line -> - if (line.endTime == null) { - totalRemainingMinutes += (line.processingTime ?: 0).toLong() - totalRemainingMinutes += (line.setupTime ?: 0).toLong() - totalRemainingMinutes += (line.changeoverTime ?: 0).toLong() + val lines = productProcessLineRepository.findByProductProcess_Id(process.id ?: 0L) + .sortedBy { it.seqNo } + + val firstStartTime = lines.firstOrNull { it.startTime != null }?.startTime + val calculatedPlanEndTime = if (firstStartTime != null) { + var totalRemainingMinutes = 0L + lines.forEach { line -> + if (line.endTime == null) { + totalRemainingMinutes += (line.processingTime ?: 0).toLong() + totalRemainingMinutes += (line.setupTime ?: 0).toLong() + totalRemainingMinutes += (line.changeoverTime ?: 0).toLong() + } } + firstStartTime.plusMinutes(totalRemainingMinutes) + } else { + jobOrder?.planEnd } - firstStartTime.plusMinutes(totalRemainingMinutes) - } else { - jobOrder?.planEnd - } - - JobProcessStatusResponse( - jobOrderId = jobOrder?.id ?: 0L, - 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] - - // equipment.description + equipment_detail.name - val equipmentName = try { line.bomProcess?.equipment?.name } catch (_: jakarta.persistence.EntityNotFoundException) { null } - - - val equipmentDetailName = line.equipmentDetailId?.let { id -> - equipmentDetailRepository.findById(id).orElse(null)?.name - } - ProcessStatusInfo( - processName = line.name, // ✅ 新增:工序名称 - equipmentName = equipmentName, // ✅ 替代 equipmentCode - equipmentDetailName = equipmentDetailName, // ✅ 新增 - startTime = line.startTime, - endTime = line.endTime, - processingTime = line.processingTime, - setupTime = line.setupTime, - changeoverTime = line.changeoverTime, - isRequired = true - ) - } else { - ProcessStatusInfo( - processName = null, - equipmentName = null, - equipmentDetailName = null, - startTime = null, - endTime = null, - processingTime = null, - setupTime = null, - changeoverTime = null, - isRequired = false - ) + JobProcessStatusResponse( + jobOrderId = jobOrder?.id ?: 0L, + jobOrderCode = jobOrder?.code ?: "", + itemCode = process.item?.code ?: "", + itemName = process.item?.name ?: "", + status = process.status?.value ?: "", + planEndTime = calculatedPlanEndTime, + processes = (0 until 16).map { index -> + if (index < lines.size) { + val line = lines[index] + + // equipment.description + equipment_detail.name + val equipmentName = try { + line.bomProcess?.equipment?.name + } catch (_: jakarta.persistence.EntityNotFoundException) { + null + } + + + val equipmentDetailName = line.equipmentDetailId?.let { id -> + equipmentDetailRepository.findById(id).orElse(null)?.name + } + + ProcessStatusInfo( + processName = line.name, // ✅ 新增:工序名称 + equipmentName = equipmentName, // ✅ 替代 equipmentCode + equipmentDetailName = equipmentDetailName, // ✅ 新增 + startTime = line.startTime, + endTime = line.endTime, + processingTime = line.processingTime, + setupTime = line.setupTime, + changeoverTime = line.changeoverTime, + isRequired = true + ) + } else { + ProcessStatusInfo( + processName = null, + equipmentName = null, + equipmentDetailName = null, + startTime = null, + endTime = null, + processingTime = null, + setupTime = null, + changeoverTime = null, + isRequired = false + ) + } } - } - ) + ) + } } -} - private fun findNewCreatedLineIds(productProcessId: Long, bomId: Long): Set { + + 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") @@ -1951,105 +1993,288 @@ val sufficientStockQty = bomMaterials } } } - 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, - ) + // ===== Operator KPI Dashboard ===== + open fun getOperatorKpi(date: LocalDate?): List { + val targetDate = date ?: LocalDate.now() + val startOfDay = targetDate.atStartOfDay() + val endOfDay = targetDate.plusDays(1).atStartOfDay().minusSeconds(1) + + // 找出在該日期內有重疊的所有 line + val lines = productProcessLineRepository.findAllOverlappingWithDateRange(startOfDay, endOfDay) + + if (lines.isEmpty()) { + return emptyList() + } + + // 依 operator 分組,只保留有 operator 的 line + val linesByOperator = lines + .filter { it.operator?.id != null } + .groupBy { it.operator!!.id!! } + + return linesByOperator.map { (operatorId, operatorLines) -> + val operator = userRepository.findById(operatorId).orElse(null) + + // 將所有 interval 截斷到當天範圍內,並合併重疊區間 + val intervals = operatorLines.mapNotNull { line -> + val start = line.startTime ?: return@mapNotNull null + val rawEnd = line.endTime ?: LocalDateTime.now() + + val effectiveStart = if (start.isBefore(startOfDay)) startOfDay else start + val effectiveEnd = if (rawEnd.isAfter(endOfDay)) endOfDay else rawEnd + + if (effectiveEnd.isBefore(effectiveStart)) { + null + } else { + effectiveStart to effectiveEnd + } + }.sortedBy { it.first } + + var mergedTotalMinutes = 0L + var currentStart: LocalDateTime? = null + var currentEnd: LocalDateTime? = null + + for ((s, e) in intervals) { + if (currentStart == null) { + currentStart = s + currentEnd = e + } else if (!s.isAfter(currentEnd)) { + // 重疊或相鄰,擴展 end + if (e.isAfter(currentEnd)) { + currentEnd = e + } + } else { + // 不重疊,結束上一段 + mergedTotalMinutes += ChronoUnit.MINUTES.between(currentStart, currentEnd) + currentStart = s + currentEnd = e + } + } + if (currentStart != null && currentEnd != null) { + mergedTotalMinutes += ChronoUnit.MINUTES.between(currentStart, currentEnd) + } + + val currentProcesses = operatorLines + .filter { it.endTime == null || (it.status != null && it.status != "Completed" && it.status != "Pass") } + .map { line -> + val productProcess = line.productProcess + val jobOrder = productProcess.jobOrder + val bom = jobOrder?.bom + val equipmentDetail = line.equipmentDetailId?.let { id -> + equipmentDetailRepository.findByIdAndDeletedFalse(id) + } + + OperatorKpiProcessInfo( + jobOrderId = jobOrder?.id, + jobOrderCode = jobOrder?.code, + productProcessId = productProcess.id, + productProcessLineId = line.id, + processName = line.name, + equipmentName = line.equipmentType, + equipmentDetailName = equipmentDetail?.name, + startTime = line.startTime, + endTime = line.endTime, + processingTime = line.processingTime, + itemCode = bom?.item?.code, + itemName = bom?.item?.name, + + ) + } + val totalJobOrderCount = operatorLines + .mapNotNull { it.productProcess.jobOrder?.id } + .distinct() + .size + OperatorKpiResponse( + operatorId = operatorId, + operatorName = operator?.name, + staffNo = operator?.staffNo, + totalProcessingMinutes = mergedTotalMinutes, + totalJobOrderCount = totalJobOrderCount, + currentProcesses = currentProcesses + ) + }.sortedByDescending { it.totalProcessingMinutes } } - - // 检查是否是原始 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, - ) + + // ===== Equipment Status Dashboard ===== + open fun getEquipmentStatusByType(): List { + // 讀取所有未刪除的設備明細 + val details = equipmentDetailRepository.findAllByDeletedFalse() + if (details.isEmpty()) { + return emptyList() + } + + // 預先查詢所有處於進行中的 line,以減少 DB round-trip + val detailIdToLines: Map> = details + .mapNotNull { it.id } + .associateWith { detailId -> + productProcessLineRepository.findByEquipmentDetailIdAndDeletedFalseAndEndTimeIsNull(detailId) + } + + // 取得所有關聯的 equipmentTypeId -> equipment (爐類型) + val equipmentTypeIdSet = details.mapNotNull { it.equipmentTypeId }.toSet() + val equipmentsByTypeId: Map = equipmentRepository.findAll() + .filter { equipmentTypeIdSet.contains(it.id) } + .associateBy { it.id!! } + + val perDetail = details.mapNotNull { detail -> + val detailId = detail.id ?: return@mapNotNull null + val runningLines = detailIdToLines[detailId].orEmpty() + val activeLine = runningLines.maxByOrNull { it.startTime ?: LocalDateTime.MIN } + + val equipmentTypeId = detail.equipmentTypeId + val equipment = equipmentTypeId?.let { equipmentsByTypeId[it] } + + val (status, currentProcessInfo) = if (activeLine != null) { + val process = activeLine.productProcess + val jobOrder = process.jobOrder + val operator = activeLine.operator + + "Processing" to EquipmentStatusProcessInfo( + jobOrderId = jobOrder?.id, + jobOrderCode = jobOrder?.code, + productProcessId = process.id, + productProcessLineId = activeLine.id, + processName = activeLine.name, + operatorName = operator?.name, + startTime = activeLine.startTime, + processingTime = activeLine.processingTime + ) + } else { + val repairStatus = detail.repairAndMaintenanceStatus + val statusStr = if (repairStatus == true) "Repair" else "Idle" + statusStr to null + } + + EquipmentStatusPerDetail( + equipmentDetailId = detailId, + equipmentDetailCode = detail.code, + equipmentDetailName = detail.name, + equipmentId = equipment?.id, + equipmentTypeName = equipment?.name, + status = status, + repairAndMaintenanceStatus = detail.repairAndMaintenanceStatus, + latestRepairAndMaintenanceDate = detail.latestRepairAndMaintenanceDate, + lastRepairAndMaintenanceDate = detail.lastRepairAndMaintenanceDate, + repairAndMaintenanceRemarks = detail.repairAndMaintenanceRemarks, + currentProcess = currentProcessInfo + ) + } + + return perDetail + .groupBy { it.equipmentId ?: -1L } + .map { (equipmentId, list) -> + val equipment = equipmentId.takeIf { it != -1L }?.let { id -> + equipmentsByTypeId[id] + } + EquipmentStatusByTypeResponse( + equipmentTypeId = equipmentId, + equipmentTypeName = equipment?.name, + details = list.sortedBy { it.equipmentDetailCode } + ) + } + + } - - 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) + // 辅助函数:判断是否是原始创建的 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, + ) + } } - - return MessageResponse( - id = productProcessLineId, - code = "200", - name = "ProductProcess Line Deleted", - type = "success", - message = "ProductProcess Line deleted successfully", - errorPosition = null, - ) -} -} +