From 35b42db401542f3a85b2670873363ad4025da3d4 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期五, 31 七月 2026 10:55:48 +0800
Subject: [PATCH] 排班计划提交修改
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java | 259 ++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 210 insertions(+), 49 deletions(-)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
index c2d586c..a44b286 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
@@ -12,14 +12,24 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProWorkOrderBomSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
+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.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
+import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderItemRespDTO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
-import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -31,6 +41,7 @@
import java.math.BigDecimal;
import java.util.*;
+import java.util.Comparator;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -44,13 +55,23 @@
@Resource
private MesProWorkOrderBomService workOrderBomService;
@Resource
+ private MesProWorkOrderService workOrderService;
+ @Resource
+ private MesProWorkOrderProcessService workOrderProcessService;
+ @Resource
private MesMdItemService itemService;
@Resource
private MesMdUnitMeasureService unitMeasureService;
@Resource
private MesMdItemTypeService itemTypeService;
@Resource
- private MesMdProductBomService productBomService;
+ private MesProRouteProductService routeProductService;
+ @Resource
+ private MesProRouteProductBomService routeProductBomService;
+ @Resource
+ private MesWmMaterialStockMapper materialStockMapper;
+ @Resource
+ private ErpPurchaseOrderApi purchaseOrderApi;
@PostMapping("/create")
@Operation(summary = "鍒涘缓宸ュ崟 BOM")
@@ -102,40 +123,67 @@
@PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
public CommonResult<List<MesProWorkOrderItemRespVO>> getWorkOrderBomItemListByWorkOrderId(
@RequestParam("workOrderId") Long workOrderId) {
- // 1. 鑾峰彇宸ュ崟 BOM 琛�
+ // 1. 鑾峰彇宸ュ崟
+ MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(workOrderId);
+ if (workOrder == null) {
+ return success(Collections.emptyList());
+ }
+
+ // 2. 鑾峰彇宸ュ崟宸ュ簭鍒楄〃锛堝惈鍚勫伐搴忕殑鎶曟枡鏄庣粏涓庝骇鍑哄搧锛�
+ List<MesProWorkOrderProcessDO> processList = workOrderProcessService
+ .getWorkOrderProcessListByWorkOrderId(workOrderId);
+ if (CollUtil.isEmpty(processList)) {
+
+ return success(Collections.emptyList());
+ }
+
+ // 3. 鑾峰彇宸ュ崟 BOM 琛岋紙鍚勫伐搴忕殑鎶曟枡璁板綍锛屾寜 processId 鍒嗙粍浣跨敤锛�
List<MesProWorkOrderBomDO> bomList = workOrderBomService.getWorkOrderBomListByWorkOrderId(workOrderId);
if (CollUtil.isEmpty(bomList)) {
return success(Collections.emptyList());
}
- // 2. 閫愬眰灞曞紑锛屽緱鍒板彾瀛愮墿鏂� itemId 鈫� quantity
- Map<Long, BigDecimal> leafItems = buildWorkOrderItems(bomList);
+ // 4. 閫氳繃浜у搧ID鍙嶆煡宸ヨ壓璺嚎锛堝伐鍗� routeId 鍙兘涓虹┖锛�
+ Long routeId = null;
+ if (workOrder.getProductId() != null) {
+ MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(workOrder.getProductId());
+ if (routeProduct != null) {
+ routeId = routeProduct.getRouteId();
+ }
+ }
+
+ // 5. 鎸夊伐搴忎骇鍑洪摼閫愬眰灞曞紑 BOM锛屽緱鍒板彾瀛愮墿鏂欙紙姣忓崟浣嶆垚鍝佺殑鐢ㄩ噺锛�
+ Map<Long, BigDecimal> leafItems = buildWorkOrderItems(processList, bomList, routeId);
if (CollUtil.isEmpty(leafItems)) {
return success(Collections.emptyList());
}
- // 3. 鎷兼帴 VO 瀛楁
- Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(leafItems.keySet());
- Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
- convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
- Map<Long, MesMdItemTypeDO> itemTypeMap = itemTypeService.getItemTypeMap(
- convertSet(itemMap.values(), MesMdItemDO::getItemTypeId));
- List<MesProWorkOrderItemRespVO> result = new ArrayList<>(leafItems.size());
+ // 6. 涔樹互宸ュ崟鐢熶骇鏁伴噺锛屽緱鍒版�婚渶姹�
+ BigDecimal orderQty = workOrder.getQuantity() != null ? workOrder.getQuantity() : BigDecimal.ONE;
+ Map<Long, BigDecimal> totalRequirements = new LinkedHashMap<>();
for (Map.Entry<Long, BigDecimal> entry : leafItems.entrySet()) {
- MesMdItemDO item = itemMap.get(entry.getKey());
- if (item == null) {
- continue;
- }
- MesProWorkOrderItemRespVO vo = new MesProWorkOrderItemRespVO()
- .setItemId(item.getId()).setQuantity(entry.getValue())
- .setItemCode(item.getCode()).setItemName(item.getName()).setItemSpecification(item.getSpecification());
- MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(),
- unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
- MapUtils.findAndThen(itemTypeMap, item.getItemTypeId(),
- itemType -> vo.setItemOrProduct(itemType.getItemOrProduct()));
- result.add(vo);
+ totalRequirements.put(entry.getKey(), entry.getValue().multiply(orderQty));
}
- return success(result);
+
+ // 7. 鏌ヨ鏉ユ簮涓烘湰宸ュ崟鐨勯噰璐鍗曟槑缁嗭紝鎸変骇鍝佹眹鎬诲湪閫旈噰璐暟
+ // sourceType=1 琛ㄧず鏉ユ簮绫诲瀷涓�"鐢熶骇宸ュ崟"
+ Map<Long, BigDecimal> purchaseOrderQtyMap = new HashMap<>();
+ List<ErpPurchaseOrderItemRespDTO> poItems = purchaseOrderApi
+ .getPurchaseOrderItemListBySource(1, workOrderId)
+ .getCheckedData();
+ if (CollUtil.isNotEmpty(poItems)) {
+ for (ErpPurchaseOrderItemRespDTO poItem : poItems) {
+ // 鍦ㄩ�� = 璁㈣喘鏁伴噺 - 宸插叆搴撴暟閲�
+ BigDecimal inCount = poItem.getInCount() != null ? poItem.getInCount() : BigDecimal.ZERO;
+ BigDecimal inTransit = poItem.getCount().subtract(inCount);
+ if (inTransit.compareTo(BigDecimal.ZERO) > 0) {
+ purchaseOrderQtyMap.merge(poItem.getProductId(), inTransit, BigDecimal::add);
+ }
+ }
+ }
+
+ // 8. 鏌ヨ搴撳瓨锛屾嫾鎺� VO
+ return success(buildItemRespVOList(totalRequirements, purchaseOrderQtyMap));
}
// ==================== 鎷兼帴 VO ====================
@@ -164,47 +212,160 @@
});
}
- // ==================== 鐗╂枡闇�姹傞�愬眰灞曞紑 ====================
+ // ==================== 鐗╂枡闇�姹傚睍寮�锛堝伐搴忎骇鍑洪摼 + 璺敱浜у搧 BOM锛� ====================
/**
- * 閫愬眰 IN 鏌ヨ灞曞紑宸ュ崟 BOM 鍒板彾瀛愮墿鏂欙紝鍚堝苟鍚岀墿鏂欑殑闇�姹傛暟閲�
+ * 鎸夊伐搴忎骇鍑洪摼閫愬眰灞曞紑鐗╂枡闇�姹傘��
*
- * @param bomList 宸ュ崟 BOM 鍒楄〃
- * @return 鍙跺瓙鐗╂枡 Map锛歩temId 鈫� 鍚堝苟鍚庣殑闇�姹傛暟閲�
+ * <p><b>鏍稿績閫昏緫锛�</b></p>
+ * <ol>
+ * <li>鎵惧嚭鍏抽敭宸ュ簭锛坘eyFlag=true锛夛紝浠ュ畠鐨勬姇鏂欎负灞曞紑璧风偣</li>
+ * <li>鏋勫缓浜у嚭鍝佲啋宸ュ簭鎶曟枡鐨勬槧灏勶紙{@code outputItemId 鈫� per-unit bomItems}锛�</li>
+ * <li>閫愬眰灞曞紑锛氳嫢鐗╂枡鏄煇宸ュ簭鐨勪骇鍑哄搧 鈫� 鐢ㄨ宸ュ簭鐨勬姇鏂欓厤鏂规浛浠o紱
+ * 鍚﹀垯鏌ヨ矾鐢变骇鍝� BOM 鈫� 鐢� BOM 瀛愰」鏇夸唬锛�
+ * 涓よ�呴兘娌℃湁 鈫� 鍙跺瓙鐗╂枡锛堝璐欢锛夛紝绱姞鍒扮粨鏋�</li>
+ * </ol>
+ *
+ * <p>杩欐牱閬垮厤浜嗘妸涓棿宸ュ簭鐨勬姇鏂欑洿鎺ョ撼鍏ヨ捣鐐瑰鑷寸殑閲嶅璁$畻闂銆�</p>
+ *
+ * @param processList 宸ュ崟宸ュ簭鍒楄〃锛堟寜 sort 鍗囧簭锛�
+ * @param bomList 宸ュ崟 BOM 琛岋紙鎵�鏈夊伐搴忕殑鎶曟枡鏄庣粏锛�
+ * @param routeId 宸ヨ壓璺嚎缂栧彿锛堝彲涓� null锛宯ull 鏃惰烦杩囪矾鐢� BOM 鏌ヨ锛�
+ * @return 鍙跺瓙鐗╂枡 Map锛歩temId 鈫� 闇�姹傛暟閲�
*/
- private Map<Long, BigDecimal> buildWorkOrderItems(List<MesProWorkOrderBomDO> bomList) {
- // 1. 鍒濆鍖栵細鏀堕泦绗竴灞傜墿鏂� ID 鍜屽搴旀暟閲�
- Map<Long, BigDecimal> currentLayer = new LinkedHashMap<>();
- for (MesProWorkOrderBomDO bom : bomList) {
- currentLayer.merge(bom.getItemId(), bom.getQuantity(), BigDecimal::add);
+ private Map<Long, BigDecimal> buildWorkOrderItems(List<MesProWorkOrderProcessDO> processList,
+ List<MesProWorkOrderBomDO> bomList,
+ Long routeId) {
+ // 1. 鏋勫缓浜у嚭鍝� 鈫� 宸ュ簭鎶曟枡閰嶆柟锛坧er-unit锛�
+ // outputItemId 鈫� [{itemId, quantity}, ...]
+ Map<Long, List<MesProWorkOrderBomDO>> processBomMap = new HashMap<>();
+ for (MesProWorkOrderProcessDO process : processList) {
+ if (process.getOutputItemId() == null) {
+ continue;
+ }
+ List<MesProWorkOrderBomDO> items = new ArrayList<>();
+ for (MesProWorkOrderBomDO bom : bomList) {
+ if (bom.getProcessId().equals(process.getProcessId())) {
+ items.add(bom);
+ }
+ }
+ processBomMap.put(process.getOutputItemId(), items);
}
- // 2. 閫愬眰 IN 鏌ヨ灞曞紑 BOM
+ // 2. 鍙栨渶鍚庝竴閬撳伐搴忥紙sort 鏈�澶э級浣滀负灞曞紑璧风偣锛岄�愬眰鍚戝墠閫掓帹
+ MesProWorkOrderProcessDO lastProcess = processList.stream()
+ .max(Comparator.comparing(MesProWorkOrderProcessDO::getSort))
+ .orElse(null);
+ if (lastProcess == null) {
+ return Collections.emptyMap();
+ }
+
+ // 3. 浠ユ渶鍚庝竴閬撳伐搴忕殑鎶曟枡涓鸿捣鐐�
+ Map<Long, BigDecimal> currentLayer = new LinkedHashMap<>();
+ for (MesProWorkOrderBomDO bom : bomList) {
+ if (bom.getProcessId().equals(lastProcess.getProcessId())) {
+ currentLayer.merge(bom.getItemId(), bom.getQuantity(), BigDecimal::add);
+ }
+ }
+ if (CollUtil.isEmpty(currentLayer)) {
+ return Collections.emptyMap();
+ }
+
+ // 4. 閫愬眰灞曞紑
Map<Long, BigDecimal> leafItems = new LinkedHashMap<>();
for (int i = 0; i < Byte.MAX_VALUE && CollUtil.isNotEmpty(currentLayer); i++) {
- // 2.1 鎵归噺鏌ヨ褰撳墠灞傛墍鏈夌墿鏂欑殑瀛� BOM
- List<MesMdProductBomDO> subBomList = productBomService.getProductBomListByItemIds(currentLayer.keySet());
- // 2.2 鎸� itemId 鍒嗙粍
- Map<Long, List<MesMdProductBomDO>> subBomMap = convertMultiMap(subBomList, MesMdProductBomDO::getItemId);
- // 2.3 鍖哄垎鍙跺瓙鑺傜偣鍜岄潪鍙跺瓙鑺傜偣
Map<Long, BigDecimal> nextLayer = new LinkedHashMap<>();
+
+ // 4.1 鎵归噺鏌ヨ璺敱浜у搧 BOM锛堢敤浜庨潪宸ュ簭浜у嚭鐨勭墿鏂欏睍寮�锛�
+ Map<Long, List<MesProRouteProductBomDO>> routeSubBomMap;
+ if (routeId != null) {
+ List<MesProRouteProductBomDO> routeSubBomList = routeProductBomService
+ .getRouteProductBomListByProductIds(routeId, currentLayer.keySet());
+ routeSubBomMap = convertMultiMap(routeSubBomList, MesProRouteProductBomDO::getProductId);
+ } else {
+ routeSubBomMap = Collections.emptyMap();
+ }
+
for (Map.Entry<Long, BigDecimal> entry : currentLayer.entrySet()) {
Long itemId = entry.getKey();
- BigDecimal quantity = entry.getValue();
- List<MesMdProductBomDO> children = subBomMap.get(itemId);
- // 鎯呭喌涓�锛氬彾瀛愯妭鐐癸紝绱姞鍒扮粨鏋�
- if (CollUtil.isEmpty(children)) {
- leafItems.merge(itemId, quantity, BigDecimal::add);
+ BigDecimal qty = entry.getValue();
+
+ // 鎯呭喌涓�锛氱墿鏂欐槸鏌愬伐搴忕殑浜у嚭鍝� 鈫� 鐢ㄥ伐搴忛厤鏂规浛浠�
+ List<MesProWorkOrderBomDO> processBom = processBomMap.get(itemId);
+ if (CollUtil.isNotEmpty(processBom)) {
+ for (MesProWorkOrderBomDO child : processBom) {
+ nextLayer.merge(child.getItemId(), qty.multiply(child.getQuantity()), BigDecimal::add);
+ }
continue;
}
- // 鎯呭喌浜岋細闈炲彾瀛愶紝瀛愮墿鏂欒繘鍏ヤ笅涓�灞�
- for (MesMdProductBomDO child : children) {
- nextLayer.merge(child.getBomItemId(), quantity.multiply(child.getQuantity()), BigDecimal::add);
+
+ // 鎯呭喌浜岋細璺敱浜у搧 BOM 涓湁涓嬬骇 鈫� 灞曞紑
+ List<MesProRouteProductBomDO> routeChildren = routeSubBomMap.get(itemId);
+ if (CollUtil.isNotEmpty(routeChildren)) {
+ for (MesProRouteProductBomDO child : routeChildren) {
+ nextLayer.merge(child.getItemId(), qty.multiply(child.getQuantity()), BigDecimal::add);
+ }
+ continue;
}
+
+ // 鎯呭喌涓夛細鍙跺瓙鐗╂枡
+ leafItems.merge(itemId, qty, BigDecimal::add);
}
currentLayer = nextLayer;
}
return leafItems;
}
+ // ==================== 鎷兼帴鐗╂枡闇�姹� VO锛堝惈搴撳瓨鏍¢獙锛� ====================
+
+ private List<MesProWorkOrderItemRespVO> buildItemRespVOList(Map<Long, BigDecimal> requirements,
+ Map<Long, BigDecimal> purchaseOrderQtyMap) {
+ // 1. 鎵归噺鑾峰彇鐗╂枡鍩虹淇℃伅
+ Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(requirements.keySet());
+ Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
+ convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
+ Map<Long, MesMdItemTypeDO> itemTypeMap = itemTypeService.getItemTypeMap(
+ convertSet(itemMap.values(), MesMdItemDO::getItemTypeId));
+
+ // 2. 鎵归噺鏌ヨ搴撳瓨锛屾寜 itemId 姹囨�诲彲鐢ㄩ噺锛堝湪搴� - 鍐荤粨 - 鍗犵敤锛�
+ Map<Long, BigDecimal> stockMap = new HashMap<>();
+ List<MesWmMaterialStockDO> stockList = materialStockMapper.selectListByItemIds(requirements.keySet());
+ for (MesWmMaterialStockDO stock : stockList) {
+ BigDecimal frozen = stock.getFrozenQuantity() != null ? stock.getFrozenQuantity() : BigDecimal.ZERO;
+ BigDecimal reserved = stock.getReservedQuantity() != null ? stock.getReservedQuantity() : BigDecimal.ZERO;
+ BigDecimal available = stock.getQuantity().subtract(frozen).subtract(reserved);
+ if (available.compareTo(BigDecimal.ZERO) > 0) {
+ stockMap.merge(stock.getItemId(), available, BigDecimal::add);
+ }
+ }
+
+ // 3. 鎷兼帴 VO
+ List<MesProWorkOrderItemRespVO> result = new ArrayList<>(requirements.size());
+ for (Map.Entry<Long, BigDecimal> entry : requirements.entrySet()) {
+ MesMdItemDO item = itemMap.get(entry.getKey());
+ if (item == null) {
+ continue;
+ }
+ BigDecimal requiredQty = entry.getValue();
+ BigDecimal stockQty = stockMap.getOrDefault(item.getId(), BigDecimal.ZERO);
+ BigDecimal purchaseQty = purchaseOrderQtyMap.getOrDefault(item.getId(), BigDecimal.ZERO);
+
+ // 鍏呰冻鍒ゆ柇 = 鍙敤搴撳瓨 + 鍦ㄩ�旈噰璐暟 >= 闇�姹傛暟閲�
+ BigDecimal totalSupply = stockQty.add(purchaseQty);
+
+ MesProWorkOrderItemRespVO vo = new MesProWorkOrderItemRespVO()
+ .setItemId(item.getId()).setQuantity(requiredQty)
+ .setItemCode(item.getCode()).setItemName(item.getName()).setItemSpecification(item.getSpecification())
+ .setStockQuantity(stockQty)
+ .setPurchaseOrderQuantity(purchaseQty)
+ .setStockSufficient(totalSupply.compareTo(requiredQty) >= 0);
+ MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(),
+ unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
+ MapUtils.findAndThen(itemTypeMap, item.getItemTypeId(),
+ itemType -> vo.setItemOrProduct(itemType.getItemOrProduct()));
+ result.add(vo);
+ }
+ return result;
+ }
+
}
--
Gitblit v1.9.3