Compare commits

...

2 Commits

@ -17,6 +17,10 @@ public interface ErrorCodeConstants {
ErrorCode BOM_NOT_EXISTS = new ErrorCode(5_001, "产品BOM不存在");
ErrorCode BOM_EXISTS = new ErrorCode(5_002, "产品BOM已存在");
ErrorCode UNIT_ERROR = new ErrorCode(5_002, "物料bom单位不一致");
ErrorCode BOM_ONLY = new ErrorCode(5_002, "每个产品启用的BOM只能有一个请先禁用改产品其他BOM");
ErrorCode ORGANIZATION_NOT_EXISTS = new ErrorCode(5_0011, "产线工位不存在");
ErrorCode ORGANIZATION_ID_NOT_EXISTS = new ErrorCode(5_0011, "产线工位不存在");
ErrorCode ORGANIZATION_EXITS_CHILDREN = new ErrorCode(5_0012, "存在存在子产线工位,无法删除");

@ -34,9 +34,32 @@ public class DevicePlanGanttRespVO {
private LocalDateTime latestStartTime;
@Schema(description = "计划数量")
private Long planNumber;
@Schema(description = "计划编码")
private String planCode;
@Schema(description = "任务单编码")
private String taskCode;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "产品编码")
private String productCode;
@Schema(description = "设备名称")
private String deviceName;
@Schema(description = "设备编码")
private String deviceCode;
@Schema(description = "来源HISTORY-历史计划CURRENT-本次排产", example = "CURRENT")
private String sourceType;
@Schema(description = "计划开始时间(yyyy-MM-dd HH:mm:ss)")
private String planStartTimeStr;
@Schema(description = "计划结束时间(yyyy-MM-dd HH:mm:ss)")
private String planEndTimeStr;
@Schema(description = "最晚开工时间(yyyy-MM-dd HH:mm:ss)")
private String latestStartTimeStr;
@Schema(description = "交货日期(yyyy-MM-dd)")
private String deliveryDateStr;
}
}

@ -151,4 +151,10 @@ public class PlanRespVO {
@Schema(description = "设备名称")
private String deviceName;
@Schema(description = "最晚开工时间")
private LocalDateTime latestStartTime;
@Schema(description = "交货日期")
private LocalDateTime deliveryDate;
}

