package org.jeecg.modules.mes.service.impl; import cn.hutool.core.collection.CollectionUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 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.LswMaterialInventory; import org.jeecg.modules.lsw.service.ILswMaterialInventoryService; import org.jeecg.modules.lsw.vo.LswMaterialInventoryVo; import org.jeecg.modules.mes.dto.MesProductionWorkScheduleRequest; import org.jeecg.modules.mes.entity.MesKittingCompletenessCheck; import org.jeecg.modules.mes.entity.MesProductionOrder; import org.jeecg.modules.mes.enums.ProductionOrderStatus; import org.jeecg.modules.mes.service.IMesProductionOrderService; import org.jeecg.modules.mes.service.IMesProductionWorkOrderService; import org.jeecg.modules.mes.entity.MesProductionWorkOrder; import org.jeecg.modules.mes.mapper.MesProductionWorkOrderMapper; import org.jeecg.modules.pms.entity.PmsProcessBillMaterials; import org.jeecg.modules.pms.entity.PmsProcessBillMaterialsDetail; import org.jeecg.modules.pms.service.IPmsProcessBillMaterialsDetailService; import org.jeecg.modules.pms.service.IPmsProcessBillMaterialsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.ZoneId; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @Description: 排产工单 * @Author: jeecg-boot * @Date: 2025-07-04 * @Version: V1.0 */ @Service public class MesProductionWorkOrderServiceImpl extends ServiceImpl implements IMesProductionWorkOrderService { @Autowired private IShiftService shiftService; @Autowired private IShiftGroupService shiftGroupService; @Autowired private IFactoryService factoryService; @Autowired private IMesProductionOrderService mesProductionOrderService; @Autowired private IPmsProcessBillMaterialsService pmsProcessBillMaterialsService; @Autowired private IPmsProcessBillMaterialsDetailService pmsProcessBillMaterialsDetailService; @Autowired private ILswMaterialInventoryService lswMaterialInventoryService; @Autowired private ILineSideWarehouseService lineSideWarehouseService; @Override public List schedule(MesProductionWorkScheduleRequest request) { //查询起止日期范围内的排产计划,先排除 //查询该产线下所有的班次 Map shiftGroupMap = shiftGroupService.list(new LambdaQueryWrapper() .eq(ShiftGroup::getFactoryId, request.getFactoryId()) .eq(ShiftGroup::getDelFlag, CommonConstant.DEL_FLAG_0)) .stream().collect(Collectors.toMap(ShiftGroup::getShiftId, v1 -> v1, (v1, v2) -> v1)); Factory factory = factoryService.getById(request.getFactoryId()); Map shiftNameMap = new HashMap<>(); List shifts = shiftService.list(new LambdaQueryWrapper() .in(Shift::getId, shiftGroupMap.keySet())); shifts.forEach(shift -> shiftNameMap.put(shift.getId(), shift)); LocalDate startDate = request.getStartDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate endDate = request.getEndDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); // 使用日期范围进行遍历处理 List dateRange = Stream.iterate(startDate, date -> date.plusDays(1)) .limit(ChronoUnit.DAYS.between(startDate, endDate) + 1) .collect(Collectors.toList()); List newProductionWorkOrderList = CollectionUtil.newArrayList(); for (LocalDate date : dateRange) { for (String shiftId : shiftGroupMap.keySet()) { Date workOrderDate = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant()); //查询该产线、班次在该日期下是否有排产计划 Optional optional = list(new LambdaQueryWrapper() .eq(MesProductionWorkOrder::getWorkOrderDate, workOrderDate) .eq(MesProductionWorkOrder::getFactoryId, request.getFactoryId()) .eq(MesProductionWorkOrder::getShiftId, shiftId)) .stream().findAny(); if (!optional.isPresent()) { ShiftGroup shiftGroup = shiftGroupMap.get(shiftId); //没有,生成新排产计划 MesProductionWorkOrder mesProductionWorkOrder = new MesProductionWorkOrder() .setFactoryId(factory.getId()) .setFactoryCode(factory.getFactoryCode()) .setFactoryName(factory.getFactoryName()) .setShiftId(shiftId) .setShiftCode(shiftNameMap.get(shiftId).getShiftCode()) .setShiftName(shiftNameMap.get(shiftId).getShiftName()) .setGroupId(shiftGroup.getId()) .setGroupName(shiftGroup.getGroupName()) .setWorkOrderDate(workOrderDate); newProductionWorkOrderList.add(mesProductionWorkOrder); } } } //如果为空,默认给一条,用于手动新增时表格的初始化 if (newProductionWorkOrderList.isEmpty()) { MesProductionWorkOrder mesProductionWorkOrder = new MesProductionWorkOrder() .setFactoryId(factory.getId()) .setFactoryCode(factory.getFactoryCode()) .setFactoryName(factory.getFactoryName()); newProductionWorkOrderList.add(mesProductionWorkOrder); } return newProductionWorkOrderList; } @Override public IPage queryPageList(Page page, Map parameterMap) { QueryWrapper queryWrapper = Wrappers.query(); String[] factoryIds = parameterMap.get("factoryId"); if (factoryIds != null && factoryIds.length > 0) { queryWrapper.eq("t1.factory_id", factoryIds[0]); } String[] startDates = parameterMap.get("startDate"); String[] endDates = parameterMap.get("endDate"); if (startDates != null && startDates.length > 0) { queryWrapper.ge("t1.work_order_date", startDates[0]); } if (endDates != null && endDates.length > 0) { queryWrapper.le("t1.work_order_date", endDates[0]); } String[] workOrderStatuses = parameterMap.get("workOrderStatus"); if (workOrderStatuses != null && workOrderStatuses.length > 0) { queryWrapper.eq("t1.work_order_status", workOrderStatuses[0]); } queryWrapper.eq("t1.del_flag", CommonConstant.DEL_FLAG_0); queryWrapper.orderByAsc("t1.work_order_date"); return this.baseMapper.queryPageList(page, queryWrapper); } @Override public List workOrderCompletenessCheck(MesProductionWorkOrder workOrder) { //根据当前排产工单确定生产订单 List orderList = mesProductionOrderService.list(new LambdaQueryWrapper() .eq(MesProductionOrder::getMaterialNumber, workOrder.getMaterialNumber()) .eq(MesProductionOrder::getOrderStatus, ProductionOrderStatus.REL.name()) .eq(MesProductionOrder::getDelFlag, CommonConstant.DEL_FLAG_0) .orderByAsc(MesProductionOrder::getPlanStart)); if (orderList.isEmpty()) { throw new JeecgBootException("未找到该物料的关联生产订单!"); } //默认取时间最早未完成的订单,也就是第一项 MesProductionOrder order = orderList.get(0); //根据生产订单id和物料编码查询订单BOM PmsProcessBillMaterials processBillMaterials = pmsProcessBillMaterialsService.list(new LambdaQueryWrapper() .eq(PmsProcessBillMaterials::getOrderId, order.getId()) .eq(PmsProcessBillMaterials::getMaterialNumber, workOrder.getMaterialNumber())) .stream().findFirst().orElse(null); if (processBillMaterials == null) { throw new JeecgBootException("未找到与该物料关联的订单BOM!"); } //查询工单所属产线对应的线边仓 LineSideWarehouse lineSideWarehouse = lineSideWarehouseService.list(new LambdaQueryWrapper() .eq(LineSideWarehouse::getFactoryId, workOrder.getFactoryId()) .eq(LineSideWarehouse::getDelFlag, CommonConstant.DEL_FLAG_0) .eq(LineSideWarehouse::getWarehouseStatus, CommonConstant.DEFAULT_1)) .stream().findFirst().orElse(null); if (lineSideWarehouse == null) { throw new JeecgBootException("该产线未配置线边仓!"); } //订单BOM明细 List processBillMaterialsDetails = pmsProcessBillMaterialsDetailService.queryByMaterialId(processBillMaterials.getId()); //查询订单BOM明细中的物料在该产线线边仓中的库存 List bomMaterialNumberList = processBillMaterialsDetails.stream() .map(PmsProcessBillMaterialsDetail::getMaterialNumber).collect(Collectors.toList()); Map lswMaterialInventoryMap = lswMaterialInventoryService .selectLineSideMaterialInventoryByMaterialNumber(bomMaterialNumberList, lineSideWarehouse.getId()).stream() .collect(Collectors.toMap(LswMaterialInventoryVo::getMaterialNumber, v1 -> v1, (v1, v2) -> v1)); List completenessCheckResultList = CollectionUtil.newArrayList(); //根据订单BOM明细列出齐套检查结果 for (PmsProcessBillMaterialsDetail processBillMaterialsDetail : processBillMaterialsDetails) { LswMaterialInventoryVo materialInventoryVo = lswMaterialInventoryMap.get(processBillMaterialsDetail.getMaterialNumber()); MesKittingCompletenessCheck completenessCheckItem = new MesKittingCompletenessCheck() .setMaterialNumber(processBillMaterialsDetail.getMaterialNumber()) .setMaterialName(processBillMaterialsDetail.getMaterialName()) //需求数量 = (bom明细的需求数量 / bom订单的数量) * 排产工单计划生产数量 .setRequiredQuantity(processBillMaterialsDetail.getUsageQuantity() .divide(processBillMaterials.getProductionQuantity(), 2, RoundingMode.HALF_UP) .multiply(workOrder.getPlanQuantity())) .setActualQuantity(materialInventoryVo == null ? BigDecimal.ZERO : materialInventoryVo.getStockQuantity()) .setProductionUnit(processBillMaterialsDetail.getProductionUnit()); completenessCheckResultList.add(completenessCheckItem); } completenessCheckResultList.forEach(item -> { if (item.getRequiredQuantity().compareTo(item.getActualQuantity()) > 0) { item.setCheckFlag(CommonConstant.DEFAULT_0); } else { item.setCheckFlag(CommonConstant.DEFAULT_1); } }); return completenessCheckResultList; } }