package org.jeecg.modules.mes.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.exception.JeecgBootException; import org.jeecg.modules.base.entity.Factory; import org.jeecg.modules.base.entity.LineSideWarehouse; import org.jeecg.modules.base.entity.Shift; import org.jeecg.modules.base.entity.ShiftGroup; import org.jeecg.modules.base.service.IFactoryService; import org.jeecg.modules.base.service.ILineSideWarehouseService; import org.jeecg.modules.base.service.IShiftGroupService; import org.jeecg.modules.base.service.IShiftService; import org.jeecg.modules.lsw.entity.LswMaterial; import org.jeecg.modules.lsw.entity.LswMaterialInbound; import org.jeecg.modules.lsw.enums.MaterialCategoryEnum; import org.jeecg.modules.lsw.service.ILswMaterialInboundService; import org.jeecg.modules.lsw.service.ILswMaterialService; import org.jeecg.modules.mes.entity.MesMaterialLoading; import org.jeecg.modules.mes.entity.MesProductionOrder; import org.jeecg.modules.mes.entity.MesProductionWorkOrder; import org.jeecg.modules.mes.entity.MesWorkReporting; import org.jeecg.modules.mes.mapper.MesWorkReportingMapper; import org.jeecg.modules.mes.service.IMesMaterialLoadingService; import org.jeecg.modules.mes.service.IMesProductionOrderService; import org.jeecg.modules.mes.service.IMesProductionWorkOrderService; import org.jeecg.modules.mes.service.IMesWorkReportingService; import org.jeecg.modules.mes.utils.CommonUtils; import org.jeecg.modules.pms.entity.PmsMaterialProcess; import org.jeecg.modules.pms.service.IPmsMaterialProcessService; import org.jeecg.modules.pms.service.IPmsProcessBillMaterialsService; import org.jeecg.modules.pms.vo.ProcessBillMaterialsDetailVo; import org.jeecg.modules.sap.request.OrderLoadRequest; import org.jeecg.modules.sap.request.OrderReportRequest; import org.jeecg.modules.sap.service.OrderLoadService; import org.jeecg.modules.sap.service.OrderReportService; import org.jeecg.modules.system.service.ISysBusinessCodeRuleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; import java.util.*; import java.util.stream.Collectors; /** * @Description: 工单报工 * @Author: jeecg-boot * @Date: 2025-07-04 * @Version: V1.0 */ @Slf4j @Service public class MesWorkReportingServiceImpl extends ServiceImpl implements IMesWorkReportingService { @Autowired private IMesProductionWorkOrderService mesProductionWorkOrderService; @Autowired private IMesProductionOrderService mesProductionOrderService; @Autowired private ILswMaterialService lswMaterialService; @Autowired private IFactoryService factoryService; @Autowired private IShiftService shiftService; @Autowired private IShiftGroupService shiftGroupService; @Autowired private ILineSideWarehouseService lineSideWarehouseService; @Autowired private ILswMaterialInboundService lswMaterialInboundService; @Autowired private IMesMaterialLoadingService mesMaterialLoadingService; @Autowired private IPmsProcessBillMaterialsService pmsProcessBillMaterialsService; @Autowired private IPmsMaterialProcessService pmsMaterialProcessService; @Autowired private ISysBusinessCodeRuleService sysBusinessCodeRuleService; @Autowired private OrderReportService orderReportService; @Autowired private OrderLoadService orderLoadService; @Override public List queryWorkReportingByWorkOrderId(String workOrderId) { return baseMapper.queryWorkReportingByWorkOrderId(workOrderId); } @Override public List queryWorkReportingByOrderId(String orderId) { return baseMapper.queryWorkReportingByOrderId(orderId); } @Override @Transactional(rollbackFor = Exception.class) public void addReporting(MesWorkReporting mesWorkReporting) { MesProductionWorkOrder workOrder = mesProductionWorkOrderService.getById(mesWorkReporting.getWorkOrderId()); if (workOrder == null) { throw new JeecgBootException("工单不存在!"); } LswMaterial material = lswMaterialService.list(new LambdaQueryWrapper() .eq(LswMaterial::getMaterialNumber, workOrder.getMaterialNumber()) .eq(LswMaterial::getDelFlag, CommonConstant.DEL_FLAG_0)).stream() .findAny().orElse(null); if (material == null) { throw new JeecgBootException("物料不存在!"); } MaterialCategoryEnum materialCategory = MaterialCategoryEnum.fromName(material.getMaterialCategory()); if (materialCategory == null) { throw new JeecgBootException("未知的物料类型!"); } LineSideWarehouse lineSideWarehouse = lineSideWarehouseService.list(new LambdaQueryWrapper() .eq(LineSideWarehouse::getFactoryId, mesWorkReporting.getFactoryId()) .eq(LineSideWarehouse::getDelFlag, CommonConstant.DEL_FLAG_0)) .stream().findAny().orElse(null); if (lineSideWarehouse == null) { throw new JeecgBootException("线边仓库不存在!"); } //生产批次号 LocalDate today = LocalDate.now(); String yearLastTwo = today.format(DateTimeFormatter.ofPattern("yy")); String dayOfYearStr = today.format(DateTimeFormatter.ofPattern("DDD")); ShiftGroup shiftGroup = shiftGroupService.getById(workOrder.getGroupId()); int currentPallets = workOrder.getFinishedPallets() + 1; String batchNumber = yearLastTwo + dayOfYearStr + shiftGroup.getGroupCode() + currentPallets; mesWorkReporting.setBatchNumber(batchNumber) .setWarehouseId(lineSideWarehouse.getId()) .setReporter(Objects.requireNonNull(CommonUtils.getCurrentUser()).getUsername()) .setReportTime(new Date()); String date = today.format(DateTimeFormatter.ofPattern("yyMMdd")); Factory factory = factoryService.getById(mesWorkReporting.getFactoryId()); workOrder.setFactoryCode(factory.getFactoryCode()); switch (materialCategory) { case FINISHED_PRODUCT: //成品报工 productReport(mesWorkReporting, workOrder, lineSideWarehouse); //拼接成品报工托号 String palletNumber4 = sysBusinessCodeRuleService.generateBusinessCodeSeq("WorkReportingPalletNumber4"); String finishedPalletNumber = material.getMaterialNumber() + date + palletNumber4; mesWorkReporting.setPalletNumber(finishedPalletNumber); //todo 打印成品托标签、检验标识卡、移库单、完工检验工单 break; case INNER_FLANGE: case OUTER_FLANGE: //半成品(内法兰、外法兰)报工 productReport(mesWorkReporting, workOrder, lineSideWarehouse); //拼接半成品报工托号 Shift shift = shiftService.getById(workOrder.getShiftId()); String palletNumber6 = sysBusinessCodeRuleService.generateBusinessCodeSeq("WorkReportingPalletNumber6"); String halfPalletNumber = factory.getFactoryCode() + material.getMaterialNumber() + date + shift.getShiftCode() + palletNumber6; mesWorkReporting.setPalletNumber(halfPalletNumber); break; } //新增报工记录 save(mesWorkReporting); //更新排产工单 MesProductionWorkOrder productionWorkUpdateOrder = new MesProductionWorkOrder() .setId(workOrder.getId()) .setActualQuantity(workOrder.getActualQuantity().add(mesWorkReporting.getQuantity())) .setFinishedPallets(workOrder.getFinishedPallets() + 1); mesProductionWorkOrderService.updateById(productionWorkUpdateOrder); } //半成品(内法兰、外法兰)、成品报工 private void productReport(MesWorkReporting mesWorkReporting, MesProductionWorkOrder workOrder, LineSideWarehouse lineSideWarehouse) { //1、入库 半成品/成品 到对应线边库 LswMaterialInbound materialInbound = new LswMaterialInbound() .setFactoryId(workOrder.getFactoryId()) .setWarehouseId(lineSideWarehouse.getId()) .setMaterialNumber(workOrder.getMaterialNumber()) .setMaterialName(workOrder.getMaterialName()) .setBatchNumber(mesWorkReporting.getBatchNumber()) .setQuantity(mesWorkReporting.getQuantity()); lswMaterialInboundService.save(materialInbound); //2、扣减使用毛坯库存(更新上料记录)或半成品物料库存(内法兰、外法兰、小内圈、配件等),按照 排产工单id、物料编码 查询上料记录,按照时间从早到晚排序,优先扣减早的上料记录(先进先出) //先查询物料 BOM Map processBillMaterialsDetailMap = pmsProcessBillMaterialsService .queryMaterialsDetailByOrderIdAndMaterialNumber(mesWorkReporting.getOrderId(), workOrder.getMaterialNumber()).stream() .collect(Collectors.toMap(ProcessBillMaterialsDetailVo::getMaterialNumber, v1 -> v1, (v1, v2) -> v1)); //查询上料记录 Map> materialLoadingRecordMap = mesMaterialLoadingService.list(new LambdaQueryWrapper() .eq(MesMaterialLoading::getWorkOrderId, workOrder.getId()) .in(MesMaterialLoading::getMaterialNumber, processBillMaterialsDetailMap.keySet()) .gt(MesMaterialLoading::getRemainingQuantity, BigDecimal.ZERO)).stream() .sorted(Comparator.comparing(MesMaterialLoading::getCreateTime)) .collect(Collectors.groupingBy(MesMaterialLoading::getMaterialNumber)); List materialLoadingRecordUpdateList = CollectionUtil.newArrayList(); for (String materialNumber : processBillMaterialsDetailMap.keySet()) { List materialLoadingRecordList = materialLoadingRecordMap.get(materialNumber); if (materialLoadingRecordList.isEmpty()) { throw new JeecgBootException("未查询到相应物料的上料记录!"); } //计算物料扣减数量 ProcessBillMaterialsDetailVo processBillMaterialsDetailVo = processBillMaterialsDetailMap.get(materialNumber); BigDecimal quantity = processBillMaterialsDetailVo.getPerQuantity().multiply(mesWorkReporting.getQuantity()); List updateList = deductQuantity(materialLoadingRecordList, quantity); materialLoadingRecordUpdateList.addAll(updateList); } //更新上料记录 mesMaterialLoadingService.updateBatchById(materialLoadingRecordUpdateList); MesProductionOrder productionOrder = mesProductionOrderService.getById(mesWorkReporting.getOrderId()); //3、调用SAP投料接口(消耗的物料) OrderLoadService for (String materialNumber : processBillMaterialsDetailMap.keySet()) { ProcessBillMaterialsDetailVo processBillMaterialsDetailVo = processBillMaterialsDetailMap.get(materialNumber); OrderLoadRequest loadRequest = new OrderLoadRequest() .setOrderCode(productionOrder.getOrderCode()) .setFactoryCode(workOrder.getFactoryCode()) .setMaterialNumber(materialNumber) .setWarehouseCode(lineSideWarehouse.getWarehouseCode()) .setQuantity(processBillMaterialsDetailVo.getPerQuantity().multiply(mesWorkReporting.getQuantity())) .setBatchNumber(mesWorkReporting.getBatchNumber()) .setProductionUnit(processBillMaterialsDetailVo.getProductionUnit()); try { orderLoadService.productionOrderLoad(loadRequest); } catch (Exception e) { log.error("SAP生产订单投料异常!", e); } } //4、调用SAP工序报工(根据生产订单id查询对应的物料工序(半成品是多个工序,成品只有一个工序),按照工序号从小到大依次挨个报工;目前考虑异步执行) OrderReportService List processList = pmsMaterialProcessService.list(new LambdaQueryWrapper() .eq(PmsMaterialProcess::getOrderId, mesWorkReporting.getOrderId()) .orderByAsc(PmsMaterialProcess::getProcessCode)); for (PmsMaterialProcess process : processList) { OrderReportRequest reportRequest = new OrderReportRequest() .setOrderCode(productionOrder.getOrderCode()) .setFactoryCode(workOrder.getFactoryCode()) .setProcessCode(process.getProcessCode()) .setQualifiedQuantity(mesWorkReporting.getQuantity()) .setWarehouseCode(lineSideWarehouse.getWarehouseCode()) .setBatchNumber(mesWorkReporting.getBatchNumber()); try { orderReportService.productionOrderReport(reportRequest); } catch (Exception e) { log.error("SAP工序报工异常!", e); } } } private List deductQuantity(List materialLoadingRecordList, BigDecimal quantity) { for (MesMaterialLoading mesMaterialLoading : materialLoadingRecordList) { if (mesMaterialLoading.getRemainingQuantity().compareTo(quantity) >= 0) { mesMaterialLoading.setRemainingQuantity(mesMaterialLoading.getRemainingQuantity().subtract(quantity)); break; } else { quantity = quantity.subtract(mesMaterialLoading.getRemainingQuantity()); mesMaterialLoading.setRemainingQuantity(BigDecimal.ZERO); } } return materialLoadingRecordList; } }