@ -34,8 +34,7 @@ import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.BOM_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.BOM_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
/**
* BOM Service
@ -91,7 +90,7 @@ public class BomServiceImpl implements BomService {
if (updateReqVO.getIsEnable()) {
BomDO temp = bomMapper.selectByProductId(updateReqVO.getProductId());
if (temp != null && !Objects.equals(temp.getId(), updateReqVO.getId())) {
throw exception(new ErrorCode(500, "每个产品启用的BOM只能有一个请先禁用改产品其他BOM"));
throw exception((BOM_ONLY));
}
}
// 校验存在

@ -32,6 +32,7 @@ import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.UNIT_ERROR;
/**
* Service
@ -42,7 +43,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
@Validated
public class ItemAnalysisServiceImpl implements ItemAnalysisService {
private static final Logger log = LoggerFactory.getLogger(ItemAnalysisServiceImpl.class);
private static final String UNIT_ERROR = "物料bom单位不一致";
// private static final String UNIT_ERROR = "物料bom单位不一致";
@Resource
private ErpStockService erpStockService;
@Resource
@ -64,32 +65,53 @@ public class ItemAnalysisServiceImpl implements ItemAnalysisService {
public List<ItemRequisitionAndStock> getItemAnalysis(TaskDO taskDO) {
List<TaskDetailDO> detailDOList = taskService.getTaskDetailListByTaskId(taskDO.getId());
Map<Long, ItemRequisitionAndStock> itemMap = new HashMap<>();
//算物料需求
for (TaskDetailDO detail : detailDOList) {
//这里要求销售的单位和物料单位一致等于内置单位
// BomDO bomDO = bomService.selectByProductId(detail.getProductId());
// if (!bomDO.getUnitId().equals(ProductUnitEnum.Each.getUnitId())
// || !detail.getUnitId().equals(ProductUnitEnum.Each.getUnitId())) {
// log.error(UNIT_ERROR);
// throw exception(new ErrorCode(500, UNIT_ERROR));
// }
List<ItemRequisitionAndStock> itemList = new ArrayList<>();
//每个taskDetail计算一次物料需求汇总到map里面
// 算物料需求:逐条追加,不合并
for (TaskDetailDO detail : detailDOList) {
List<BomDetailDO> bomDetailDOList =
bomService.getBomDetailListByProductId(detail.getProductId(), detail.getNumber());
buildItemMap(itemMap, bomDetailDOList);
buildItemList(itemList, bomDetailDOList, detail.getProductId());
}
//算库存
for (Long key : itemMap.keySet()) {
BigDecimal stockCount = erpStockService.getStockCount(key);
itemMap.get(key).setStockNumber(stockCount);
BigDecimal stockWorkshopCount = stockWorkshopService.getStockCount(key);
itemMap.get(key).setStockWorkshopNumber(stockWorkshopCount);
// 算库存:每条都查并赋值(不合并情况下可能重复物料)
for (ItemRequisitionAndStock item : itemList) {
BigDecimal stockCount = erpStockService.getStockCount(item.getItemId());
item.setStockNumber(stockCount);
BigDecimal stockWorkshopCount = stockWorkshopService.getStockCount(item.getItemId());
item.setStockWorkshopNumber(stockWorkshopCount);
}
List<ItemRequisitionAndStock> list = new ArrayList<>(itemMap.values());
list = buildDetailVOList(list);
return list;
return buildDetailVOList(itemList);
// ============ 合并子物料版本 ===========
// List<TaskDetailDO> detailDOList = taskService.getTaskDetailListByTaskId(taskDO.getId());
// Map<Long, ItemRequisitionAndStock> itemMap = new HashMap<>();
// //算物料需求
// for (TaskDetailDO detail : detailDOList) {
// //这里要求销售的单位和物料单位一致等于内置单位
//// BomDO bomDO = bomService.selectByProductId(detail.getProductId());
//// if (!bomDO.getUnitId().equals(ProductUnitEnum.Each.getUnitId())
//// || !detail.getUnitId().equals(ProductUnitEnum.Each.getUnitId())) {
//// log.error(UNIT_ERROR);
//// throw exception(new ErrorCode(500, UNIT_ERROR));
//// }
//
// //每个taskDetail计算一次物料需求汇总到map里面
// List<BomDetailDO> bomDetailDOList =
// bomService.getBomDetailListByProductId(detail.getProductId(), detail.getNumber());
// buildItemMap(itemMap, bomDetailDOList,detail.getProductId());
// }
// //算库存
// for (Long key : itemMap.keySet()) {
// BigDecimal stockCount = erpStockService.getStockCount(key);
// itemMap.get(key).setStockNumber(stockCount);
// BigDecimal stockWorkshopCount = stockWorkshopService.getStockCount(key);
// itemMap.get(key).setStockWorkshopNumber(stockWorkshopCount);
// }
// List<ItemRequisitionAndStock> list = new ArrayList<>(itemMap.values());
// list = buildDetailVOList(list);
// return list;
}
//分析计划的物料需求
@ -135,13 +157,13 @@ public class ItemAnalysisServiceImpl implements ItemAnalysisService {
BomDO bomDO = bomService.selectByProductId(detail.getProductId());
if (!bomDO.getUnitId().equals(ProductUnitEnum.Each.getUnitId())
|| !detail.getProductUnitId().equals(ProductUnitEnum.Each.getUnitId())) {
log.error(UNIT_ERROR);
throw exception(new ErrorCode(500, UNIT_ERROR));
log.error(String.valueOf(UNIT_ERROR));
throw exception(UNIT_ERROR);
}
//每个detail计算一次物料需求汇总到map里面
List<BomDetailDO> bomDetailDOList =
bomService.getBomDetailListByProductId(detail.getProductId(), detail.getCount().longValue());
buildItemMap(itemMap, bomDetailDOList);
buildItemMap(itemMap, bomDetailDOList,detail.getProductId());
}
//算库存
for (Long key : itemMap.keySet()) {
@ -173,22 +195,118 @@ public class ItemAnalysisServiceImpl implements ItemAnalysisService {
});
}
private void buildItemMap(Map<Long, ItemRequisitionAndStock> itemMap, List<BomDetailDO> bomDetailDOList) {
private void buildItemList(List<ItemRequisitionAndStock> itemList,
List<BomDetailDO> bomDetailDOList,
Long currentProductId) {
for (BomDetailDO bomDetail : bomDetailDOList) {
if (itemMap.containsKey(bomDetail.getProductId())) {
ItemRequisitionAndStock temp = itemMap.get(bomDetail.getProductId());
//比较单位是否一致
Long itemId = bomDetail.getProductId();
// 可选:仅做单位冲突校验(跨产品同子物料)
// for (ItemRequisitionAndStock existed : itemList) {
// if (!Objects.equals(existed.getItemId(), itemId)) {
// continue;
// }
// if (!Objects.equals(existed.getUnitId(), bomDetail.getUnitId())) {
// BomDO currBom = bomService.getBom(bomDetail.getBomId());
// BomDO prevBom = existed.getSourceBomId() == null ? null : bomService.getBom(existed.getSourceBomId());
//
// ErpProductDO subItem = productService.getProduct(itemId);
// ErpProductDO currProduct = currentProductId == null ? null : productService.getProduct(currentProductId);
// ErpProductDO prevProduct = existed.getSourceProductId() == null ? null : productService.getProduct(existed.getSourceProductId());
//
// ErpProductUnitDO currUnit = productUnitService.getProductUnit(bomDetail.getUnitId());
// ErpProductUnitDO prevUnit = productUnitService.getProductUnit(existed.getUnitId());
//
// String currProductCode = currProduct == null ? "-" : Objects.toString(currProduct.getBarCode(), "-");
// String currProductName = currProduct == null ? "-" : Objects.toString(currProduct.getName(), "-");
// String prevProductCode = prevProduct == null ? "-" : Objects.toString(prevProduct.getBarCode(), "-");
// String prevProductName = prevProduct == null ? "-" : Objects.toString(prevProduct.getName(), "-");
//
// String currBomCode = currBom == null ? "-" : Objects.toString(currBom.getCode(), "-");
// String prevBomCode = prevBom == null ? "-" : Objects.toString(prevBom.getCode(), "-");
//
// String subItemCode = subItem == null ? "-" : Objects.toString(subItem.getBarCode(), "-");
// String subItemName = subItem == null ? "-" : Objects.toString(subItem.getName(), "-");
//
// String currUnitName = currUnit == null ? "-" : Objects.toString(currUnit.getName(), "-");
// String prevUnitName = prevUnit == null ? "-" : Objects.toString(prevUnit.getName(), "-");
//
// String msg = String.format(
// "产品[%s-%s] 的 BOM[%s] 中,子物料[%s-%s] 单位[%s] 与 产品[%s-%s] 的 BOM[%s] 中子物料单位[%s] 冲突!应改成一致的单位",
// currProductCode, currProductName, currBomCode,
// subItemCode, subItemName, currUnitName,
// prevProductCode, prevProductName, prevBomCode, prevUnitName
// );
// log.error(msg);
// throw exception(new ErrorCode(5_002, msg));
// }
// }
// 不合并:每条 BOM 明细生成一条记录
ItemRequisitionAndStock temp = new ItemRequisitionAndStock()
.setItemId(itemId)
.setNumber(bomDetail.getUsageNumber())
.setUnitId(bomDetail.getUnitId())
.setSourceBomId(bomDetail.getBomId())
.setSourceProductId(currentProductId);
itemList.add(temp);
}
}
private void buildItemMap(Map<Long, ItemRequisitionAndStock> itemMap,
List<BomDetailDO> bomDetailDOList,
Long currentProductId) {
for (BomDetailDO bomDetail : bomDetailDOList) {
Long itemId = bomDetail.getProductId();
if (itemMap.containsKey(itemId)) {
ItemRequisitionAndStock temp = itemMap.get(itemId);
if (!temp.getUnitId().equals(bomDetail.getUnitId())) {
log.error(UNIT_ERROR);
throw exception(new ErrorCode(500, UNIT_ERROR));
BomDO currBom = bomService.getBom(bomDetail.getBomId());
BomDO prevBom = temp.getSourceBomId() == null ? null : bomService.getBom(temp.getSourceBomId());
ErpProductDO subItem = productService.getProduct(itemId);
ErpProductDO currProduct = currentProductId == null ? null : productService.getProduct(currentProductId);
ErpProductDO prevProduct = temp.getSourceProductId() == null ? null : productService.getProduct(temp.getSourceProductId());
ErpProductUnitDO currUnit = productUnitService.getProductUnit(bomDetail.getUnitId());
ErpProductUnitDO prevUnit = productUnitService.getProductUnit(temp.getUnitId());
String currProductCode = currProduct == null ? "-" : Objects.toString(currProduct.getBarCode(), "-");
String currProductName = currProduct == null ? "-" : Objects.toString(currProduct.getName(), "-");
String prevProductCode = prevProduct == null ? "-" : Objects.toString(prevProduct.getBarCode(), "-");
String prevProductName = prevProduct == null ? "-" : Objects.toString(prevProduct.getName(), "-");
String currBomCode = currBom == null ? "-" : Objects.toString(currBom.getCode(), "-");
String prevBomCode = prevBom == null ? "-" : Objects.toString(prevBom.getCode(), "-");
String subItemCode = subItem == null ? "-" : Objects.toString(subItem.getBarCode(), "-");
String subItemName = subItem == null ? "-" : Objects.toString(subItem.getName(), "-");
String currUnitName = currUnit == null ? "-" : Objects.toString(currUnit.getName(), "-");
String prevUnitName = prevUnit == null ? "-" : Objects.toString(prevUnit.getName(), "-");
String msg = String.format(
"产品[%s-%s] 的 BOM[%s] 中,子物料[%s-%s] 单位[%s] 与 产品[%s-%s] 的 BOM[%s] 中子物料单位[%s] 冲突!应改成一致的单位",
currProductCode, currProductName, currBomCode,
subItemCode, subItemName, currUnitName,
prevProductCode, prevProductName, prevBomCode, prevUnitName
);
log.error(msg);
throw exception(new ErrorCode(5_002, msg));
}
temp.setNumber(temp.getNumber().add(bomDetail.getUsageNumber()));
} else {
ItemRequisitionAndStock temp = new ItemRequisitionAndStock()
.setItemId(bomDetail.getProductId())
.setItemId(itemId)
.setNumber(bomDetail.getUsageNumber())
.setUnitId(bomDetail.getUnitId());
itemMap.put(bomDetail.getProductId(), temp);
.setUnitId(bomDetail.getUnitId())
.setSourceBomId(bomDetail.getBomId())
.setSourceProductId(currentProductId);
itemMap.put(itemId, temp);
}
}
}

@ -47,5 +47,12 @@ public class ItemRequisitionAndStock {
*
*/
private BigDecimal stockWorkshopNumber;
/**
* BOM
*/
private Long sourceBomId;
/**
*
*/
private Long sourceProductId;
}

