zhangcl 1 неделя назад
Родитель
Сommit
4e23d8b5f0

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

@@ -23,7 +23,9 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.util.List;
+import java.util.Map;
 
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -86,6 +88,22 @@ public class IotProdInventoryAgingController {
         return success(stockAgeStatistics);
     }
 
+    @GetMapping("/inventoryTrend")
+    @Operation(summary = "获得库存库龄 积压库存 变化趋势")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:query')")
+    public CommonResult<Map<String, BigDecimal>> inventoryTrend(@Valid IotProdInventoryAgingPageReqVO pageReqVO) {
+        Map<String, BigDecimal> inventoryTrend = iotProdInventoryAgingService.inventoryTrend(pageReqVO);
+        return success(inventoryTrend);
+    }
+
+    @GetMapping("/inventoryAgeDetail")
+    @Operation(summary = "获得库存库龄 数据明细")
+    @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:query')")
+    public CommonResult<Map<String, BigDecimal>> inventoryAgeDetail(@Valid IotProdInventoryAgingPageReqVO pageReqVO) {
+        Map<String, BigDecimal> inventoryAgeDetail = iotProdInventoryAgingService.inventoryAgeDetail(pageReqVO);
+        return success(inventoryAgeDetail);
+    }
+
     @GetMapping("/export-excel")
     @Operation(summary = "导出库存库龄表DWD Excel")
     @PreAuthorize("@ss.hasPermission('pms:iot-prod-inventory-aging:export')")

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

@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
 import java.math.BigDecimal;
+import java.util.Collection;
 
 @Schema(description = "管理后台 - 库存库龄表DWD分页 Request VO")
 @Data
@@ -95,4 +96,10 @@ public class IotProdInventoryAgingPageReqVO extends PageParam {
     @Schema(description = "库龄段其它成本")
     private BigDecimal amtn;
 
+    /**
+     * 扩展字段
+     */
+    @Schema(description = "日期列表 2026-08-15,2026-07-31,2026-06-30...")
+    private Collection<String> dateStrs;
+
 }

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

@@ -59,6 +59,7 @@ public interface IotProdInventoryAgingMapper extends BaseMapperX<IotProdInventor
                 .eqIfPresent(IotProdInventoryAgingDO::getFyear, reqVO.getFyear())
                 .eqIfPresent(IotProdInventoryAgingDO::getFmonth, reqVO.getFmonth())
                 .eqIfPresent(IotProdInventoryAgingDO::getFdate, reqVO.getFdate())
+                .inIfPresent(IotProdInventoryAgingDO::getFdate, reqVO.getDateStrs())
                 .eqIfPresent(IotProdInventoryAgingDO::getWerks, reqVO.getWerks())
                 .eqIfPresent(IotProdInventoryAgingDO::getDept, reqVO.getDept())
                 .eqIfPresent(IotProdInventoryAgingDO::getMatnr, reqVO.getMatnr())

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

@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotprodinventoryaging.vo.Iot
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprodinventoryaging.IotProdInventoryAgingDO;
 
 import javax.validation.Valid;
+import java.math.BigDecimal;
+import java.util.Map;
 
 /**
  * 库存库龄表DWD Service 接口
@@ -60,4 +62,20 @@ public interface IotProdInventoryAgingService {
      * @return 库存库龄表DWD分页
      */
     IotInventoryAgingStatisticsRespVO inventoryStatus(IotProdInventoryAgingPageReqVO pageReqVO);
+
+    /**
+     * 获得各公司的积压库存变化趋势
+     *
+     * @param pageReqVO 查询条件
+     * @return 各公司最近6个月的积压库存变化趋势
+     */
+    Map<String, BigDecimal> inventoryTrend(IotProdInventoryAgingPageReqVO pageReqVO);
+
+    /**
+     * 获得各公司的 库龄数据 明细
+     *
+     * @param pageReqVO 查询条件
+     * @return 各公司库龄数据明细
+     */
+    Map<String, BigDecimal> inventoryAgeDetail(IotProdInventoryAgingPageReqVO pageReqVO);
 }

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

