| | |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackApproveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| | |
| | | private MesWmProductProduceDetailService productProduceDetailService; |
| | | @Resource |
| | | private MesProWorkOrderService workOrderService; |
| | | @Resource |
| | | private MesProWorkOrderProcessService workOrderProcessService; |
| | | @Resource |
| | | private MesProRouteProductService routeProductService; |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | @Resource |
| | |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesWmProductProduceDO generateProductProduce(MesProFeedbackDO feedback, boolean checkFlag) { |
| | | public MesWmProductProduceDO generateProductProduce(MesProFeedbackDO feedback, boolean checkFlag, |
| | | MesProFeedbackApproveReqVO approveReqVO) { |
| | | // 0. 查询关联的工单(用于获取 clientId 等信息) |
| | | MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(feedback.getWorkOrderId()); |
| | | |
| | |
| | | .build(); |
| | | productProduceMapper.insert(produce); |
| | | |
| | | // 2. 获取或生成批次 |
| | | // 2. 获取或生成批次(审批请求中的批次属性优先) |
| | | MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO() |
| | | .setItemId(feedback.getItemId()) |
| | | .setProduceDate(LocalDate.now().atStartOfDay()) // 截断到当天零点,确保同一天的报工生成相同批次号 |
| | | .setExpireDate(feedback.getExpireDate()) |
| | | .setWorkOrderId(feedback.getWorkOrderId()) |
| | | .setTaskId(feedback.getTaskId()) |
| | | .setClientId(workOrder != null ? workOrder.getClientId() : null) |
| | | .setSalesOrderCode(workOrder != null ? workOrder.getOrderSourceCode() : null) |
| | | .setWorkstationId(feedback.getWorkstationId()) |
| | | .setLotNumber(feedback.getLotNumber()); |
| | | // 合并审批请求中用户填写的批次属性(用户输入值优先) |
| | | if (approveReqVO != null) { |
| | | if (approveReqVO.getExpireDate() != null) batchReqVO.setExpireDate(approveReqVO.getExpireDate()); |
| | | if (approveReqVO.getReceiptDate() != null) batchReqVO.setReceiptDate(approveReqVO.getReceiptDate()); |
| | | if (approveReqVO.getVendorId() != null) batchReqVO.setVendorId(approveReqVO.getVendorId()); |
| | | if (approveReqVO.getClientId() != null) batchReqVO.setClientId(approveReqVO.getClientId()); |
| | | if (approveReqVO.getSalesOrderCode() != null) batchReqVO.setSalesOrderCode(approveReqVO.getSalesOrderCode()); |
| | | if (approveReqVO.getPurchaseOrderCode() != null) batchReqVO.setPurchaseOrderCode(approveReqVO.getPurchaseOrderCode()); |
| | | if (approveReqVO.getToolId() != null) batchReqVO.setToolId(approveReqVO.getToolId()); |
| | | if (approveReqVO.getMoldId() != null) batchReqVO.setMoldId(approveReqVO.getMoldId()); |
| | | if (approveReqVO.getLotNumber() != null) batchReqVO.setLotNumber(approveReqVO.getLotNumber()); |
| | | if (approveReqVO.getQualityStatus() != null) batchReqVO.setQualityStatus(approveReqVO.getQualityStatus()); |
| | | } |
| | | MesWmBatchDO batch = batchService.getOrGenerateBatchCode(batchReqVO); |
| | | Long batchId = batch != null ? batch.getId() : null; |
| | | String batchCode = batch != null ? batch.getCode() : null; |
| | | |
| | | // 1.5 获取虚拟线边库 |
| | | MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); |
| | | MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); |
| | | WarehouseTarget outputTarget = resolveOutputTarget(feedback.getWorkOrderId(), feedback.getProcessId(), feedback.getItemId()); |
| | | |
| | | // 3. 根据是否需要检验分支处理 |
| | | if (checkFlag) { |
| | |
| | | // 3.2 无需检验:按合格品/不合格品各生成一行 + 明细 |
| | | BigDecimal qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO); |
| | | BigDecimal unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO); |
| | | // 3.2.0 兜底:合格/不良数量都未填写时(报工只填报工数量),默认整单为合格品 |
| | | if (qualifiedQty.compareTo(BigDecimal.ZERO) <= 0 && unqualifiedQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | qualifiedQty = feedback.getFeedbackQuantity(); |
| | | } |
| | | // 3.2.1 不合格品行 + 明细 |
| | | if (unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | MesWmProductProduceLineDO unqualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode, |
| | |
| | | productProduceLineService.createProductProduceLine(unqualifiedLine); |
| | | MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode, |
| | | unqualifiedLine.getId(), unqualifiedQty, |
| | | virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId()); |
| | | outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId()); |
| | | productProduceDetailService.createProductProduceDetail(unqualifiedDetail); |
| | | } |
| | | // 3.2.2 合格品行 + 明细 |
| | |
| | | productProduceLineService.createProductProduceLine(qualifiedLine); |
| | | MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode, |
| | | qualifiedLine.getId(), qualifiedQty, |
| | | virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId()); |
| | | outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId()); |
| | | productProduceDetailService.createProductProduceDetail(qualifiedDetail); |
| | | } |
| | | } |
| | |
| | | if (produce == null) { |
| | | throw exception(WM_PRODUCT_PRODUCE_NOT_EXISTS); |
| | | } |
| | | // 1.2 获取虚拟线边库 |
| | | MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); |
| | | MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); |
| | | |
| | | // 2. 通过 sourceLineId 直接定位待检验行 |
| | | MesWmProductProduceLineDO pendingLine = productProduceLineService.validateProductProduceLineExists(sourceLineId); |
| | | WarehouseTarget outputTarget = resolveOutputTarget(produce.getWorkOrderId(), produce.getProcessId(), pendingLine.getItemId()); |
| | | |
| | | // 3A. 情况一:存在不合格品数量,需要拆分行 |
| | | if (unqualifiedQty != null && unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) { |
| | |
| | | .produceId(produce.getId()).lineId(unqualifiedLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(unqualifiedQty) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | // 3A.2 合格品:复用原待检行,更新数量和状态 + 明细 |
| | | pendingLine.setQuantity(ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO)); |
| | |
| | | .produceId(produce.getId()).lineId(pendingLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(qualifiedQty) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | } |
| | | } else { |
| | |
| | | .produceId(produce.getId()).lineId(pendingLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(pendingLine.getQuantity()) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | } |
| | | |
| | |
| | | finishProductProduce(produce.getId()); |
| | | } |
| | | |
| | | private WarehouseTarget resolveOutputTarget(Long workOrderId, Long processId, Long itemId) { |
| | | if (workOrderId != null && processId != null) { |
| | | MesProWorkOrderProcessDO process = workOrderProcessService |
| | | .getWorkOrderProcessByWorkOrderIdAndProcessId(workOrderId, processId); |
| | | if (process != null && process.getWarehouseId() != null |
| | | && process.getLocationId() != null && process.getAreaId() != null) { |
| | | areaService.validateWarehouseAreaExists(process.getWarehouseId(), process.getLocationId(), process.getAreaId()); |
| | | return new WarehouseTarget(process.getWarehouseId(), process.getLocationId(), process.getAreaId()); |
| | | } |
| | | } |
| | | MesProRouteProductDO product = itemId == null ? null : routeProductService.getRouteProductByItemId(itemId); |
| | | if (product != null && product.getWarehouseId() != null |
| | | && product.getLocationId() != null && product.getAreaId() != null) { |
| | | areaService.validateWarehouseAreaExists(product.getWarehouseId(), product.getLocationId(), product.getAreaId()); |
| | | return new WarehouseTarget(product.getWarehouseId(), product.getLocationId(), product.getAreaId()); |
| | | } |
| | | MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); |
| | | MesWmWarehouseAreaDO area = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); |
| | | return new WarehouseTarget(warehouse.getId(), location.getId(), area.getId()); |
| | | } |
| | | |
| | | private record WarehouseTarget(Long warehouseId, Long locationId, Long areaId) { } |
| | | |
| | | } |