Explorar o código

pms 统计各公司的 总库存 积压库存

zhangcl hai 1 semana
pai
achega
fe48dd64d5

+ 15 - 7
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java

@@ -8,6 +8,8 @@ import java.math.RoundingMode;
 import java.sql.Timestamp;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -338,14 +340,20 @@ public class DateUtils {
         return timestamp;
     }
 
-        public static boolean checkIfFullDayDifference(String timeStr) {
-            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-            LocalDateTime givenTime = LocalDateTime.parse(timeStr, formatter);
-            LocalDateTime now = LocalDateTime.now();
+    public static boolean checkIfFullDayDifference(String timeStr) {
+        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
+                .appendPattern("yyyy-MM-dd HH:mm:ss")
+                .optionalStart()
+                .appendLiteral('.')
+                .appendFraction(ChronoField.MILLI_OF_SECOND, 1, 3, false)
+                .optionalEnd()
+                .toFormatter();
+        LocalDateTime givenTime = LocalDateTime.parse(timeStr, formatter);
+        LocalDateTime now = LocalDateTime.now();
 
-            long hoursDifference = ChronoUnit.MINUTES.between(givenTime, now);
-            return Math.abs(hoursDifference) >= 5;
-        }
+        long minutesDifference = ChronoUnit.MINUTES.between(givenTime, now);
+        return Math.abs(minutesDifference) >= 5;
+    }
 
         public static LocalDateTime contactTime(LocalDateTime targetTime ) {
             LocalDateTime now = LocalDateTime.now();

+ 9 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/IotProdInventoryAgingController.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotInventoryAgingStatisticsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
@@ -77,6 +78,14 @@ public class IotProdInventoryAgingController {
         return success(BeanUtils.toBean(pageResult, IotProdInventoryAgingRespVO.class));
     }
 
+    @GetMapping("/inventoryStatus")
+    @Operation(summary = "获得库存库龄存货情况")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:query')")
+    public CommonResult<IotInventoryAgingStatisticsRespVO> inventoryStatus(@Valid IotProdInventoryAgingPageReqVO pageReqVO) {
+        IotInventoryAgingStatisticsRespVO stockAgeStatistics = iotProdInventoryAgingService.inventoryStatus(pageReqVO);
+        return success(stockAgeStatistics);
+    }
+
     @GetMapping("/export-excel")
     @Operation(summary = "导出库存库龄表DWD Excel")
     @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:export')")

+ 33 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprodinventoryaging/vo/IotInventoryAgingStatisticsRespVO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+@Schema(description = "管理后台 - 库存库龄 总库存 积压库存 VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotInventoryAgingStatisticsRespVO {
+
+    @Schema(description = "总库存")
+    private BigDecimal totalInventory;
+
+    @Schema(description = "总积压存")
+    private BigDecimal totalOverStock;
+
+    @Schema(description = "以工厂维度统计的总积压库存数据")
+    private Map<String, BigDecimal> factoryNameOverStockPair;
+
+    @Schema(description = "以工厂维度统计的总库存数据")
+    private Map<String, BigDecimal> factoryNameInventoryPair;
+
+    @Schema(description = "以工厂-库存地点为维度统计的总积压库存数据")
+    private Map<String, BigDecimal> storageNameOverStockPair;
+
+    @Schema(description = "以工厂-库存地点为维度统计的总库存数据")
+    private Map<String, BigDecimal> storageNameInventoryPair;
+
+}

+ 35 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotprodinventoryaging/IotProdInventoryAgingMapper.java

@@ -9,6 +9,8 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdI
 import com.baomidou.dynamic.datasource.annotation.DS;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 /**
  * 库存库龄表DWD Mapper
  *
@@ -50,4 +52,37 @@ public interface IotProdInventoryAgingMapper extends BaseMapperX<IotProdInventor
                 .eqIfPresent(IotProdInventoryAgingDO::getAmtn, reqVO.getAmtn()));
     }
 
+    @DS("fanruan")
+    @TenantIgnore
+    default List<IotProdInventoryAgingDO> InventoryAges(IotProdInventoryAgingPageReqVO reqVO) {
+        return selectList(new LambdaQueryWrapperX<IotProdInventoryAgingDO>()
+                .eqIfPresent(IotProdInventoryAgingDO::getFyear, reqVO.getFyear())
+                .eqIfPresent(IotProdInventoryAgingDO::getFmonth, reqVO.getFmonth())
+                .eqIfPresent(IotProdInventoryAgingDO::getFdate, reqVO.getFdate())
+                .eqIfPresent(IotProdInventoryAgingDO::getWerks, reqVO.getWerks())
+                .eqIfPresent(IotProdInventoryAgingDO::getDept, reqVO.getDept())
+                .eqIfPresent(IotProdInventoryAgingDO::getMatnr, reqVO.getMatnr())
+                .eqIfPresent(IotProdInventoryAgingDO::getLgort, reqVO.getLgort())
+                .eqIfPresent(IotProdInventoryAgingDO::getBklas, reqVO.getBklas())
+                .eqIfPresent(IotProdInventoryAgingDO::getMatkl, reqVO.getMatkl())
+                .eqIfPresent(IotProdInventoryAgingDO::getMtart, reqVO.getMtart())
+                .eqIfPresent(IotProdInventoryAgingDO::getMaktx, reqVO.getMaktx())
+                .eqIfPresent(IotProdInventoryAgingDO::getVbeln, reqVO.getVbeln())
+                .eqIfPresent(IotProdInventoryAgingDO::getPosnr, reqVO.getPosnr())
+                .eqIfPresent(IotProdInventoryAgingDO::getBezei, reqVO.getBezei())
+                .eqIfPresent(IotProdInventoryAgingDO::getTotal, reqVO.getTotal())
+                .eqIfPresent(IotProdInventoryAgingDO::getStprs, reqVO.getStprs())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmttotal, reqVO.getAmttotal())
+                .eqIfPresent(IotProdInventoryAgingDO::getPeinh, reqVO.getPeinh())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty1, reqVO.getQty1())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty2, reqVO.getQty2())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty3, reqVO.getQty3())
+                .eqIfPresent(IotProdInventoryAgingDO::getQty4, reqVO.getQty4())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt1, reqVO.getAmt1())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt2, reqVO.getAmt2())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt3, reqVO.getAmt3())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmt4, reqVO.getAmt4())
+                .eqIfPresent(IotProdInventoryAgingDO::getAmtn, reqVO.getAmtn()));
+    }
+
 }

+ 8 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprodinventoryaging/IotProdInventoryAgingService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.pms.service.iotprodinventoryaging;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotInventoryAgingStatisticsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
@@ -52,4 +53,11 @@ public interface IotProdInventoryAgingService {
      */
     PageResult<IotProdInventoryAgingDO> getIotProdInventoryAgingPage(IotProdInventoryAgingPageReqVO pageReqVO);
 
+    /**
+     * 获得各公司的存货情况
+     *
+     * @param pageReqVO 分页查询
+     * @return 库存库龄表DWD分页
+     */
+    IotInventoryAgingStatisticsRespVO inventoryStatus(IotProdInventoryAgingPageReqVO pageReqVO);
 }