@@ -24,6 +24,11 @@ import org.springframework.validation.annotation.Validated;
 import javax.annotation.Resource;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAdjusters;
 import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -264,4 +269,187 @@ public class IotProdInventoryAgingServiceImpl implements IotProdInventoryAgingSe
         return result;
     }
 
+    @Override
+    public Map<String, BigDecimal> inventoryTrend(IotProdInventoryAgingPageReqVO pageReqVO) {
+        // 库龄数据量大 必须选择日期
+        String fDate = pageReqVO.getFdate();
+        if (StrUtil.isBlank(fDate)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        String deptIdStr = pageReqVO.getDept();
+        if (StrUtil.isBlank(deptIdStr)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        // 根据 fDate 生成最近6个月的的日期 fDate所在的月保持现状
+        // 比如 fDate = 2026年8月15日 则最终生成的 6个日期是
+        // 2026年8月15日
+        // 2026年7月31日
+        // 2026年6月30日
+        // 2026年5月31日
+        // 2026年4月30日
+        // 2026年3月31日
+        // 解析传入的日期(假设格式为 yyyy-MM-dd)
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        LocalDate startDate = LocalDate.parse(fDate, formatter);
+        // 生成6个日期字符串
+        List<String> latestSixDateStr = new ArrayList<>(6);
+        // 第一个日期就是 fDate 本身
+        latestSixDateStr.add(startDate.format(formatter));
+
+        // 从当前月份开始,依次向前推5个月,取每个月的最后一天
+        YearMonth currentMonth = YearMonth.from(startDate);
+        for (int i = 1; i < 6; i++) {
+            // 上一个月
+            currentMonth = currentMonth.minusMonths(1);
+            LocalDate lastDayOfMonth = currentMonth.atEndOfMonth();
+            latestSixDateStr.add(lastDayOfMonth.format(formatter));
+        }
+
+        // 查询指定公司在这6个日期节点上的 总库存 1年内的总库存 积压库存
+        // 查询帆软数仓中的库龄数据
+        Map<String, List<IotProdInventoryAgingDO>> dateInventoryAgesPair = new HashMap<>();
+        pageReqVO.setDept(deptIdStr);
+        pageReqVO.setFdate(StrUtil.EMPTY);
+        pageReqVO.setDateStrs(latestSixDateStr);
+        List<IotProdInventoryAgingDO> stockAges = iotProdInventoryAgingMapper.InventoryAges(pageReqVO);
+        if (CollUtil.isNotEmpty(stockAges)) {
+            dateInventoryAgesPair = stockAges.stream()
+                    .collect(Collectors.groupingBy(age -> age.getFdate()));
+        }
+        // 各公司的积压库存 key日期yyyy-MM-dd   value积压库存金额
+        Map<String, BigDecimal> overStockPair = new HashMap<>();
+        // 循环分组统计总库存、积压库存 按照库存地点统计
+        dateInventoryAgesPair.forEach((groupDate, 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);
+
+            overStockPair.put(groupDate, sumOverStock);
+        });
+
+        // 各公司的积压库存 key日期yyyy-MM   value积压库存金额
+        Map<String, BigDecimal> resultOverStockPair = latestSixDateStr.stream()
+                .collect(Collectors.toMap(
+                        date -> date.substring(0, 7),                     // 提取 yyyy-MM
+                        date -> overStockPair.getOrDefault(date, BigDecimal.ZERO),
+                        (v1, v2) -> v1,                                   // 不会出现重复 key,占位
+                        LinkedHashMap::new                                // 保持顺序
+                ));
+        // 按照6个日期的顺序 返回 积压库存
+
+        return resultOverStockPair;
+    }
+
+    @Override
+    public Map<String, BigDecimal> inventoryAgeDetail(IotProdInventoryAgingPageReqVO pageReqVO) {
+        // 库龄数据量大 必须选择日期
+        String fDate = pageReqVO.getFdate();
+        if (StrUtil.isBlank(fDate)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        String deptIdStr = pageReqVO.getDept();
+        if (StrUtil.isBlank(deptIdStr)) {
+            throw exception(IOT_PROD_INVENTORY_AGING_NOT_EXISTS);
+        }
+        // 存货及变动情况 账龄分析
+        // 年初存货 年初积压 期末库存 期末积压
+        // 年初存货 当前查询日期所在年的第一天
+        // 1年以内  1-2年 2-3年  3年以上 积压比
+        // 需要查询2个时间节点的数据 fDate所在年的第一天yyyy-MM-dd   fDate
+        LocalDate fDateLocal = LocalDate.parse(fDate);
+        LocalDate firstDayOfYear = fDateLocal.with(TemporalAdjusters.firstDayOfYear());
+        String yearStartDate = firstDayOfYear.toString(); // 年初日期字符串
+        List<String> queryDates = new ArrayList<>();
+        queryDates.add(yearStartDate);
+        queryDates.add(fDate);
+
+        // 查询帆软数仓中的库龄数据
+        Map<String, List<IotProdInventoryAgingDO>> dateInventoryAgesPair = new HashMap<>();
+
+        pageReqVO.setDept(deptIdStr);
+        pageReqVO.setFdate(StrUtil.EMPTY);
+        pageReqVO.setDateStrs(queryDates);
+        List<IotProdInventoryAgingDO> stockAges = iotProdInventoryAgingMapper.InventoryAges(pageReqVO);
+        if (CollUtil.isNotEmpty(stockAges)) {
+            dateInventoryAgesPair = stockAges.stream()
+                    .collect(Collectors.groupingBy(age -> age.getFdate()));
+        }
+
+        // 期末 期初 的库龄数据 key期末期初标识   value 总库存 积压库存
+        Map<String, BigDecimal> stockAgePair = new HashMap<>();
+        // 统计期初 期末库龄数据
+        // 循环分组统计 期初 期末 数据 总库存、积压库存
+        dateInventoryAgesPair.forEach((groupDate, list) -> {
+            // 总库存
+            BigDecimal sumTotal = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmttotal)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 1年以内库存
+            BigDecimal sumOneYear = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmt1)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 1-2年库存
+            BigDecimal sumTwoTotal = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmt2)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 2-3年库存
+            BigDecimal sumThreeTotal = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmt3)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 4年库存
+            BigDecimal sumFourTotal = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmt4)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 其它库存
+            BigDecimal sumOtherTotal = list.stream()
+                    .map(IotProdInventoryAgingDO::getAmtn)
+                    .map(val -> ObjUtil.isEmpty(val) ? BigDecimal.ZERO : val)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            BigDecimal sumOverStock = sumTotal.subtract(sumOneYear);
+            if (yearStartDate.equals(groupDate)) {
+                // 期初库龄数据
+                stockAgePair.put("年初存货", sumTotal);
+                // 期初库龄数据
+                stockAgePair.put("年初积压", sumOverStock);
+            }
+            if (fDate.equals(groupDate)) {
+                // 期末库龄数据
+                stockAgePair.put("期末库存", sumTotal);
+                // 期末库龄数据
+                stockAgePair.put("期末积压", sumOverStock);
+
+                // 1年以内 SUM(AMT1)
+                // 1-2年 SUM(AMT2)
+                // 2-3年 SUM(AMT3)
+                // 3年以上 SUM(AMT4 + AMTN)
+                stockAgePair.put("1年以内", sumOneYear);
+                stockAgePair.put("1-2年", sumTwoTotal);
+                stockAgePair.put("2-3年", sumThreeTotal);
+                stockAgePair.put("3年以上", sumFourTotal.add(sumOtherTotal));
+
+                // 积压比  期末积压 / 期末库存
+                if (sumTotal.compareTo(BigDecimal.ZERO) > 0) {
+                    BigDecimal overStockRate = sumOverStock.divide(sumTotal, 4, RoundingMode.HALF_UP);
+                    stockAgePair.put("积压比", overStockRate);
+                }
+            }
+        });
+
+        return stockAgePair;
+    }
+
 }