1
0

2 Ревизии 2434b73349 ... ef4f1780f6

Автор SHA1 Съобщение Дата
  zhangcl ef4f1780f6 Merge remote-tracking branch 'origin/master' преди 1 ден
  zhangcl 432fe10e4c pms 保养工单 记录出库 преди 1 ден

+ 1 - 0
yudao-module-pms/yudao-module-pms-api/src/main/java/cn/iocoder/yudao/module/pms/enums/ErrorCodeConstant.java

@@ -34,6 +34,7 @@ public interface ErrorCodeConstant{
     ErrorCode IOT_INFORMATION_DB_NOT_EXISTS = new ErrorCode(130, "知识库不存在");
     ErrorCode IOT_LOCK_STOCK_FUSHU = new ErrorCode(131, "超过本地库存");
     ErrorCode IOT_MAINTENANCE_SAME_DEVICE_EXISTS = new ErrorCode(163, "已经存在相同设备的保养计划 {}");
+    ErrorCode IOT_MAIN_PLAN_ORDER_EXISTS = new ErrorCode(163, "请先维护保养项关联的保养计划或保养工单");
     ErrorCode IOT_MAINTENANCE_PLAN_NOT_EXISTS = new ErrorCode(133, "保养计划明细不存在");
     ErrorCode IOT_MAINTENANCE_DETAILS_NOT_EXISTS = new ErrorCode(134, "保养计划明细不存在");
     ErrorCode IOT_STORAGE_AREA_NOT_EXISTS = new ErrorCode(131, "PMS 库区不存在");

+ 8 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotmainworkorder/IotMainWorkOrderController.java

@@ -89,6 +89,14 @@ public class IotMainWorkOrderController {
         return success(true);
     }
 
+    @PutMapping("/historyWorkOrderOutbound")
+    @Operation(summary = "历史保养工单物料记录出库 初始化保养的出库记录")
+    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order:update')")
+    public CommonResult<Boolean> historyWorkOrderOutbound() {
+        iotMainWorkOrderService.historyWorkOrderOutbound();
+        return success(true);
+    }
+
     @PutMapping("/addWorkOrder")
     @Operation(summary = "手工新增保养工单")
     @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order:create')")

+ 7 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotmainworkorder/IotMainWorkOrderService.java

@@ -162,4 +162,11 @@ public interface IotMainWorkOrderService {
      * @return
      */
     List<IotMainWorkOrderBomDO> historyWorkOrderBoms(Set<Long> deviceIds, Set<Long> bomNodeIds);
+
+    /**
+     * 历史保养工单物料记录出库 初始化保养的出库记录
+     *
+     * @return
+     */
+    void historyWorkOrderOutbound();
 }

+ 121 - 8
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotmainworkorder/IotMainWorkOrderServiceImpl.java

@@ -29,12 +29,14 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.iotmaintenancebom.IotMaintenan
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorder.IotMainWorkOrderDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorderbom.IotMainWorkOrderBomDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorderbommaterial.IotMainWorkOrderBomMaterialDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotoutbound.IotOutboundDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotlockstock.IotLockStockMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotmaintenancebom.IotMaintenanceBomMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotmainworkorder.IotMainWorkOrderMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotmainworkorderbom.IotMainWorkOrderBomMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotmainworkorderbommaterial.IotMainWorkOrderBomMaterialMapper;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotoutbound.IotOutboundMapper;
 import cn.iocoder.yudao.module.pms.dal.redis.BizNoRedisDAO;
 import cn.iocoder.yudao.module.pms.service.IotDeviceService;
 import cn.iocoder.yudao.module.pms.service.iotdevicerunlog.IotDeviceRunLogService;
@@ -93,6 +95,8 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
     private IotDeviceMapper iotDeviceMapper;
     @Resource
     private DeptService deptService;
+    @Resource
+    private IotOutboundMapper iotOutboundMapper;
 
     @Override
     public Long createIotMainWorkOrder(IotMainWorkOrderSaveReqVO createReqVO) {
@@ -1241,6 +1245,8 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         Set<Long> storageLocationIds = new HashSet<>();
         Set<String> materialCodes = new HashSet<>();
         Map<String, BigDecimal> lockStockPair = new HashMap<>();
+        // key工厂id-成本中心id-物料编码-bomId        value保养项物料对象
+        Map<String, IotMainWorkOrderBomMaterialSaveReqVO> bomMaterialPair = new HashMap<>();
         // 标识保养项是否已经添加了物料
         Set<Long> bomNodeIds = new HashSet<>();
         AtomicBoolean mainCompleted = new AtomicBoolean(true);
@@ -1254,7 +1260,7 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         Set<Long> mctBomNodeIds = new HashSet<>();
         workOrderMaterials.forEach(material -> {
             if (ObjUtil.isEmpty(material.getId())) {
-                // 兼容保养工单中部分保养项保养完成 部分保养项 延期保养的情况 这里只新增延期保养 或正常 保养的物料
+                // 兼容保养工单中部分保养项保养完成 部分保养项延期保养的情况 这里只新增延期保养 或 正常保养的物料
                 IotMainWorkOrderBomMaterialDO tempMaterial = BeanUtils.toBean(material, IotMainWorkOrderBomMaterialDO.class);
                 tempMaterial.setWorkOrderId(mainWorkOrder.getId());
                 workOrderBomMaterialDOS.add(tempMaterial);
@@ -1272,8 +1278,11 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                     if (ObjUtil.isNotEmpty(material.getMaterialCode())) {
                         materialCodes.add(material.getMaterialCode());
                     }
+                    String uniqueKey = StrUtil.join("-", material.getFactoryId(),
+                            material.getCostCenterId(), material.getMaterialCode(), material.getBomNodeId());
                     String tempKey = material.getFactoryId() + String.valueOf(material.getCostCenterId())
                             + material.getMaterialCode();
+                    // 扣减库存使用 合并不同保养项下的相同物料
                     if (lockStockPair.containsKey(tempKey)) {
                         BigDecimal tempQuantity = lockStockPair.get(tempKey);
                         BigDecimal totalQuantity = tempQuantity.add(material.getQuantity());
@@ -1281,6 +1290,8 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                     } else {
                         lockStockPair.put(tempKey, material.getQuantity());
                     }
+                    // 记录出库使用 记录保养项id
+                    bomMaterialPair.put(uniqueKey, material);
                 }
             }
         });
@@ -1442,12 +1453,13 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         // 如果在填报工单时 填写了 保养项 中的 ‘运行时间周期(H)’ 保存时需要同步到 保养工单 关联的 保养计划的 对应的保养项的 ‘运行时间周期(H)’
         // 包含 延时保养 的情况
         // 只扣减本地库存 不处理SAP库存
+        // 保养基于单设备 出库时记录 单设备所属的部门id(即保养工单的部门id)
         if (CollUtil.isNotEmpty(factoryIds) && CollUtil.isNotEmpty(costCenterIds) && CollUtil.isNotEmpty(materialCodes)) {
             IotLockStockPageReqVO reqVO = new IotLockStockPageReqVO();
             reqVO.setFactoryIds(factoryIds);
             reqVO.setCostCenterIds(costCenterIds);
             reqVO.setMaterialCodes(materialCodes);
-            processLockStock(reqVO, lockStockPair);
+            processLockStock(reqVO, lockStockPair, updateObj, bomMaterialPair);
         }
     }
 