@ -30,6 +30,7 @@ import cn.iocoder.yudao.module.mes.dal.mysql.deviceledger.DeviceLedgerMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.paigongrecord.PaigongRecordMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.plan.PlanMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.stockindetail.StockInDetailMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.task.TaskMapper;
import cn.iocoder.yudao.module.mes.dal.redis.no.MesNoRedisDAO;
import cn.iocoder.yudao.module.mes.service.itemrequisition.ItemAnalysisService;
import cn.iocoder.yudao.module.mes.service.itemrequisition.ItemRequisitionService;
@ -37,6 +38,7 @@ import cn.iocoder.yudao.module.mes.service.itemrequisition.entity.ItemRequisitio
import cn.iocoder.yudao.module.mes.service.organization.OrganizationService;
import cn.iocoder.yudao.module.mes.service.paigongrecord.PaigongRecordService;
import cn.iocoder.yudao.module.mes.service.task.TaskService;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -107,6 +109,9 @@ public class PlanServiceImpl implements PlanService {
@Lazy
private ErpProductService erpProductService;
@Resource
@Lazy
private TaskMapper taskMapper;
@Override
@ -286,11 +291,30 @@ public class PlanServiceImpl implements PlanService {
}
@Override
public PlanRespVO getPlanRespVO(PlanDO planDO) {
PlanRespVO planRespVO = BeanUtils.toBean(planDO, PlanRespVO.class);
planRespVO.setProductName(productService.getProduct(planDO.getProductId()).getName());
planRespVO.setTaskCode(taskService.getTask(planDO.getTaskId()).getCode());
planRespVO.setWorker(userService.getUser(planDO.getWorkerId()).getUsername());
planRespVO.setFeedingPipelineName(organizationService.getOrganization(planDO.getFeedingPipeline()).getName());
PlanRespVO planRespVO = BeanUtils.toBean(planDO, PlanRespVO.class);
if (planDO.getProductId() != null) {
ErpProductDO product = productService.getProduct(planDO.getProductId());
planRespVO.setProductName(product == null ? null : product.getName());
}
if (planDO.getTaskId() != null) {
TaskDO task = taskService.getTask(planDO.getTaskId());
planRespVO.setTaskCode(task == null ? null : task.getCode());
}
if (planDO.getWorkerId() != null) {
AdminUserDO user = userService.getUser(planDO.getWorkerId());
planRespVO.setWorker(user == null ? null : user.getUsername());
} else {
planRespVO.setWorker(null);
}
if (planDO.getFeedingPipeline() != null) {
OrganizationDO organization = organizationService.getOrganization(planDO.getFeedingPipeline());
planRespVO.setFeedingPipelineName(organization == null ? null : organization.getName());
}
return planRespVO;
}
@ -538,14 +562,24 @@ public class PlanServiceImpl implements PlanService {
Set<Long> deviceIds = planList.stream().map(PlanDO::getDeviceId).filter(Objects::nonNull).collect(Collectors.toSet());
Set<Long> productIds = planList.stream().map(PlanDO::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
Set<Long> taskIds = planList.stream().map(PlanDO::getTaskId).filter(Objects::nonNull).collect(Collectors.toSet());
Map<Long, DeviceLedgerDO> deviceMap = deviceLedgerMapper.selectBatchIds(deviceIds).stream()
.collect(Collectors.toMap(DeviceLedgerDO::getId, d -> d, (a, b) -> a));
Map<Long, ErpProductDO> productMap = erpProductService.getProductMap(productIds);
Map<Long, String> taskCodeMap = CollUtil.isEmpty(taskIds)
? Collections.emptyMap()
: taskMapper.selectBatchIds(taskIds).stream()
.collect(Collectors.toMap(TaskDO::getId, TaskDO::getCode, (a, b) -> a));
Map<Long, List<PlanDO>> group = planList.stream()
.collect(Collectors.groupingBy(PlanDO::getDeviceId, LinkedHashMap::new, Collectors.toList()));
List<DevicePlanGanttRespVO> result = new ArrayList<>();
for (Map.Entry<Long, List<PlanDO>> entry : group.entrySet()) {
Long deviceId = entry.getKey();
@ -557,9 +591,13 @@ public class PlanServiceImpl implements PlanService {
vo.setDeviceCode(device == null ? null : device.getDeviceCode());
List<DevicePlanGanttRespVO.PlanItem> plans = new ArrayList<>();
DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
for (PlanDO plan : entry.getValue()) {
DevicePlanGanttRespVO.PlanItem item = new DevicePlanGanttRespVO.PlanItem();
item.setPlanId(plan.getId());
item.setPlanCode(plan.getCode());
item.setTaskCode(taskCodeMap.get(plan.getTaskId()));
item.setPlanStartTime(plan.getPlanStartTime());
item.setPlanEndTime(plan.getPlanEndTime());
item.setLatestStartTime(plan.getLatestStartTime()); // 前提PlanDO已加该字段
@ -567,6 +605,13 @@ public class PlanServiceImpl implements PlanService {
ErpProductDO product = productMap.get(plan.getProductId());
item.setProductName(product == null ? null : product.getName());
item.setDeviceName(device == null ? null : device.getDeviceName());
item.setProductCode(product == null ? null : product.getBarCode());
item.setDeviceCode(device == null ? null : device.getDeviceCode());
item.setSourceType("HISTORY");
item.setPlanStartTimeStr(plan.getPlanStartTime().format(DATETIME_FMT));
item.setPlanEndTimeStr(plan.getPlanEndTime().format(DATETIME_FMT));
item.setLatestStartTimeStr(plan.getLatestStartTime().format(DATETIME_FMT));
plans.add(item);
}
vo.setPlans(plans);

@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.mes.strategy.task;
import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.PlanSaveReqVO;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
@Component
public class Rule1NearDueDateStrategy implements ScheduleSortStrategy {
@Override
public Integer ruleCode() {
return 1;
}
@Override
public void sort(List<PlanSaveReqVO> plans) {
if (plans == null || plans.size() <= 1) {
return;
}
plans.sort(
Comparator
// 第一排序:最晚开工时间 ASC
.comparing((PlanSaveReqVO p) -> nvlTime(p.getLatestStartTime()))
// 第二排序:订单交期 ASC
.thenComparing(p -> nvlTime(p.getDeliveryDate()))
// 第三排序:订单优先级 DESC
.thenComparing((PlanSaveReqVO p) -> nvlInt(p.getOrderPriority()), Comparator.reverseOrder())
// 第四排序:订单明细交期时间 ASC
.thenComparing(p -> nvlTime(p.getOrderDetailDeliveryDate()))
// 第五排序:订单号 ASC这里用任务单号 workOrderCode 作为订单号)
.thenComparing(p -> nvlStr(p.getWorkOrderCode()))
// 第六排序:明细行号 ASC这里用订单明细Id代替
.thenComparing(p -> nvlLong(p.getOrderDetailId()))
);
}
private LocalDateTime nvlTime(LocalDateTime value) {
return value != null ? value : LocalDateTime.MAX;
}
private Integer nvlInt(Integer value) {
// DESC 排序里null 放最后:给最小值
return value != null ? value : Integer.MIN_VALUE;
}
private Long nvlLong(Long value) {
return value != null ? value : Long.MAX_VALUE;
}
private String nvlStr(String value) {
return value != null ? value : "";
}
}
Loading…
Cancel
Save