package cn.iocoder.yudao.module.mes.service.pro.route;
|
|
import cn.hutool.core.util.StrUtil;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteFullExcelVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.*;
|
import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.process.MesProProcessMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.*;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.warehouse.*;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_FULL_EXCEL_INVALID;
|
|
/** 导出指定路线选中的模板产品及所需中间产出。 */
|
@Service
|
public class MesProRouteFullExcelService {
|
public static final String REFERENCE = "@模板产品";
|
@Resource private MesProRouteMapper routeMapper;
|
@Resource private MesProRouteProductMapper productMapper;
|
@Resource private MesProRouteProcessMapper processMapper;
|
@Resource private MesProRouteProcessOutputMapper outputMapper;
|
@Resource private MesProRouteProductBomMapper bomMapper;
|
@Resource private MesMdItemMapper itemMapper;
|
@Resource private MesMdItemTypeService itemTypeService;
|
@Resource private MesProProcessMapper operationMapper;
|
@Resource private MesWmWarehouseMapper warehouseMapper;
|
@Resource private MesWmWarehouseLocationMapper locationMapper;
|
@Resource private MesWmWarehouseAreaMapper areaMapper;
|
|
@Transactional(readOnly = true)
|
public List<MesProRouteFullExcelVO> exportRouteReference(Long routeId) {
|
MesProRouteDO selectedRoute = routeMapper.selectById(routeId);
|
if (selectedRoute == null) throw exception(PRO_ROUTE_FULL_EXCEL_INVALID, "工艺路线不存在");
|
List<MesProRouteFullExcelVO> rows = new ArrayList<>();
|
for (MesProRouteDO route : List.of(selectedRoute)) {
|
List<MesProRouteProductDO> allProducts = productMapper.selectListByRouteId(route.getId());
|
List<MesProRouteProductDO> refs = allProducts.stream().filter(p -> Boolean.TRUE.equals(p.getTemplateReference())).toList();
|
if (refs.size() > 1) throw exception(PRO_ROUTE_FULL_EXCEL_INVALID, "路线 " + route.getCode() + " 的模板引用不唯一");
|
if (refs.isEmpty()) throw exception(PRO_ROUTE_FULL_EXCEL_INVALID, "请先选择该路线的模板引用产品");
|
MesProRouteProductDO ref = refs.get(0);
|
if (!isFinished(ref.getItemId()))
|
throw exception(PRO_ROUTE_FULL_EXCEL_INVALID, "路线 " + route.getCode() + " 的模板引用须选择成品");
|
List<MesProRouteProcessDO> processes = processMapper.selectListByRouteId(route.getId());
|
List<MesProRouteProcessOutputDO> outputs = outputMapper.selectListByRouteId(route.getId());
|
Map<Long, List<MesProRouteProcessOutputDO>> byProcess = outputs.stream().collect(Collectors.groupingBy(MesProRouteProcessOutputDO::getRouteProcessId));
|
Set<Long> needed = new LinkedHashSet<>(); needed.add(ref.getItemId());
|
for (MesProRouteProcessDO process : processes) {
|
List<MesProRouteProcessOutputDO> part = byProcess.get(process.getId());
|
if (part != null && !part.isEmpty()) {
|
part.stream().filter(o -> o.getProductId() == null || o.getProductId().equals(ref.getItemId()))
|
.map(MesProRouteProcessOutputDO::getItemId).filter(Objects::nonNull).forEach(needed::add);
|
} else if (StrUtil.isNotBlank(process.getOutputItemId())) {
|
Set<Long> other = allProducts.stream().map(MesProRouteProductDO::getItemId)
|
.filter(id -> !id.equals(ref.getItemId()) && isFinished(id)).collect(Collectors.toSet());
|
for (String id : process.getOutputItemId().split(",")) if (StrUtil.isNotBlank(id)) {
|
Long itemId = Long.valueOf(id.trim()); if (!other.contains(itemId)) needed.add(itemId);
|
}
|
}
|
}
|
MesProRouteFullExcelVO header = row(route, "路线");
|
header.setRouteName(route.getName()); header.setRouteDescription(route.getDescription()); header.setRouteRemark(route.getRemark());
|
rows.add(header);
|
for (MesProRouteProductDO product : allProducts) {
|
if (!needed.contains(product.getItemId())) continue;
|
var warehouse = product.getWarehouseId() == null ? null : warehouseMapper.selectById(product.getWarehouseId());
|
var location = product.getLocationId() == null ? null : locationMapper.selectById(product.getLocationId());
|
var area = product.getAreaId() == null ? null : areaMapper.selectById(product.getAreaId());
|
MesProRouteFullExcelVO r = row(route, "产品");
|
r.setProductCode(itemCode(product.getItemId()));
|
r.setTemplateReference(product.getId().equals(ref.getId()) ? "是" : "否");
|
r.setWarehouseCode(warehouse == null ? null : warehouse.getCode());
|
r.setLocationCode(location == null ? null : location.getCode());
|
r.setAreaCode(area == null ? null : area.getCode());
|
r.setQuantity(product.getQuantity()); r.setProductionTime(product.getProductionTime());
|
r.setTimeUnitType(timeUnitName(product.getTimeUnitType())); r.setProductRemark(product.getRemark()); rows.add(r);
|
}
|
for (MesProRouteProcessDO process : processes) {
|
var operation = operationMapper.selectById(process.getProcessId());
|
if (operation == null) throw exception(PRO_ROUTE_FULL_EXCEL_INVALID, "路线 " + route.getCode() + " 的工序不存在");
|
MesProRouteFullExcelVO r = row(route, "工序"); r.setProcessCode(operation.getCode());
|
r.setProcessSort(process.getSort()); r.setLinkType(process.getLinkType()); r.setPrepareTime(process.getPrepareTime());
|
r.setWaitTime(process.getWaitTime()); r.setColorCode(process.getColorCode()); r.setKeyFlag(yn(process.getKeyFlag()));
|
r.setCheckFlag(yn(process.getCheckFlag())); r.setBackflushFlag(yn(process.getBackflushFlag())); r.setProcessRemark(process.getRemark()); rows.add(r);
|
List<MesProRouteProcessOutputDO> part = byProcess.get(process.getId());
|
if (part != null && !part.isEmpty()) {
|
for (var out : part) {
|
if (out.getProductId() != null && !out.getProductId().equals(ref.getItemId())) continue;
|
MesProRouteFullExcelVO o = row(route, "产出"); o.setProcessCode(operation.getCode());
|
o.setProductCode(out.getProductId() == null ? "" : REFERENCE);
|
o.setItemCode(Objects.equals(out.getItemId(), ref.getItemId()) ? REFERENCE : itemCode(out.getItemId()));
|
o.setOutputSort(out.getSort()); o.setDetailRemark(out.getRemark()); rows.add(o);
|
}
|
} else if (StrUtil.isNotBlank(process.getOutputItemId())) {
|
for (String id : process.getOutputItemId().split(",")) if (StrUtil.isNotBlank(id)) {
|
Long itemId = Long.valueOf(id.trim()); if (!needed.contains(itemId)) continue;
|
MesProRouteFullExcelVO o = row(route, "产出"); o.setProcessCode(operation.getCode());
|
o.setProductCode(itemId.equals(ref.getItemId()) ? REFERENCE : "");
|
o.setItemCode(itemId.equals(ref.getItemId()) ? REFERENCE : itemCode(itemId)); rows.add(o);
|
}
|
}
|
}
|
var boms = bomMapper.selectListByProductIds(route.getId(), needed);
|
for (MesProRouteProductBomDO bom : boms) {
|
var operation = operationMapper.selectById(bom.getProcessId());
|
if (operation == null) throw exception(PRO_ROUTE_FULL_EXCEL_INVALID, "投料的工序不存在");
|
MesProRouteFullExcelVO r = row(route, "投料"); r.setProcessCode(operation.getCode());
|
r.setProductCode(bom.getProductId().equals(ref.getItemId()) ? REFERENCE : itemCode(bom.getProductId()));
|
r.setItemCode(itemCode(bom.getItemId())); r.setBomQuantity(bom.getQuantity());
|
r.setDetailRemark(bom.getRemark()); rows.add(r);
|
}
|
}
|
return rows;
|
}
|
|
private MesProRouteFullExcelVO row(MesProRouteDO route, String type) {
|
MesProRouteFullExcelVO row = new MesProRouteFullExcelVO(); row.setRecordType(type); row.setRouteCode(route.getCode()); return row;
|
}
|
private String itemCode(Long id) {
|
MesMdItemDO item = itemMapper.selectById(id);
|
if (item == null) throw exception(PRO_ROUTE_FULL_EXCEL_INVALID, "引用的物料不存在:" + id);
|
return item.getCode();
|
}
|
private boolean isFinished(Long itemId) {
|
MesMdItemDO item = itemMapper.selectById(itemId);
|
if (item == null) throw exception(PRO_ROUTE_FULL_EXCEL_INVALID, "引用的物料不存在:" + itemId);
|
var category = itemTypeService.getItemType(item.getItemTypeId());
|
Set<Long> seen = new HashSet<>();
|
while (category != null && seen.add(category.getId())) {
|
if ("FG".equals(category.getCode())) return true;
|
if (category.getParentId() == null || category.getParentId() == 0L) break;
|
category = itemTypeService.getItemType(category.getParentId());
|
}
|
return false;
|
}
|
private String timeUnitName(String value) {
|
if (value == null) return null;
|
return switch (value) { case "MINUTE" -> "分钟"; case "HOUR" -> "小时"; case "DAY" -> "天"; default -> value; };
|
}
|
private String yn(Boolean value) { return Boolean.TRUE.equals(value) ? "是" : "否"; }
|
}
|