| @@ -14,7 +14,9 @@ open class Warehouse : BaseEntity<Long>() { | |||
| @NotNull | |||
| @Column(name = "code", nullable = false, length = 30) | |||
| open var code: String? = null | |||
| @NotNull | |||
| @Column(name = "order", nullable = false, length = 30) | |||
| open var order: String? = null | |||
| @NotNull | |||
| @Column(name = "name", nullable = false, length = 30) | |||
| open var name: String? = null | |||
| @@ -4520,6 +4520,169 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri | |||
| "pickOrders" to pickOrders | |||
| ) | |||
| } | |||
| private fun numToBigDecimal(n: Number?): BigDecimal { | |||
| return when (n) { | |||
| null -> BigDecimal.ZERO | |||
| is BigDecimal -> n | |||
| else -> BigDecimal.valueOf(n.toDouble()) | |||
| } | |||
| } | |||
| open fun getLotDetailsByDoPickOrderRecordId2(doPickOrderRecordId: Long): Map<String, Any?> { | |||
| // 1) 头信息(来自 record 表) | |||
| val dpor = doPickOrderRecordRepository.findById(doPickOrderRecordId).orElse(null) | |||
| ?: return mapOf("fgInfo" to null, "pickOrders" to emptyList<Any>()) | |||
| val fgInfo = mapOf( | |||
| "doPickOrderId" to dpor.id, | |||
| "ticketNo" to dpor.ticketNo, | |||
| "storeId" to dpor.storeId, | |||
| "shopCode" to dpor.shopCode, | |||
| "shopName" to dpor.shopName, | |||
| "truckLanceCode" to dpor.truckLanceCode, | |||
| "departureTime" to dpor.truckDepartureTime | |||
| ) | |||
| // 2) 取该 record 下所有 pick orders(通过行记录聚合) | |||
| val lineRecords = doPickOrderLineRecordRepository.findByDoPickOrderIdAndDeletedFalse(dpor.id!!) | |||
| val pickOrderIds = lineRecords.mapNotNull { it.pickOrderId }.distinct() | |||
| if (pickOrderIds.isEmpty()) { | |||
| return mapOf("fgInfo" to fgInfo, "pickOrders" to emptyList<Any>()) | |||
| } | |||
| val pickOrders = pickOrderRepository.findAllById(pickOrderIds) | |||
| // 3) 逐个 pick order 组装行与批次/出库行 | |||
| val pickOrderDtos = pickOrders.map { po -> | |||
| val lines = po.pickOrderLines | |||
| val lineDtos = lines.map { pol -> | |||
| val item = pol.item | |||
| val uom = pol.uom | |||
| // 建议批次 | |||
| val lots = pol.suggestedPickLots.mapNotNull { spl -> | |||
| val ill = spl.suggestedLotLine ?: return@mapNotNull null | |||
| val il = ill.inventoryLot | |||
| val w = ill.warehouse | |||
| val today = LocalDate.now() | |||
| val isExpired = il?.expiryDate?.let { exp -> exp.isBefore(today) } == true | |||
| mapOf( | |||
| "id" to ill.id, | |||
| "lotNo" to il?.lotNo, | |||
| "expiryDate" to il?.expiryDate, | |||
| "location" to w?.name, | |||
| "stockUnit" to (ill.stockUom?.uom?.udfudesc ?: uom?.udfudesc ?: "N/A"), | |||
| "availableQty" to ((ill.inQty ?: BigDecimal.ZERO) | |||
| .minus(ill.outQty ?: BigDecimal.ZERO) | |||
| .minus(ill.holdQty ?: BigDecimal.ZERO)), | |||
| "requiredQty" to spl.qty, | |||
| "actualPickQty" to null, // 稍后回填 | |||
| "inQty" to ill.inQty, | |||
| "outQty" to ill.outQty, | |||
| "holdQty" to ill.holdQty, | |||
| "lotStatus" to ill.status?.value, | |||
| "lotAvailability" to when { | |||
| isExpired -> "expired" | |||
| ill.status?.value == "unavailable" -> "status_unavailable" | |||
| ((ill.inQty ?: BigDecimal.ZERO) | |||
| .minus(ill.outQty ?: BigDecimal.ZERO) | |||
| .minus(ill.holdQty ?: BigDecimal.ZERO)) <= BigDecimal.ZERO -> "insufficient_stock" | |||
| else -> "available" | |||
| }, | |||
| "processingStatus" to "pending", | |||
| "suggestedPickLotId" to spl.id, | |||
| "stockOutLineId" to null, | |||
| "stockOutLineStatus" to null, | |||
| "stockOutLineQty" to null, | |||
| "router" to mapOf( | |||
| "id" to null, | |||
| "index" to w?.order, | |||
| "route" to w?.code, | |||
| "area" to w?.code | |||
| ) | |||
| ) | |||
| } | |||
| // 出库行(投影):支持 inventoryLotLineId 为空 | |||
| val stockOutLines = pol.id?.let { | |||
| // 使用你已有的方法;若该方法返回投影类型(如 StockOutLineInfo),则以下基于字段名访问 | |||
| stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(it) | |||
| } ?: emptyList() | |||
| // 如果是投影,通常字段:id, qty(Double?), status(String?), inventoryLotLineId(Long?) | |||
| // 计算每个 ILL 的已拣数量(BigDecimal) | |||
| val actualQtyByIllId: Map<Long, BigDecimal> = stockOutLines | |||
| .mapNotNull { sol -> | |||
| val illId = sol.inventoryLotLineId ?: return@mapNotNull null | |||
| val qtyBD = numToBigDecimal(sol.qty as Number?) | |||
| illId to qtyBD | |||
| } | |||
| .groupBy({ it.first }, { it.second }) | |||
| .mapValues { (_, list) -> list.fold(BigDecimal.ZERO) { acc, v -> acc + v } } | |||
| val lotsWithActual = lots.map { lot -> | |||
| val illId = lot["id"] as? Long | |||
| val actual = illId?.let { actualQtyByIllId[it] } | |||
| lot.toMutableMap().apply { if (actual != null) put("actualPickQty", actual) } | |||
| } | |||
| // 构建 stockouts,若有 lotLineId 则加载 ILL 以补充 lotNo/location/availableQty | |||
| val stockouts = stockOutLines.map { sol -> | |||
| val illId = sol.inventoryLotLineId | |||
| val ill = illId?.let { inventoryLotLineRepository.findById(it).orElse(null) } | |||
| val il = ill?.inventoryLot | |||
| val w = ill?.warehouse | |||
| val available = if (ill == null) null else | |||
| (ill.inQty ?: BigDecimal.ZERO) | |||
| .minus(ill.outQty ?: BigDecimal.ZERO) | |||
| .minus(ill.holdQty ?: BigDecimal.ZERO) | |||
| mapOf( | |||
| "id" to sol.id, | |||
| "status" to sol.status, | |||
| "qty" to numToBigDecimal(sol.qty as Number?), | |||
| "lotId" to ill?.id, | |||
| "lotNo" to (il?.lotNo ?: ""), | |||
| "location" to (w?.name ?: ""), | |||
| "availableQty" to available, | |||
| "noLot" to (ill == null) | |||
| ) | |||
| } | |||
| mapOf( | |||
| "id" to pol.id, | |||
| "requiredQty" to pol.qty, | |||
| "status" to pol.status?.value, | |||
| "item" to mapOf( | |||
| "id" to item?.id, | |||
| "code" to item?.code, | |||
| "name" to item?.name, | |||
| "uomCode" to uom?.code, | |||
| "uomDesc" to uom?.udfudesc, | |||
| "uomShortDesc" to uom?.udfShortDesc | |||
| ), | |||
| "lots" to lotsWithActual, | |||
| "stockouts" to stockouts | |||
| ) | |||
| } | |||
| mapOf( | |||
| "pickOrderId" to po.id, | |||
| "pickOrderCode" to po.code, | |||
| "doOrderId" to po.deliveryOrder?.id, | |||
| "deliveryOrderCode" to po.deliveryOrder?.code, | |||
| "consoCode" to po.consoCode, | |||
| "status" to po.status?.value, | |||
| "targetDate" to po.targetDate?.toLocalDate(), | |||
| "pickOrderLines" to lineDtos | |||
| ) | |||
| } | |||
| return mapOf( | |||
| "fgInfo" to fgInfo, | |||
| "pickOrders" to pickOrderDtos | |||
| ) | |||
| } | |||
| // ✅ 新增:从 do_pick_order_record 表查询(用于已完成的 pick orders) | |||
| open fun getFgPickOrdersFromRecordByPickOrderId(pickOrderId: Long): List<Map<String, Any?>> { | |||
| try { | |||
| @@ -163,7 +163,7 @@ class PickOrderController( | |||
| } | |||
| @GetMapping("/lot-details-by-do-pick-order-record/{doPickOrderRecordId}") | |||
| fun getLotDetailsByDoPickOrderRecordId(@PathVariable doPickOrderRecordId: Long): Map<String, Any?> { | |||
| return pickOrderService.getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId) | |||
| return pickOrderService.getLotDetailsByDoPickOrderRecordId2(doPickOrderRecordId) | |||
| } | |||
| @PostMapping("/groups") | |||
| @@ -584,7 +584,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).orElse(null) | |||
| val user = userRepository.findById(request.operatorId?:0L).orElse(null) | |||
| val bomProcess= bomProcessRepository.findById(productProcessLine?.bomProcess?.id?:0L).orElse(null) | |||
| val bomProcessEquipment=bomProcess?.equipment | |||
| if(equipmentId != null &&( equipmentId==bomProcessEquipment?.id)) { | |||
| @@ -596,7 +596,7 @@ open class ProductProcessService( | |||
| else | |||
| { | |||
| println("not match") | |||
| println("not match equipment id${equipmentId} and ${bomProcessEquipment?.id}") | |||
| } | |||
| if(user != null) { | |||
| productProcessLine.operator = user | |||
| @@ -613,4 +613,50 @@ open class ProductProcessService( | |||
| 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) { | |||
| productProcessLine.handler = user | |||
| productProcessLineRepository.save(productProcessLine) | |||
| } | |||
| return MessageResponse( | |||
| id = null, | |||
| code = null, | |||
| name = null, | |||
| type = null, | |||
| message = null, | |||
| errorPosition = null, | |||
| ) | |||
| } | |||
| open fun getJobOrderProcessLineDetail(productProcessLineId: Long): JobOrderProcessLineDetailResponse { | |||
| val productProcessLine = productProcessLineRepository.findById(productProcessLineId).orElse(null) | |||
| 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, | |||
| seqNo = productProcessLine.seqNo?:0, | |||
| name = productProcessLine.name?:"", | |||
| description = productProcessLine.description?:"", | |||
| equipmentType = productProcessLine.equipmentType?:"", | |||
| equipment = productProcessLine.equipment?.name?:"", | |||
| startTime = productProcessLine.startTime?:LocalDateTime.now(), | |||
| endTime = productProcessLine.endTime?:LocalDateTime.now(), | |||
| status = productProcessLine.status?:"", | |||
| outputFromProcessQty = productProcessLine.outputFromProcessQty?:0, | |||
| outputFromProcessUom = productProcessLine.outputFromProcessUom?:"", | |||
| defectQty = productProcessLine.defectQty?:0, | |||
| defectUom = productProcessLine.defectUom?:"", | |||
| scrapQty = productProcessLine.scrapQty?:0, | |||
| scrapUom = productProcessLine.scrapUom?:"", | |||
| byproductId = productProcessLine.byproduct?.id?:0, | |||
| byproductName = productProcessLine.byproduct?.name?:"", | |||
| byproductQty = productProcessLine.byproductQty?:0, | |||
| byproductUom = productProcessLine.byproductUom?:"" | |||
| ) | |||
| } | |||
| } | |||
| @@ -167,4 +167,8 @@ class ProductProcessController( | |||
| fun demoupdate(@RequestBody request: UpdateProductProcessLineOperatorIdOrEquipmentIdRequest): MessageResponse { | |||
| return productProcessService.UpdateProductProcessLineOperatorIdOrEquipmentId(request) | |||
| } | |||
| @PostMapping("/Demo/update/handler") | |||
| fun demoupdatehandler(@RequestBody request: UpdateProductProcessLineHandlerIdRequest): MessageResponse { | |||
| return productProcessService.UpdateProductProcessLineHandlerId(request) | |||
| } | |||
| } | |||
| @@ -11,6 +11,10 @@ data class UpdateProductProcessLineOperatorIdOrEquipmentIdRequest( | |||
| val operatorId: Long?, | |||
| val equipmentId: Long? | |||
| ) | |||
| data class UpdateProductProcessLineHandlerIdRequest( | |||
| val productProcessLineId: Long, | |||
| val handlerId: Long? | |||
| ) | |||
| data class ProductionProcessIssueResponse( | |||
| val id: Long, | |||
| val productProcessId: Long?, | |||
| @@ -89,4 +93,31 @@ data class UpdateLineOutputRequest( | |||
| val byproductName: String?, | |||
| val byproductQty: Int?, | |||
| val byproductUom: String? | |||
| ) | |||
| ) | |||
| data class JobOrderProcessLineDetailResponse( | |||
| val id: Long, | |||
| val productProcessId: Long?, | |||
| val bomProcessId: Long?, | |||
| val operatorId: Long?, | |||
| val equipmentType: String?, | |||
| val operatorName: String?, | |||
| val handlerId: Long?, | |||
| val seqNo: Long?, | |||
| val name: String?, | |||
| val description: String?, | |||
| val equipment: String?, | |||
| val startTime: LocalDateTime?, | |||
| val endTime: LocalDateTime?, | |||
| val status: String, | |||
| val outputFromProcessQty: Int?, | |||
| val outputFromProcessUom: String?, | |||
| val defectQty: Int?, | |||
| val defectUom: String?, | |||
| val scrapQty: Int?, | |||
| val scrapUom: String?, | |||
| val byproductId: Long?, | |||
| val byproductName: String?, | |||
| val byproductQty: Int?, | |||
| val byproductUom: String? | |||
| ) | |||