| @@ -35,7 +35,8 @@ interface JobOrderInfo { | |||
| // @get:Value("#{target.bom.item.itemUoms.^[salesUnit == true && deleted == false]?.uom}") | |||
| //// @get:Value("#{target.bom.item.itemUoms.^[salesUnit == true && deleted == false]?.uom.udfudesc}") | |||
| // val uom: UomConversion; | |||
| @get:Value("#{target.productProcess?.productionPriority}") | |||
| val productionPriority: Int?; | |||
| @get:Value("#{target.status.value}") | |||
| val status: String; | |||
| @@ -62,6 +63,7 @@ data class JobOrderInfoWithTypeName( | |||
| val insufficientCount: Int?, | |||
| val silHandlerId: Long?, | |||
| val planStart: LocalDateTime?, | |||
| val productionPriority: Int?, | |||
| val status: String, | |||
| val jobTypeId: Long?, | |||
| val jobTypeName: String? | |||
| @@ -3,8 +3,9 @@ package com.ffii.fpsms.modules.jobOrder.enums | |||
| enum class JobOrderStatus(val value: String) { | |||
| PLANNING("planning"), | |||
| PENDING("pending"), | |||
| PROCESSING("processing"), | |||
| PACKAGING("packaging"), | |||
| PROCESSING("processing"), | |||
| PENDING_QC("pendingQC"), | |||
| STORING("storing"), | |||
| COMPLETED("completed") | |||
| } | |||
| @@ -2050,34 +2050,96 @@ open fun getJobOrderLotsHierarchicalByPickOrderId(pickOrderId: Long): JobOrderLo | |||
| } | |||
| } | |||
| open fun updateHandledByForItem(pickOrderId: Long, itemId: Long, userId: Long): JoPickOrder? { | |||
| open fun updateHandledByForItem(pickOrderId: Long, itemId: Long, userId: Long): MessageResponse { | |||
| val joPickOrderOpt = joPickOrderRepository.findByPickOrderIdAndItemId(pickOrderId, itemId) | |||
| if (joPickOrderOpt.isEmpty) { | |||
| println("⚠️ JoPickOrder not found for pickOrderId: $pickOrderId, itemId: $itemId") | |||
| return null | |||
| return MessageResponse( | |||
| id = 0, | |||
| code = "404", | |||
| name = "Not Found", | |||
| type = "error", | |||
| message = "JoPickOrder not found", | |||
| errorPosition = "operator" | |||
| ) | |||
| } | |||
| val joPickOrder = joPickOrderOpt.get() | |||
| if (userId != null && joPickOrder.handledBy != null) { | |||
| val existingOperatorId = joPickOrder.handledBy | |||
| val newOperatorId = userId | |||
| // 如果不是同一个用户,拒绝更新 | |||
| if (existingOperatorId != null && existingOperatorId != newOperatorId) { | |||
| return MessageResponse( | |||
| id = joPickOrder.id, | |||
| code = "409", | |||
| name = "Conflict", | |||
| type = "error", | |||
| message = "This pick order is already assigned to another user", | |||
| errorPosition = "operator" | |||
| ) | |||
| } | |||
| } | |||
| joPickOrder.handledBy = userId | |||
| joPickOrderRepository.save(joPickOrder) | |||
| // Don't update other fields - only handledBy | |||
| return joPickOrderRepository.save(joPickOrder) | |||
| return MessageResponse( | |||
| id = joPickOrder.id, | |||
| code = null, | |||
| name = null, | |||
| type = null, | |||
| message = "Pick order handled by updated", | |||
| errorPosition = null | |||
| ) | |||
| } | |||
| open fun updateRecordHandledByForItem(pickOrderId: Long, itemId: Long, userId: Long): JoPickOrderRecord? { | |||
| open fun updateRecordHandledByForItem(pickOrderId: Long, itemId: Long, userId: Long): MessageResponse { | |||
| val joPickOrderRecordOpt = joPickOrderRecordRepository.findByPickOrderIdAndItemId(pickOrderId, itemId) | |||
| if (joPickOrderRecordOpt.isEmpty) { | |||
| println("⚠️ JoPickOrderRecord not found for pickOrderId: $pickOrderId, itemId: $itemId") | |||
| return null | |||
| return MessageResponse( | |||
| id = 0, | |||
| code = "404", | |||
| name = "Not Found", | |||
| type = "error", | |||
| message = "JoPickOrderRecord not found", | |||
| errorPosition = "operator" | |||
| ) | |||
| } | |||
| val joPickOrderRecord = joPickOrderRecordOpt.get() | |||
| if (userId != null && joPickOrderRecord.handledBy != null) { | |||
| val existingOperatorId = joPickOrderRecord.handledBy | |||
| val newOperatorId = userId | |||
| // 如果不是同一个用户,拒绝更新 | |||
| if (existingOperatorId != null && existingOperatorId != newOperatorId) { | |||
| return MessageResponse( | |||
| id = joPickOrderRecord.id, | |||
| code = "409", | |||
| name = "Conflict", | |||
| type = "error", | |||
| message = "This pick order is already assigned to another user", | |||
| errorPosition = "operator" | |||
| ) | |||
| } | |||
| } | |||
| joPickOrderRecord.handledBy = userId | |||
| joPickOrderRecordRepository.save(joPickOrderRecord) | |||
| // Don't update other fields - only handledBy | |||
| return joPickOrderRecordRepository.save(joPickOrderRecord) | |||
| return MessageResponse( | |||
| id = joPickOrderRecord.id, | |||
| code = null, | |||
| name = null, | |||
| type = null, | |||
| message = "Pick order record handled by updated", | |||
| errorPosition = null | |||
| ) | |||
| } | |||
| @Transactional(rollbackFor = [Exception::class]) | |||
| open fun deleteJoPickOrderJobOrderProductProcessPickOrder(jobOrderId: Long): MessageResponse { | |||
| @@ -27,8 +27,7 @@ import org.springframework.data.domain.PageRequest | |||
| import org.springframework.stereotype.Service | |||
| import org.springframework.transaction.annotation.Transactional | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDate | |||
| import java.time.LocalDateTime | |||
| import java.time.format.DateTimeFormatter | |||
| import kotlin.jvm.optionals.getOrNull | |||
| import com.ffii.fpsms.modules.jobOrder.entity.JobTypeRepository | |||
| @@ -62,8 +61,10 @@ import com.ffii.fpsms.modules.stock.service.QrContent | |||
| import kotlinx.serialization.encodeToString | |||
| import kotlinx.serialization.json.Json | |||
| import kotlin.math.exp | |||
| import com.ffii.fpsms.modules.productProcess.entity.ProductProcessRepository | |||
| import java.time.LocalDate | |||
| import java.time.LocalDateTime | |||
| @Service | |||
| open class JobOrderService( | |||
| val jobOrderRepository: JobOrderRepository, | |||
| @@ -81,7 +82,8 @@ open class JobOrderService( | |||
| private val printerService: PrinterService, | |||
| val jobTypeRepository: JobTypeRepository, | |||
| val inventoryRepository: InventoryRepository, | |||
| val stockInLineRepository: StockInLineRepository | |||
| val stockInLineRepository: StockInLineRepository, | |||
| val productProcessRepository: ProductProcessRepository | |||
| ) { | |||
| open fun allJobOrdersByPage(request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfo> { | |||
| @@ -176,6 +178,7 @@ open class JobOrderService( | |||
| insufficientCount = insufficientCount, | |||
| silHandlerId = info.silHandlerId, | |||
| planStart = info.planStart, | |||
| productionPriority = info.productionPriority, | |||
| status = info.status, | |||
| jobTypeId = info.jobTypeId, | |||
| jobTypeName = info.jobTypeId?.let { jobTypes[it]?.name } | |||
| @@ -192,7 +195,7 @@ open class JobOrderService( | |||
| val notCompletedPutaway = info.stockInLineStatus != "completed" | |||
| jobTypeNameMatch && notCompletedPutaway | |||
| } | |||
| }.sortedByDescending { it.productionPriority } | |||
| // 修复:使用 response.totalElements,这是过滤后的总数 | |||
| val total = response.totalElements | |||
| @@ -749,5 +752,55 @@ open class JobOrderService( | |||
| open fun getAllJobTypes(): List<JobTypeResponse> { | |||
| return jobTypeRepository.findAll().map { JobTypeResponse(it.id, it.name) } | |||
| } | |||
| open fun updateJoPlanStart(request: UpdateJoPlanStartRequest): MessageResponse { | |||
| val jo = request.id.let { jobOrderRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() | |||
| // 将 String 转换为 LocalDateTime | |||
| val planStartDateTime = if (request.planStart.isNotBlank()) { | |||
| try { | |||
| // 支持 ISO 格式: "YYYY-MM-DDTHH:mm:ss" 或 "YYYY-MM-DD" | |||
| if (request.planStart.contains("T")) { | |||
| LocalDateTime.parse(request.planStart) | |||
| } else { | |||
| // 如果只有日期,设置为当天的 00:00:00 | |||
| LocalDate.parse(request.planStart).atStartOfDay() | |||
| } | |||
| } catch (e: Exception) { | |||
| throw BadRequestException("Invalid date format: ${request.planStart}") | |||
| } | |||
| } else { | |||
| null | |||
| } | |||
| jo.apply { | |||
| planStart = planStartDateTime | |||
| } | |||
| jobOrderRepository.save(jo) | |||
| val planStartDate = planStartDateTime?.toLocalDate() | |||
| val productProcesses = productProcessRepository.findByJobOrder_Id(jo.id) | |||
| productProcesses.forEach { productProcess -> | |||
| productProcess.apply { | |||
| date = planStartDate | |||
| } | |||
| productProcessRepository.save(productProcess) | |||
| } | |||
| return MessageResponse( | |||
| id = jo.id, | |||
| code = jo.code, | |||
| name = jo.bom?.name, | |||
| type = null, | |||
| message = null, | |||
| errorPosition = null, | |||
| entity = mapOf("status" to jo.status?.value) | |||
| ) | |||
| } | |||
| /* | |||
| open fun checkJobOrderCreated(request: CheckJobOrderCreatedRequest): CheckJobOrderCreatedResponse { | |||
| val jobOrders = jobOrderRepository.findByBomIdAndDate(request.bomId, request.date) | |||
| return CheckJobOrderCreatedResponse( | |||
| isCreated = jobOrders.isNotEmpty(), | |||
| jobOrders = jobOrders.map { JobOrderBasicInfoResponse(it.id, it.code, it.bom?.name, it.reqQty) } | |||
| ) | |||
| } | |||
| */ | |||
| } | |||
| @@ -99,7 +99,7 @@ class JobOrderController( | |||
| } | |||
| jobOrderBomMaterialService.createJobOrderBomMaterialsByJoId(jo.id) | |||
| jobOrderProcessService.createJobOrderProcessesByJoId(jo.id) | |||
| productProcessService.createProductProcessByJobOrderId(jo.id) | |||
| productProcessService.createProductProcessByJobOrderId(jo.id, request.productionPriority) | |||
| return jo | |||
| } | |||
| @GetMapping("/all-lots-hierarchical/{userId}") | |||
| @@ -306,4 +306,14 @@ fun updateJoPickOrderHandledBy(@Valid @RequestBody request: UpdateJoPickOrderHan | |||
| fun deleteJoPickOrderJobOrderProductProcessPickOrder(@PathVariable jobOrderId: Long): MessageResponse { | |||
| return joPickOrderService.deleteJoPickOrderJobOrderProductProcessPickOrder(jobOrderId) | |||
| } | |||
| @PostMapping("/update-jo-plan-start") | |||
| fun updateJoPlanStart(@Valid @RequestBody request: UpdateJoPlanStartRequest): MessageResponse { | |||
| return jobOrderService.updateJoPlanStart(request) | |||
| } | |||
| /* | |||
| @PostMapping("/checkJobOrderCreated") | |||
| fun checkJobOrderCreated(@Valid @RequestBody request: CheckJobOrderCreatedRequest): Boolean { | |||
| return jobOrderService.checkJobOrderCreated(request) | |||
| } | |||
| */ | |||
| } | |||
| @@ -3,7 +3,7 @@ package com.ffii.fpsms.modules.jobOrder.web.model | |||
| import com.ffii.fpsms.modules.jobOrder.enums.JobOrderBomMaterialStatus | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDateTime | |||
| import java.time.LocalDate | |||
| data class CreateJobOrderRequest ( | |||
| val bomId: Long?, | |||
| val planStart: LocalDateTime? = null, | |||
| @@ -15,6 +15,7 @@ data class CreateJobOrderRequest ( | |||
| val approverId: Long? = null, | |||
| val prodScheduleLineId: Long? = null, | |||
| val status: String = "planning", | |||
| val productionPriority: Int?=50, | |||
| ) | |||
| data class CreateJobOrderBomMaterialRequest ( | |||
| @@ -130,4 +131,22 @@ data class JobOrderListForPrintQrCodeResponse( | |||
| val stockInLineQty: Double, | |||
| val stockInLineStatus: String, | |||
| val finihedTime: LocalDateTime, | |||
| ) | |||
| data class UpdateJoPlanStartRequest( | |||
| val id: Long, | |||
| val planStart: String, | |||
| ) | |||
| data class CheckJobOrderCreatedRequest( | |||
| val bomId: Long, | |||
| val date: LocalDate, | |||
| ) | |||
| data class CheckJobOrderCreatedResponse( | |||
| val isCreated: Boolean, | |||
| val jobOrders: List<JobOrderInfoResponse>, | |||
| ) | |||
| data class JobOrderInfoResponse( | |||
| val jobOrderId: Long, | |||
| val code: String, | |||
| val itemName: String, | |||
| val reqQty: BigDecimal, | |||
| ) | |||
| @@ -10,9 +10,9 @@ data class ProductProcessInfo( | |||
| val id: Long?, | |||
| val productProcessCode: String?, | |||
| val status: ProductProcessStatus?, | |||
| @JsonFormat(pattern = "yyyy-MM-dd") | |||
| @JsonFormat(pattern = "MM-dd HH:mm") | |||
| val startTime: LocalDateTime?, | |||
| @JsonFormat(pattern = "yyyy-MM-dd") | |||
| @JsonFormat(pattern = "MM-dd HH:mm") | |||
| val endTime: LocalDateTime?, | |||
| @JsonFormat(pattern = "yyyy-MM-dd") | |||
| val date: LocalDate?, | |||
| @@ -23,6 +23,7 @@ data class ProductProcessInfo( | |||
| val jobOrderCode: String?, | |||
| val jobOrderStatus: String?, | |||
| val jobType: String?, | |||
| val bomDescription: String?, | |||
| val isDark: String?, | |||
| val isDense: Int?, | |||
| val isFloat: String?, | |||
| @@ -685,7 +685,7 @@ open class ProductProcessService( | |||
| } | |||
| } | |||
| open fun createProductProcessByJobOrderId(jobOrderId: Long): 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) } | |||
| @@ -160,9 +160,9 @@ class ProductProcessController( | |||
| fun demojoid(@PathVariable joid: Long): List<ProductProcessInfo> { | |||
| return productProcessService.productProcessDetailfindbyjoid(joid) | |||
| } | |||
| @PostMapping("/demo/joid/{joid}") | |||
| fun democreate(@PathVariable joid: Long): MessageResponse { | |||
| return productProcessService.createProductProcessByJobOrderId(joid) | |||
| @PostMapping("/demo/joid/{joid}/{productionPriority}") | |||
| fun democreate(@PathVariable joid: Long, @PathVariable productionPriority: Int): MessageResponse { | |||
| return productProcessService.createProductProcessByJobOrderId(joid, productionPriority) | |||
| } | |||
| @PostMapping("/Demo/update") | |||
| fun demoupdate(@RequestBody request: UpdateProductProcessLineOperatorIdOrEquipmentIdRequest): MessageResponse { | |||
| @@ -213,4 +213,8 @@ class ProductProcessController( | |||
| fun updateProductProcessPriority(@PathVariable productProcessId: Long, @PathVariable productionPriority: Int): MessageResponse { | |||
| return productProcessService.UpdateProductProcessPriority(productProcessId, productionPriority) | |||
| } | |||
| @PostMapping("/Demo/ProcessLine/pass/{lineId}") | |||
| fun passProductProcessLine(@PathVariable lineId: Long): MessageResponse { | |||
| return productProcessService.passProductProcessLine(lineId) | |||
| } | |||
| } | |||
| @@ -169,13 +169,15 @@ data class AllJoborderProductProcessInfoResponse( | |||
| val RequiredQty: Int?, | |||
| val Uom: String?, | |||
| val jobOrderId: Long?, | |||
| val productionPriority: Int?, | |||
| val jobOrderCode: String?, | |||
| val assignedTo: Long?, | |||
| val pickOrderId: Long?, | |||
| val pickOrderStatus: String?, | |||
| val productProcessLineCount: Int, | |||
| val FinishedProductProcessLineCount: Int, | |||
| val stockInLineId: Long?, | |||
| val stockInLineId: Long?, | |||
| val TimeNeedToComplete: Int?, | |||
| val lines: List<ProductProcessInfoResponse> | |||
| ) | |||
| data class ProductProcessInfoResponse( | |||
| @@ -0,0 +1,7 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset Enson:add_column | |||
| ALTER TABLE `fpsmsdb`.`bag_lot_line` | |||
| CHANGE COLUMN `firstUseDate` `firstUseDate` DATETIME NULL , | |||
| CHANGE COLUMN `lastUseDate` `lastUseDate` DATETIME NULL , | |||
| ADD COLUMN `stockOutLineId` BIGINT NULL AFTER `bagId`; | |||
| @@ -0,0 +1,5 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset Enson:add_column | |||
| ALTER TABLE `fpsmsdb`.`jo_bag_consumption` | |||
| ADD COLUMN `stockOutLineId` BIGINT NULL AFTER `jobOrderCode`; | |||
| @@ -0,0 +1,5 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset Enson:add_column | |||
| ALTER TABLE `fpsmsdb`.`productprocess` | |||
| ADD COLUMN `submitedBagRecord`TINYINT(1) NULL DEFAULT 0 AFTER `productionPriority`; | |||