@@ -1456,23 +1468,54 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
      * @param reqVO
      * @param lockStockPair
      */
-    private void processLockStock(IotLockStockPageReqVO reqVO, Map<String, BigDecimal> lockStockPair){
+    private void processLockStock(IotLockStockPageReqVO reqVO, Map<String, BigDecimal> lockStockPair,
+                                  IotMainWorkOrderDO workOrder, Map<String, IotMainWorkOrderBomMaterialSaveReqVO> bomMaterialPair) {
         List<IotLockStockDO> lockStocks = iotLockStockMapper.selectList(reqVO);
         List<IotLockStockDO> tobeUpdatedLockStocks = new ArrayList<>();
+        List<IotOutboundDO> outbounds = new ArrayList<>();
         if (CollUtil.isNotEmpty(lockStocks)) {
             lockStocks.forEach(lockStock -> {
                 String tempKey = lockStock.getFactoryId() + String.valueOf(lockStock.getCostCenterId())
                         + lockStock.getMaterialCode();
+                BigDecimal outQuantity = lockStockPair.get(tempKey);
                 if (lockStockPair.containsKey(tempKey)) {
-                    lockStock.setQuantity(lockStock.getQuantity().subtract(lockStockPair.get(tempKey)));
+                    lockStock.setQuantity(lockStock.getQuantity().subtract(outQuantity));
                     tobeUpdatedLockStocks.add(lockStock);
                 }
             });
         }
+        // 记录出库 最好是保存 保养项id 方便后续统计 数据分析
+        if (CollUtil.isNotEmpty(bomMaterialPair)) {
+            bomMaterialPair.forEach((k,v) -> {
+                // k工厂id-成本中心id-物料编码-bomId
+                // 每个物料都对应一条出库记录
+                IotOutboundDO outbound = new IotOutboundDO();
+                outbound.setDeptId(workOrder.getDeptId());
+                outbound.setFactoryId(v.getFactoryId());
+                outbound.setCostCenterId(v.getCostCenterId());
+                outbound.setBomNodeId(v.getBomNodeId());
+                outbound.setMaterialCode(v.getMaterialCode());
+                outbound.setMaterialName(v.getMaterialName());
+                outbound.setQuantity(v.getQuantity());
+                outbound.setUnitPrice(v.getUnitPrice());
+                outbound.setUnit(v.getUnit());
+                outbound.setType(2);
+                outbound.setDeliveryTime(LocalDateTime.now());
+                outbound.setReason("保养");
+                outbound.setCreator(workOrder.getCreator());
+                outbound.setUpdater(workOrder.getUpdater());
+                outbounds.add(outbound);
+            });
+        }
         if (CollUtil.isNotEmpty(tobeUpdatedLockStocks)) {
             // 扣减本地库存
             iotLockStockMapper.updateBatch(tobeUpdatedLockStocks);
         }
+        // 添加出库记录
+        if (CollUtil.isNotEmpty(outbounds)) {
+            iotOutboundMapper.insertBatch(outbounds);
+        }
+
     }
 
     @Override
@@ -1582,7 +1625,10 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         Set<Long> costCenterIds = new HashSet<>();
         Set<Long> storageLocationIds = new HashSet<>();
         Set<String> materialCodes = new HashSet<>();
+        // key工厂id-成本中心id-物料编码-bomId      value物料数量
         Map<String, BigDecimal> lockStockPair = new HashMap<>();
+        // key工厂id-成本中心id-物料编码-bomId        value保养项物料对象
+        Map<String, IotMainWorkOrderBomMaterialSaveReqVO> bomMaterialPair = new HashMap<>();
         workOrderMaterials.forEach(material -> {
             IotMainWorkOrderBomMaterialDO tempMaterial = BeanUtils.toBean(material, IotMainWorkOrderBomMaterialDO.class);
             tempMaterial.setWorkOrderId(order.getId());
@@ -1600,8 +1646,11 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                 if (ObjUtil.isNotEmpty(material.getMaterialCode())) {
                     materialCodes.add(material.getMaterialCode());
                 }
+                // 有可能不同保养项使用了相同的物料 所以扣减本地库存 和 记录出库时 相同的成本中心物料有可能分开出库
+                String uniqueKey = StrUtil.join("-", material.getFactoryId(),
+                        material.getCostCenterId(), material.getMaterialCode(), material.getBomNodeId());
                 String tempKey = material.getFactoryId() + String.valueOf(material.getCostCenterId())
-                        + material.getMaterialCode();
+                        + material.getMaterialCode() + material.getBomNodeId() ;
                 if (lockStockPair.containsKey(tempKey)) {
                     BigDecimal tempQuantity = lockStockPair.get(tempKey);
                     BigDecimal totalQuantity = tempQuantity.add(material.getQuantity());
@@ -1609,17 +1658,18 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                 } else {
                     lockStockPair.put(tempKey, material.getQuantity());
                 }
+                // 记录出库 需要保养项id
+                bomMaterialPair.put(uniqueKey, material);
             }
         });
         iotMainWorkOrderBomMaterialMapper.insertBatch(workOrderBomMaterialDOS);
