From f6016887ab2ed0091c263f93110b286c2baf3754 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Mon, 1 Dec 2025 16:08:27 +0800 Subject: [PATCH] update --- .../fpsms/modules/jobOrder/entity/JobOrder.kt | 2 + .../jobOrder/entity/JobOrderTypeRepository.kt | 1 + .../entity/projections/JobOrderInfo.kt | 26 +- .../jobOrder/service/JoPickOrderService.kt | 325 +++++++++++++++++- .../jobOrder/service/JobOrderService.kt | 60 +++- .../jobOrder/web/JobOrderController.kt | 71 +++- .../jobOrder/web/JobOrderProcessController.kt | 24 -- .../web/model/CreateJobOrderRequest.kt | 34 ++ .../web/model/SearchJobOrderInfoRequest.kt | 1 + .../entity/EquipmentDetailRepository.kt | 2 +- .../pickOrder/entity/PickOrderRepository.kt | 3 + .../entity/ProductProcessLine.kt | 2 + .../entity/projections/ProductProcessInfo.kt | 1 + .../service/ProductProcessService.kt | 60 +++- .../web/model/SaveProductProcessRequest.kt | 8 +- .../ffii/fpsms/modules/user/entity/User.java | 23 +- .../modules/user/entity/UserRepository.java | 2 + .../20251130_01_enson/01_altertable_enson.sql | 6 + 18 files changed, 609 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderProcessController.kt create mode 100644 src/main/resources/db/changelog/changes/20251130_01_enson/01_altertable_enson.sql diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt index 2ca4ea7..ceb269b 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt @@ -80,4 +80,6 @@ open class JobOrder : BaseEntity() { @Column(name = "jobTypeId") open var jobTypeId: Long? = null + + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderTypeRepository.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderTypeRepository.kt index 5a1bd05..ac6804a 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderTypeRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderTypeRepository.kt @@ -16,4 +16,5 @@ import java.util.Optional interface JobTypeRepository : JpaRepository { //fun findByName(name: String): Optional //fun findByIdAndDeletedIsFalse(id: Long): Optional + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt index 2504341..9ea0570 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt @@ -38,8 +38,32 @@ interface JobOrderInfo { @get:Value("#{target.status.value}") val status: String; -} + @get:Value("#{target.jobTypeId}") + val jobTypeId: Long?; + + +} +data class JobTypeResponse( + val id: Long?, + val name: String? +) +data class JobOrderInfoWithTypeName( + val id: Long, + val code: String, + val itemCode: String, + val itemName: String, + val name: String, + val reqQty: BigDecimal, + val item: JobOrderItemInfo, + val stockInLineId: Long?, + val stockInLineStatus: String?, + val silHandlerId: Long?, + val planStart: LocalDateTime?, + val status: String, + val jobTypeId: Long?, + val jobTypeName: String? +) // Job Order interface JobOrderDetailWithJsonString { val id: Long?; diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt index ccec25d..5972e17 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt @@ -14,6 +14,7 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDate import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus +import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus import java.time.LocalDateTime import com.ffii.core.support.JdbcDao import com.ffii.fpsms.modules.jobOrder.web.model.SecondScanSubmitRequest @@ -29,6 +30,10 @@ import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository +import com.ffii.fpsms.modules.jobOrder.web.model.AllJoPickOrderResponse +import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository +import com.ffii.fpsms.modules.jobOrder.entity.JobTypeRepository +import com.ffii.fpsms.modules.master.entity.ItemsRepository @Service open class JoPickOrderService( private val joPickOrderRepository: JoPickOrderRepository, @@ -42,7 +47,11 @@ open class JoPickOrderService( private val suggestPickLotRepository: SuggestPickLotRepository, private val inventoryLotLineRepository: InventoryLotLineRepository, private val inventoryLotRepository: InventoryLotRepository, - private val stockOutLineRepository: StockOutLIneRepository + private val stockOutLineRepository: StockOutLIneRepository, + private val jobOrderRepository: JobOrderRepository, + private val jobTypeRepository: JobTypeRepository, + private val itemsRepository: ItemsRepository + ) { open fun save(record: JoPickOrder): JoPickOrder { @@ -1629,4 +1638,318 @@ open fun getCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId: Lo emptyList() } } + + +open fun getAllJoPickOrders(): List { + println("=== getAllJoPickOrders ===") + + return try { + val releasedPickOrders = pickOrderRepository.findAllByStatusAndDeletedFalse( + PickOrderStatus.RELEASED + ).filter { pickOrder -> + pickOrder.jobOrder != null + } + + println("Found ${releasedPickOrders.size} released job order pick orders") + + val jobOrderPickOrders = releasedPickOrders.mapNotNull { pickOrder -> + println("Processing pick order: ${pickOrder.id}, code: ${pickOrder.code}") + val jobOrder = pickOrder.jobOrder + if (jobOrder == null) { + println("❌ Pick order ${pickOrder.id} has no job order") + return@mapNotNull null + } + + println("Job order found: ${jobOrder.id}, code: ${jobOrder.code}") + + val bom = jobOrder.bom + + + println("BOM found: ${bom?.id}") + + val item = bom?.item + if (item == null) { + println("❌ BOM ${bom?.id} has no item") + return@mapNotNull null + } + + println("Item found: ${item.id}, name: ${item.name}") + + val uom = bom.outputQtyUom + if (uom == null) { + println("❌ BOM ${bom.id} has no uom") + return@mapNotNull null + } + + // println("UOM found: ${uom.id}, code: ${uom.code}") + + val pickOrderLines = pickOrderLineRepository.findAllByPickOrderId(pickOrder.id ?: return@mapNotNull null) + val finishedLines = pickOrderLines.count { it.status == PickOrderLineStatus.COMPLETED } + val jobOrderType = jobOrder.jobTypeId?.let { jobTypeRepository.findById(it).orElse(null) } + + println("✅ Building response for pick order ${pickOrder.id}") + + AllJoPickOrderResponse( + id = pickOrder.id ?: 0L, + pickOrderId = pickOrder.id, + pickOrderCode = pickOrder.code, + jobOrderId = jobOrder.id, + jobOrderCode = jobOrder.code, + jobOrderTypeId = jobOrder.jobTypeId, + jobOrderType = jobOrderType?.name, + itemId = item.id ?: 0L, + itemName = item.name ?: "", + reqQty = jobOrder.reqQty ?: BigDecimal.ZERO, + //uomId = bom.outputQtyUom?.id : 0L, + uomId = 0, + uomName = bom?.outputQtyUom?: "", + jobOrderStatus = jobOrder.status?.value ?: "", + finishedPickOLineCount = finishedLines + ) + } + + println("Returning ${jobOrderPickOrders.size} released job order pick orders") + jobOrderPickOrders + } catch (e: Exception) { + println("❌ Error in getAllJoPickOrders: ${e.message}") + e.printStackTrace() + emptyList() + } +} +open fun getJobOrderLotsHierarchicalByPickOrderId(pickOrderId: Long): Map { + println("=== getJobOrderLotsHierarchicalByPickOrderId ===") + println("pickOrderId: $pickOrderId") + + return try { + val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) + if (pickOrder == null || pickOrder.deleted == true) { + println("❌ Pick order $pickOrderId not found or deleted") + return mapOf( + "pickOrder" to null as Any?, + "pickOrderLines" to emptyList>() as Any? + ) + } + + val jobOrder = pickOrder.jobOrder ?: return mapOf( + "pickOrder" to null as Any?, + "pickOrderLines" to emptyList>() as Any? + ) + + // 获取 pick order lines + val pickOrderLines = pickOrderLineRepository.findAllByPickOrderId(pickOrder.id!!) + .filter { it.deleted == false } + + // 获取所有 pick order line IDs + val pickOrderLineIds = pickOrderLines.map { it.id!! } + + // 获取 suggested pick lots + val suggestedPickLots = if (pickOrderLineIds.isNotEmpty()) { + suggestPickLotRepository.findAllByPickOrderLineIdIn(pickOrderLineIds) + .filter { it.deleted == false } + } else { + emptyList() + } + + // 获取所有 inventory lot line IDs + val inventoryLotLineIds = suggestedPickLots.mapNotNull { it.suggestedLotLine?.id } + + // 获取 inventory lot lines + val inventoryLotLines = if (inventoryLotLineIds.isNotEmpty()) { + inventoryLotLineRepository.findAllByIdIn(inventoryLotLineIds) + .filter { it.deleted == false } + } else { + emptyList() + } + + // 获取 inventory lots + val inventoryLotIds = inventoryLotLines.mapNotNull { it.inventoryLot?.id }.distinct() + val inventoryLots = if (inventoryLotIds.isNotEmpty()) { + inventoryLotRepository.findAllByIdIn(inventoryLotIds) + .filter { it.deleted == false } + } else { + emptyList() + } + + // 获取 stock out lines + val stockOutLines = if (pickOrderLineIds.isNotEmpty() && inventoryLotLineIds.isNotEmpty()) { + pickOrderLineIds.flatMap { polId -> + inventoryLotLineIds.flatMap { illId -> + stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(polId, illId) + } + } + } else { + emptyList() + } + + // 取得所有 stock out line(含無 lot 情況) + val stockOutLinesByPickOrderLine = pickOrderLineIds.associateWith { polId -> + stockOutLineRepository.findAllByPickOrderLineIdAndDeletedFalse(polId) + } + + // 获取 jo_pick_order 记录 + val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrder.id!!) + + // 构建 pick order info + val pickOrderInfo = mapOf( + "id" to pickOrder.id, + "code" to pickOrder.code, + "consoCode" to pickOrder.consoCode, + "targetDate" to pickOrder.targetDate?.let { + "${it.year}-${String.format("%02d", it.monthValue)}-${String.format("%02d", it.dayOfMonth)}" + }, + "type" to pickOrder.type?.value, + "status" to pickOrder.status?.value, + "assignTo" to pickOrder.assignTo?.id, + "jobOrder" to mapOf( + "id" to jobOrder.id, + "code" to jobOrder.code, + "name" to "Job Order ${jobOrder.code}" + ) + ) + + // 构建 pick order lines with lots + val pickOrderLinesResult = pickOrderLines.map { pol -> + val item = pol.item + val uom = pol.uom + val lineId = pol.id!! + val suggestions = suggestedPickLots.filter { it.pickOrderLine?.id == lineId } + val stockoutsForLine = stockOutLinesByPickOrderLine[lineId].orEmpty() + // 获取该 line 的 suggested pick lots + val lineSuggestedLots = suggestedPickLots.filter { it.pickOrderLine?.id == pol.id } + + // 构建 lots 数据 + val lots = lineSuggestedLots.mapNotNull { spl -> + val ill = spl.suggestedLotLine + if (ill == null || ill.deleted == true) return@mapNotNull null + + val il = ill.inventoryLot + if (il == null || il.deleted == true) return@mapNotNull null + + val warehouse = ill.warehouse + + // 获取对应的 stock out line + val sol = stockOutLines.firstOrNull { + it.pickOrderLine?.id == pol.id && it.inventoryLotLine?.id == ill.id + } + + // 获取对应的 jo_pick_order + val jpo = joPickOrders.firstOrNull { it.itemId == item?.id } + + // 计算 available quantity + val availableQty = if (sol?.status == "rejected") { + null + } else { + (ill.inQty ?: BigDecimal.ZERO) - (ill.outQty ?: BigDecimal.ZERO) - (ill.holdQty ?: BigDecimal.ZERO) + } + + // 计算 total picked by all pick orders + val totalPickedByAllPickOrders = stockOutLines + .filter { it.inventoryLotLine?.id == ill.id && it.deleted == false } + .filter { it.status in listOf("pending", "checked", "partially_completed", "completed") } + .sumOf { it.qty?.toBigDecimal() ?: BigDecimal.ZERO } + + // 计算 lot availability + val lotAvailability = when { + il.expiryDate != null && il.expiryDate!!.isBefore(LocalDate.now()) -> "expired" + sol?.status == "rejected" -> "rejected" + availableQty != null && availableQty <= BigDecimal.ZERO -> "insufficient_stock" + ill.status == InventoryLotLineStatus.UNAVAILABLE -> "status_unavailable" + else -> "available" + } + + // 计算 processing status + val processingStatus = when (sol?.status) { + "completed" -> "completed" + "rejected" -> "rejected" + "created" -> "pending" + else -> "pending" + } + + mapOf( + "lotId" to ill.id, + "lotNo" to il.lotNo, + "expiryDate" to il.expiryDate?.let { + "${it.year}-${String.format("%02d", it.monthValue)}-${String.format("%02d", it.dayOfMonth)}" + }, + "location" to warehouse?.name, + "availableQty" to availableQty?.toDouble(), + "requiredQty" to (spl.qty?.toDouble() ?: 0.0), + "actualPickQty" to (sol?.qty ?: 0.0), + "processingStatus" to processingStatus, + "lotAvailability" to lotAvailability, + "pickOrderId" to pickOrder.id, + "pickOrderCode" to pickOrder.code, + "pickOrderConsoCode" to pickOrder.consoCode, + "pickOrderLineId" to pol.id, + "stockOutLineId" to sol?.id, + "suggestedPickLotId" to spl.id, + "stockOutLineQty" to (sol?.qty ?: 0.0), + "stockOutLineStatus" to sol?.status, + "routerIndex" to warehouse?.order, + "routerArea" to warehouse?.code, + "routerRoute" to warehouse?.code, + "uomShortDesc" to uom?.udfShortDesc, + "matchStatus" to jpo?.matchStatus?.value, + "matchBy" to jpo?.matchBy, + "matchQty" to jpo?.matchQty + ) + } + + mapOf( + "id" to pol.id, + "itemId" to item?.id, + "itemCode" to item?.code, + "itemName" to item?.name, + "requiredQty" to pol.qty?.toDouble(), + "uomCode" to uom?.code, + "uomDesc" to uom?.udfudesc, + "lots" to lots + ) + } + + return mapOf( + "pickOrder" to pickOrderInfo as Any?, + "pickOrderLines" to pickOrderLinesResult as Any? + ) + + } catch (e: Exception) { + println("❌ Error in getJobOrderLotsHierarchicalByPickOrderId: ${e.message}") + e.printStackTrace() + return mapOf( + "pickOrder" to null as Any?, + "pickOrderLines" to emptyList>() as Any? + ) + } +} + +open fun updateHandledByForItem(pickOrderId: Long, itemId: Long, userId: Long): JoPickOrder? { + val joPickOrderOpt = joPickOrderRepository.findByPickOrderIdAndItemId(pickOrderId, itemId) + + if (joPickOrderOpt.isEmpty) { + println("⚠️ JoPickOrder not found for pickOrderId: $pickOrderId, itemId: $itemId") + return null + } + + val joPickOrder = joPickOrderOpt.get() + joPickOrder.handledBy = userId + // Don't update other fields - only handledBy + + return joPickOrderRepository.save(joPickOrder) +} + +open fun updateRecordHandledByForItem(pickOrderId: Long, itemId: Long, userId: Long): JoPickOrderRecord? { + val joPickOrderRecordOpt = joPickOrderRecordRepository.findByPickOrderIdAndItemId(pickOrderId, itemId) + + if (joPickOrderRecordOpt.isEmpty) { + println("⚠️ JoPickOrderRecord not found for pickOrderId: $pickOrderId, itemId: $itemId") + return null + } + + val joPickOrderRecord = joPickOrderRecordOpt.get() + joPickOrderRecord.handledBy = userId + // Don't update other fields - only handledBy + + return joPickOrderRecordRepository.save(joPickOrderRecord) +} + } diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt index c718adf..337c269 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt @@ -54,6 +54,8 @@ import org.springframework.core.io.ClassPathResource import java.io.File import java.io.FileNotFoundException import java.io.IOException +import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfoWithTypeName +import com.ffii.fpsms.modules.jobOrder.entity.projections.JobTypeResponse @Service @@ -82,7 +84,7 @@ open class JobOrderService( code = request.code ?: "", bomName = request.itemName ?: "", pageable = pageable - ) + ) val planStartFrom = request.planStart val planStartTo = request.planStartTo @@ -97,7 +99,59 @@ open class JobOrderService( val total = response.totalElements return RecordsRes(records, total.toInt()); } + open fun allJobOrdersByPageWithTypeName(request: SearchJobOrderInfoRequest): RecordsRes { + val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10); + println("allJobOrdersByPage") + println(request) + val response = jobOrderRepository.findJobOrderInfoByCodeContainsAndBomNameContainsAndDeletedIsFalseOrderByIdDesc( + code = request.code ?: "", + bomName = request.itemName ?: "", + pageable = pageable + ) + + val jobTypeIds = response.content.mapNotNull { it.jobTypeId }.distinct() + val jobTypes = if (jobTypeIds.isNotEmpty()) { + jobTypeRepository.findAllById(jobTypeIds).associateBy { it.id } + } else { + emptyMap() + } + + val planStartFrom = request.planStart + val planStartTo = request.planStartTo + val records = response.content + .filter { + (planStartFrom == null || (it.planStart != null && (planStartFrom.isEqual(it.planStart) || planStartFrom.isBefore(it.planStart)))) && + (planStartTo == null || (it.planStart != null && (planStartTo.isEqual(it.planStart) || planStartTo.isAfter(it.planStart)))) + } + .map { info -> + JobOrderInfoWithTypeName( + id = info.id, + code = info.code, + itemCode = info.itemCode, + itemName = info.itemName, + name = info.name, + reqQty = info.reqQty, + item = info.item, + stockInLineId = info.stockInLineId, + stockInLineStatus = info.stockInLineStatus, + silHandlerId = info.silHandlerId, + planStart = info.planStart, + status = info.status, + jobTypeId = info.jobTypeId, + jobTypeName = info.jobTypeId?.let { jobTypes[it]?.name } + ) + } + .filter { info -> + // Filter by jobTypeName if provided + request.jobTypeName == null || + request.jobTypeName.isBlank() || + info.jobTypeName?.equals(request.jobTypeName, ignoreCase = true) == true || + info.jobTypeName?.contains(request.jobTypeName, ignoreCase = true) == true + } + val total = response.totalElements + return RecordsRes(records, total.toInt()); + } open fun jobOrderDetail(id: Long): JobOrderDetail { val sqlResult = jobOrderRepository.findJobOrderDetailById(id) ?: throw NoSuchElementException(); @@ -594,6 +648,8 @@ open class JobOrderService( //tempPdfFile.delete } } - + open fun getAllJobTypes(): List { + return jobTypeRepository.findAll().map { JobTypeResponse(it.id, it.name) } + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt index f608557..ce4d9aa 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt @@ -2,7 +2,7 @@ package com.ffii.fpsms.modules.jobOrder.web import com.ffii.core.response.RecordsRes import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail -import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo + import com.ffii.fpsms.modules.jobOrder.service.JobOrderBomMaterialService import com.ffii.fpsms.modules.jobOrder.service.JobOrderProcessService import com.ffii.fpsms.modules.jobOrder.service.JobOrderService @@ -10,6 +10,7 @@ import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderCommonActionRequest import com.ffii.fpsms.modules.jobOrder.web.model.SearchJobOrderInfoRequest import com.ffii.fpsms.modules.master.web.models.MessageResponse +import com.ffii.fpsms.modules.jobOrder.entity.projections.JobTypeResponse import jakarta.validation.Valid import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.ModelAttribute @@ -35,6 +36,9 @@ import java.io.UnsupportedEncodingException import java.text.ParseException import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.RequestParam +import com.ffii.fpsms.modules.jobOrder.web.model.UpdateJoPickOrderHandledByRequest +import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo +import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfoWithTypeName @RestController @RequestMapping("/jo") class JobOrderController( @@ -46,10 +50,11 @@ class JobOrderController( ) { @GetMapping("/getRecordByPage") - fun allJobOrdersByPage(@ModelAttribute request: SearchJobOrderInfoRequest): RecordsRes { + fun allJobOrdersByPage(@ModelAttribute request: SearchJobOrderInfoRequest): RecordsRes { println("getRecordByPage") println(request) - return jobOrderService.allJobOrdersByPage(request); + //return jobOrderService.allJobOrdersByPage(request); + return jobOrderService.allJobOrdersByPageWithTypeName(request); } @GetMapping("/detail/{id}") @@ -224,4 +229,64 @@ fun recordSecondScanIssue( fun getCompletedJobOrderPickOrderLotDetailsForCompletedPick(@PathVariable pickOrderId: Long): List> { return joPickOrderService.getCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId) } + @GetMapping("/AllJoPickOrder") + fun getAllJoPickOrder(): List { + return joPickOrderService.getAllJoPickOrders() + } + + @GetMapping("/all-lots-hierarchical-by-pick-order/{pickOrderId}") + fun getJobOrderLotsHierarchicalByPickOrderId(@PathVariable pickOrderId: Long): Map { + return joPickOrderService.getJobOrderLotsHierarchicalByPickOrderId(pickOrderId) + } + @PostMapping("/update-jo-pick-order-handled-by") +fun updateJoPickOrderHandledBy(@Valid @RequestBody request: UpdateJoPickOrderHandledByRequest): MessageResponse { + try { + val joPickOrder = joPickOrderService.updateHandledByForItem( + request.pickOrderId, + request.itemId, + request.userId + ) + + val joPickOrderRecord = joPickOrderService.updateRecordHandledByForItem( + request.pickOrderId, + request.itemId, + request.userId + ) + + if (joPickOrder == null) { + return MessageResponse( + id = null, + code = "NOT_FOUND", + name = null, + type = null, + message = "JoPickOrder not found for pickOrderId: ${request.pickOrderId}, itemId: ${request.itemId}", + errorPosition = null + ) + } + + return MessageResponse( + id = joPickOrder.id, + code = "SUCCESS", + name = null, + type = null, + message = "JoPickOrder handledBy updated successfully for item ${request.itemId}", + errorPosition = null + ) + } catch (e: Exception) { + println("Error updating JoPickOrder handledBy: ${e.message}") + e.printStackTrace() + return MessageResponse( + id = null, + code = "ERROR", + name = null, + type = null, + message = "Failed to update JoPickOrder handledBy: ${e.message}", + errorPosition = null + ) + } +} +@GetMapping("/jobTypes") + fun getAllJobTypes(): List { + return jobOrderService.getAllJobTypes() + } } diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderProcessController.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderProcessController.kt deleted file mode 100644 index b972819..0000000 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderProcessController.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.ffii.fpsms.modules.jobOrder.web - -import OperatorRequest -import com.ffii.core.response.RecordsRes -import com.ffii.core.utils.CriteriaArgsBuilder -import com.ffii.fpsms.modules.jobOrder.service.JobOrderProcessService -import com.ffii.fpsms.modules.master.web.models.MessageResponse -import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderDataClass -import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderService -import com.ffii.fpsms.modules.purchaseOrder.web.model.PagingRequest -import jakarta.servlet.http.HttpServletRequest -import org.springframework.web.bind.annotation.* - -@RestController -@RequestMapping("/jop") -class JobOrderProcessController( - private val jobOrderProcessService: JobOrderProcessService -) { - @PostMapping("/isOperatorExist") - fun checkPolAndCompletePo(@RequestBody request: OperatorRequest): MessageResponse { - println(request) - return jobOrderProcessService.isOperatorExist(request) - } -} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt index 92db5be..8e10c10 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt @@ -31,4 +31,38 @@ data class CreateJobOrderProcessRequest ( val seqNo: Long?, val remarks: String? = null, val status: String = "pending", +) + + +data class AllJoPickOrderResponse( + val id: Long, + val pickOrderId: Long?, + val pickOrderCode: String?, + val jobOrderId: Long?, + val jobOrderCode: String?, + val jobOrderTypeId: Long?, + val jobOrderType: String?, + val itemId: Long, + val itemName: String, + val reqQty: BigDecimal, + val uomId: Long, + val uomName: String, + val jobOrderStatus: String, + val finishedPickOLineCount: Int, + +) +data class JobOrderLotsHierarchicalResponse( + val id: Long, + val lotId: Long?, + val lotCode: String?, + val lotName: String?, + val lotQty: BigDecimal?, + val lotUomId: Long?, + val lotUomName: String?, +) + +data class UpdateJoPickOrderHandledByRequest( + val pickOrderId: Long, + val itemId: Long, + val userId: Long ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt index 6bdb75e..d3b1108 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt @@ -9,4 +9,5 @@ data class SearchJobOrderInfoRequest( val planStartTo: LocalDateTime?, val pageSize: Int?, val pageNum: Int?, + val jobTypeName: String?, ) diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetailRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetailRepository.kt index 88ad853..52ccb92 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetailRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetailRepository.kt @@ -11,6 +11,6 @@ interface EquipmentDetailRepository : AbstractRepository fun findByNameAndDeletedIsFalse(name: String): EquipmentDetail?; fun findByDescriptionAndDeletedIsFalse(description: String): EquipmentDetail?; - + fun findByEquipmentTypeIdAndDeletedFalse(equipmentTypeId: Long): List; } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt index 196335a..74d0509 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt @@ -78,4 +78,7 @@ interface PickOrderRepository : AbstractRepository { // 在 PickOrderRepository 中添加: fun findAllByStatusAndAssignToIsNullAndDeletedFalse(status: PickOrderStatus): List +//fun findAllByJoid(jobOrderId: Long): List +fun findAllByJobOrder_Id(jobOrderId: Long): List +fun findTopByJobOrder_IdOrderByCreatedDesc(jobOrderId: Long): PickOrder? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt index 0a75720..d277cd8 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt @@ -26,6 +26,8 @@ open class ProductProcessLine : BaseEntity() { @Column(name = "equipmentDetailId") open var equipmentDetailId: Long? = null + + @Size(max = 100) @Column(name = "name", length = 100) open var name: String? = null diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt index 604bd99..c202140 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt @@ -52,6 +52,7 @@ data class ProductProcessLineInfo( val name: String?, val description: String?, val equipment_name: String?, + val equipmentDetailCode: String?, val status: String?, val byproductId: Long?, val byproductName: String?, 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 35456ec..2f4a4e8 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 @@ -7,6 +7,7 @@ import com.ffii.fpsms.modules.productProcess.enums.ProductProcessStatus import com.ffii.fpsms.modules.productProcess.web.model.* import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable +import com.ffii.fpsms.modules.master.entity.EquipmentDetailRepository import com.ffii.fpsms.modules.productProcess.entity.projections.jobOrderLineInfo import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -38,7 +39,7 @@ import com.ffii.fpsms.modules.stock.service.StockInLineService import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest import com.ffii.fpsms.modules.master.entity.BomProcessMaterialRepository import com.ffii.fpsms.modules.master.entity.BomMaterialRepository -import com.ffii.fpsms.modules.master.entity.EquipmentDetailRepository + import com.ffii.fpsms.modules.jobOrder.entity.JobTypeRepository @Service @Transactional @@ -482,6 +483,7 @@ open class ProductProcessService( // get bom materials 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 @@ -559,6 +561,8 @@ open class ProductProcessService( return productProcesses.map { process -> val jobType = jobTypeRepository.findById(process.jobOrder?.jobTypeId?:0L).orElse(null) + println("jobType id ${process.jobOrder?.jobTypeId}") + ProductProcessInfo( id = process.id?:0, bomId = process.bom?.id?:0, @@ -586,6 +590,10 @@ open class ProductProcessService( 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, bomprocessId = line.bomProcess?.id?:0, @@ -597,6 +605,7 @@ open class ProductProcessService( name = line.name?:"", description = line.description?:"", equipment_name = line.equipmentType?:"", + equipmentDetailCode = equipmentDetail?.code?:"", status = line.status?:"", durationInMinutes = line.bomProcess?.durationInMinute?:0, prepTimeInMinutes = line.bomProcess?.prepTimeInMinute?:0, @@ -693,7 +702,7 @@ open class ProductProcessService( this.seqNo = bomProcess.seqNo?:0 this.name = process?.name?:"" this.description = bomProcess.description?:"" - this.equipmentType = equipment?.name?:"" + this.equipmentType = equipment?.code?:"" this.status = "Pending" } productProcessLineRepository.save(productProcessLine) @@ -710,6 +719,7 @@ open class ProductProcessService( } open fun UpdateProductProcessLineOperatorIdOrEquipmentId(request: UpdateProductProcessLineOperatorIdOrEquipmentIdRequest): MessageResponse { val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null) + /* val equipmentId = request.equipmentId val user = userRepository.findById(request.operatorId?:0L).orElse(null) val bomProcess= bomProcessRepository.findById(productProcessLine?.bomProcess?.id?:0L).orElse(null) @@ -753,6 +763,52 @@ open class ProductProcessService( productProcessLineRepository.save(productProcessLine) } + */ + + val equipmentTypeSubTypeEquipmentNo = request.equipmentTypeSubTypeEquipmentNo + println("equipmentTypeSubTypeEquipmentNo ${equipmentTypeSubTypeEquipmentNo}") + val staffNo = request.staffNo + println("staffNo ${staffNo}") + val user = userRepository.findByStaffNo(staffNo?:"").orElse(null) + println("user ${user?.id}") + + 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 + println("bomProcessEquipment ${bomProcessEquipment?.id}") + if (equipmentId != null && user != null) { + // 检查 equipmentId 是否与 bomProcessEquipment 匹配 + if (equipmentId != bomProcessEquipment?.id) { + println("productProcessLine id${request.productProcessLineId}") + + println("user ${user?.id}") + println("bomProcess ${bomProcess?.id}") + println("not match equipment id${equipmentId} and ${bomProcessEquipment?.id}") + + // 返回错误响应 + return MessageResponse( + id = request.productProcessLineId, + code = "400", + name = "Equipment Validation Failed", + type = "error", + message = "Input Equipment ID($equipmentId) and BOM Process Equipment ID(${bomProcessEquipment?.id}) not match", + errorPosition = "equipmentId" + ) + } + } + if(equipmentId != null &&( equipmentId==bomProcessEquipment?.id)) { + + productProcessLine?.equipment = bomProcessEquipment + productProcessLine?.equipmentDetailId = equipmentDetail?.id + productProcessLineRepository.save(productProcessLine) + } + if(user != null) { + productProcessLine.operator = user + productProcessLineRepository.save(productProcessLine) + } return MessageResponse( id = null, diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt index 45da477..4002621 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt @@ -2,14 +2,18 @@ package com.ffii.fpsms.modules.productProcess.web.model import java.time.LocalDate import java.time.LocalDateTime +import com.fasterxml.jackson.annotation.JsonProperty data class SaveProductProcessRequest( val bomId: Long, val jobOrderId: Long?, ) data class UpdateProductProcessLineOperatorIdOrEquipmentIdRequest( val productProcessLineId: Long, - val operatorId: Long?, - val equipmentId: Long? + //val operatorId: Long?, + //val equipmentId: Long?, + @JsonProperty("EquipmentType-SubType-EquipmentNo") + val equipmentTypeSubTypeEquipmentNo: String?, + val staffNo: String?, ) data class UpdateProductProcessLineHandlerIdRequest( val productProcessLineId: Long, diff --git a/src/main/java/com/ffii/fpsms/modules/user/entity/User.java b/src/main/java/com/ffii/fpsms/modules/user/entity/User.java index 510a11f..86161e8 100644 --- a/src/main/java/com/ffii/fpsms/modules/user/entity/User.java +++ b/src/main/java/com/ffii/fpsms/modules/user/entity/User.java @@ -3,18 +3,18 @@ package com.ffii.fpsms.modules.user.entity; import java.time.LocalDate; import java.util.Collection; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; -import jakarta.persistence.Transient; -import jakarta.validation.constraints.NotBlank; - import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import com.fasterxml.jackson.annotation.JsonIgnore; import com.ffii.core.entity.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import jakarta.validation.constraints.NotBlank; + /** @author Terence */ @Entity @Table(name = "user") @@ -77,6 +77,17 @@ public class User extends BaseEntity implements UserDetails { @Column private boolean lotusNotesUser = false; + @Column + private String staffNo; + + public String getStaffNo() { + return staffNo; + } + + public void setStaffNo(String staffNo) { + this.staffNo = staffNo; + } + public boolean isLocked() { return this.locked == null ? false : this.locked; } diff --git a/src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java b/src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java index 01ec68e..3b2d573 100644 --- a/src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java +++ b/src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java @@ -15,4 +15,6 @@ public interface UserRepository extends AbstractRepository { Optional findByUsernameAndDeletedFalse(String username); List findUserComboByTitleNotNullAndDepartmentNotNullAndNameNotNullAndDeletedFalse(); + + Optional findByStaffNo(@Param("staffNo") String staffNo); } diff --git a/src/main/resources/db/changelog/changes/20251130_01_enson/01_altertable_enson.sql b/src/main/resources/db/changelog/changes/20251130_01_enson/01_altertable_enson.sql new file mode 100644 index 0000000..dbf2541 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251130_01_enson/01_altertable_enson.sql @@ -0,0 +1,6 @@ +-- liquibase formatted sql +-- changeset enson:altertable_enson + + +ALTER TABLE `fpsmsdb`.`equipment_detail` +MODIFY COLUMN `equipmentTypeID` INT NULL;