+ 196 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotprodinventoryaging/IotProdInventoryAgingServiceImpl.java

@@ -1,16 +1,33 @@
 package cn.iocoder.yudao.module.pms.service.iotprodinventoryaging;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotInventoryAgingStatisticsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.IotProdInventoryAgingSaveReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprodinventoryaging.IotProdInventoryAgingMapper;
+import cn.iocoder.yudao.module.system.api.saporg.SapOrgApi;
+import cn.iocoder.yudao.module.system.api.saporg.dto.SapOrgRespDTO;
+import cn.iocoder.yudao.module.system.controller.admin.saporg.vo.SapOrgSimpleReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.saporg.SapOrgDO;
+import cn.iocoder.yudao.module.system.service.dept.DeptService;
+import cn.iocoder.yudao.module.system.service.saporg.SapOrgService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
 
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PROD_INVENTORY_AGING_NOT_EXISTS;
 
@@ -25,6 +42,12 @@ public class IotProdInventoryAgingServiceImpl implements IotProdInventoryAgingSe
 
     @Resource
     private IotProdInventoryAgingMapper iotProdInventoryAgingMapper;
+    @Autowired
+    private SapOrgApi sapOrgApi;
+    @Autowired
+    private DeptService deptService;
+    @Autowired
+    private SapOrgService sapOrgService;
 
     @Override
     public Long createIotProdInventoryAging(IotProdInventoryAgingSaveReqVO createReqVO) {
@@ -68,4 +91,177 @@ public class IotProdInventoryAgingServiceImpl implements IotProdInventoryAgingSe
         return iotProdInventoryAgingMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public IotInventoryAgingStatisticsRespVO inventoryStatus(IotProdInventoryAgingPageReqVO pageReqVO) {
+        // 库龄数据量大 必须选择日期
+        if (StrUtil.isBlank(pageReqVO.getFdate())) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        String deptIdStr = pageReqVO.getDept();
+        if (StrUtil.isBlank(deptIdStr)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        IotInventoryAgingStatisticsRespVO result = new IotInventoryAgingStatisticsRespVO();
+        // 查询当前公司对应的 库存地点 标识 名称 按照各库存地点统计 存货及积压金额
+        Long deptId = Long.valueOf(deptIdStr);
+        DeptDO dept = deptService.getDept(deptId);
+        Set<Long> factoryIds = dept.getFactoryIds();
+        List<SapOrgRespDTO> sapOrganizations = sapOrgApi.getSapOrgList(factoryIds);
+        if (CollUtil.isEmpty(sapOrganizations)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        // key工厂编码   value工厂名称
+        Map<String, String> factoryPair = new HashMap<>();
+        sapOrganizations.forEach(org -> {
+            factoryPair.put(org.getFactoryCode(), org.getFactoryName());
+        });
+        // 需要查询库龄的工厂
+        List<String> factoryCodes = sapOrganizations.stream()
+                .filter(dto -> "age".equals(dto.getCreator()))
+                .map(SapOrgRespDTO::getFactoryCode)
+                .collect(Collectors.toList());
+        // 查询工厂下所有库存地点信息
+        // key工厂编辑-库存地点编码   value库存地点名称
+        Map<String, String> storageLocationPair = new HashMap<>();
+        SapOrgSimpleReqVO reqVO = new SapOrgSimpleReqVO();
+        reqVO.setType(3);
+        reqVO.setFactoryCodes(factoryCodes);
+        List<SapOrgDO> storageLocations = sapOrgService.getSelectedList(reqVO);
+        if (CollUtil.isNotEmpty(storageLocations)) {
+            storageLocations.forEach(location -> {
+                String factoryCode = location.getFactoryCode();
+                String storageLocationCode = location.getStorageLocationCode();
+                String uniqueKey = StrUtil.join("-", factoryCode, storageLocationCode);
+                storageLocationPair.put(uniqueKey, location.getStorageLocationName());
+            });
+        }
+        // 查询帆软数仓中的库龄数据
+        List<IotProdInventoryAgingDO> stockAges = iotProdInventoryAgingMapper.InventoryAges(pageReqVO);
+        if (CollUtil.isEmpty(stockAges)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        // 总库存
+        BigDecimal amtSummary = stockAges.stream()
+                .map(IotProdInventoryAgingDO::getAmttotal)
+                // null值转为0
+                .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 1年以内的库存总额
+        BigDecimal amtSummaryOneYear = stockAges.stream()
+                .map(IotProdInventoryAgingDO::getAmt1)
+                // null值转为0
+                .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 按工厂‑库存地点分组
+        Map<String, List<IotProdInventoryAgingDO>> groupMap = stockAges.stream()
+                .collect(Collectors.groupingBy(age -> StrUtil.join("-", age.getWerks(), age.getLgort())));
+        // 按工厂‑库存地点分组
+        Map<String, List<IotProdInventoryAgingDO>> facotryGroupMap = stockAges.stream()
+                .collect(Collectors.groupingBy(age -> age.getWerks()));
+
+        // 积压库存 = 总库存 - 1年以内的库存总额
+        BigDecimal overstockInventory = amtSummary.subtract(amtSummaryOneYear);
+
+        // 各工厂的总库存 key工厂编码   value
+        Map<String, BigDecimal> factoryInventoryPair = new HashMap<>();
+        // 各工厂的积压库存 key工厂编码
+        Map<String, BigDecimal> factoryOverStockPair = new HashMap<>();
+        // 各工厂的总库存 key工厂编码   value
+        Map<String, BigDecimal> factoryNameInventoryPair = new HashMap<>();
+        // 各工厂的积压库存 key工厂编码
+        Map<String, BigDecimal> factoryNameOverStockPair = new HashMap<>();
+
+        // 循环分组统计总库存、积压库存 按照库存地点统计
+        facotryGroupMap.forEach((factoryCode, ages) -> {
+            BigDecimal sumTotal = ages.stream()
+                    .map(IotProdInventoryAgingDO::getAmttotal)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOneYear = ages.stream()
+                    .map(IotProdInventoryAgingDO::getAmt1)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOverStock = sumTotal.subtract(sumOneYear);
+
+            factoryInventoryPair.put(factoryCode, sumTotal);
+            factoryOverStockPair.put(factoryCode, sumOverStock);
+        });
+
+        // 生成 storageNameInventoryPair
+        factoryNameInventoryPair = factoryInventoryPair.entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> factoryPair.getOrDefault(e.getKey(), "未知"),
+                        Map.Entry::getValue,
+                        (v1, v2) -> v1   // 若键冲突(例如多个“未知”),保留第一个值
+                ));
+
+        // 生成 storageNameOverStockPair
+        factoryNameOverStockPair = factoryOverStockPair.entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> factoryPair.getOrDefault(e.getKey(), "未知"),
+                        Map.Entry::getValue,
+                        (v1, v2) -> v1
+                ));
+
+        // 各库存地点的总库存 key工厂编码-库存地点编码   value
+        Map<String, BigDecimal> storageInventoryPair = new HashMap<>();
+        // 各库存地点的积压库存 key工厂编码-库存地点编码
+        Map<String, BigDecimal> storageOverStockPair = new HashMap<>();
+        // 循环分组统计总库存、积压库存 按照库存地点统计
+        groupMap.forEach((uniqueKey, list) -> {
+            BigDecimal sumTotal = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmttotal)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOneYear = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmt1)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOverStock = sumTotal.subtract(sumOneYear);
+
+            storageInventoryPair.put(uniqueKey, sumTotal);
+            storageOverStockPair.put(uniqueKey, sumOverStock);
+        });
+        // 各库存地点的总库存 key工厂编码-库存地点名称   value
+        Map<String, BigDecimal> storageNameInventoryPair = new HashMap<>();
+        // 各库存地点的积压库存 key工厂编辑-库存地点编码
+        Map<String, BigDecimal> storageNameOverStockPair = new HashMap<>();
+        Function<String, String> keyMapper = uniqueKey -> {
+            String locationName = storageLocationPair.get(uniqueKey);
+            if (StrUtil.isBlank(locationName)) {
+                return "未知";
+            }
+            String factoryCode = StrUtil.subBefore(uniqueKey, "-", false);
+            return StrUtil.join("-", factoryCode, locationName);
+        };
+        // 生成 storageNameInventoryPair
+        storageNameInventoryPair = storageInventoryPair.entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> keyMapper.apply(e.getKey()),
+                        Map.Entry::getValue,
+                        (v1, v2) -> v1   // 若键冲突(例如多个“未知”),保留第一个值
+                ));
+
+        // 生成 storageNameOverStockPair
+        storageNameOverStockPair = storageOverStockPair.entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> keyMapper.apply(e.getKey()),
+                        Map.Entry::getValue,
+                        (v1, v2) -> v1
+                ));
+        result.setTotalInventory(amtSummary);
+        result.setTotalOverStock(overstockInventory);
+
+        result.setFactoryNameInventoryPair(factoryNameInventoryPair);
+        result.setFactoryNameOverStockPair(factoryNameOverStockPair);
+
+        result.setStorageNameInventoryPair(storageInventoryPair);
+        result.setStorageNameOverStockPair(storageOverStockPair);
+        return result;
+    }
+
 }