-        // 只扣减本地库存 不处理SAP库存
+        // 只扣减本地库存 不处理SAP库存 同时 记录出库
         if (CollUtil.isNotEmpty(factoryIds) && CollUtil.isNotEmpty(costCenterIds) && CollUtil.isNotEmpty(materialCodes)) {
             IotLockStockPageReqVO reqVO = new IotLockStockPageReqVO();
             reqVO.setFactoryIds(factoryIds);
             reqVO.setCostCenterIds(costCenterIds);
             reqVO.setMaterialCodes(materialCodes);
-            processLockStock(reqVO, lockStockPair);
-            // 记录出库
+            processLockStock(reqVO, lockStockPair, order, bomMaterialPair);
         }
     }
 
@@ -1816,6 +1866,69 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         return CollUtil.isNotEmpty(workOrderBomS) ? workOrderBomS : new ArrayList<>();
     }
 
+    @Override
+    public void historyWorkOrderOutbound() {
+        // 查询历史已经执行完成的保养工单 将保养工单关联的物料 添加出库记录
+        // 查询已经执行完成的保养工单
+        // 查询保养工单明细 status=1 记录 即查询出所有已经保养完成的保养项 但是保养工单不一定执行完成
+        IotMainWorkOrderBomPageReqVO bomReqVO = new IotMainWorkOrderBomPageReqVO();
+        bomReqVO.setStatus(1);
+        List<IotMainWorkOrderBomDO> workOrderBoms = iotMainWorkOrderBomMapper.selectList(bomReqVO);
+        if (CollUtil.isNotEmpty(workOrderBoms)) {
+            List<IotOutboundDO> outbounds = new ArrayList<>();
+            Set<Long> workOrderIds = new HashSet<>();
+            Set<Long> bomNodeIds = new HashSet<>();
+            workOrderBoms.forEach(bom -> {
+                workOrderIds.add(bom.getWorkOrderId());
+                bomNodeIds.add(bom.getBomNodeId());
+            });
+            if (CollUtil.isNotEmpty(workOrderIds) && CollUtil.isNotEmpty(bomNodeIds)) {
+                // 查询保养工单id
+                IotMainWorkOrderPageReqVO orderReqVO = new IotMainWorkOrderPageReqVO();
+                orderReqVO.setWorkOrderIds(new ArrayList<>(workOrderIds));
+                List<IotMainWorkOrderDO> finishedOrders = iotMainWorkOrderMapper.selectList(orderReqVO);
+                Map<Long, Long> orderDeptPair = new HashMap<>();
+                if (CollUtil.isNotEmpty(finishedOrders)) {
+                    finishedOrders.forEach(order -> {
+                        orderDeptPair.put(order.getId(), order.getDeptId());
+                    });
+                }
+                // 查询保养工单明细关联的所有物料
+                IotMainWorkOrderBomMaterialPageReqVO reqVO = new IotMainWorkOrderBomMaterialPageReqVO();
+                reqVO.setWorkOrderIds(new ArrayList<>(workOrderIds));
+                reqVO.setBomNodeIds(new ArrayList<>(bomNodeIds));
+                List<IotMainWorkOrderBomMaterialDO> bomMaterials = iotMainWorkOrderBomMaterialMapper.selectList(reqVO);
+                if (CollUtil.isNotEmpty(bomMaterials)) {
+                    bomMaterials.forEach(material -> {
+                        if ("本地库存".equals(material.getMaterialSource())) {
+                            IotOutboundDO outbound = new IotOutboundDO();
+                            if (orderDeptPair.containsKey(material.getWorkOrderId())) {
+                                outbound.setDeptId(orderDeptPair.get(material.getWorkOrderId()));
+                            }
+                            outbound.setBomNodeId(material.getBomNodeId());
+                            outbound.setFactoryId(material.getFactoryId());
+                            outbound.setCostCenterId(material.getCostCenterId());
+                            outbound.setMaterialCode(material.getMaterialCode());
+                            outbound.setMaterialName(material.getMaterialName());
+                            outbound.setQuantity(material.getQuantity());
+                            outbound.setUnitPrice(material.getUnitPrice());
+                            outbound.setUnit(material.getUnit());
+                            outbound.setType(2);
+                            outbound.setDeliveryTime(LocalDateTime.now());
+                            outbound.setReason("保养");
+                            outbound.setCreator(material.getCreator());
+                            outbound.setUpdater(material.getUpdater());
+                            outbounds.add(outbound);
+                        }
+                    });
+                }
+            }
+            if (CollUtil.isNotEmpty(outbounds)) {
+                iotOutboundMapper.insertBatch(outbounds);
+            }
+        }
+    }
+
     private static class Triple<A, B, C> {
         private final A first;
         private final B second;