SQL版本的库龄分析报表,使用存储过程实现,同时支持分支是否启用。
2025-12-01T07:40:41.png

  
CREATE PROCEDURE "IPS_Report_StockAge"
(
  @BPLId NVARCHAR(50),                     -- 分支ID(筛选指定分支,为空时不限制)
  @ItmsGrpCod NVARCHAR(20),                -- 物料组编码(筛选指定物料组,为空时不限制)
  @EndDate DATE                             -- 统计结束日期(所有数据的时间截止点)
)
AS
BEGIN
  SET NOCOUNT ON; -- 关闭计数消息输出,提升执行效率

  ---01 临时表#StockList:筛选截止日期前库存数量不为0的物料清单
  SELECT 
    T0."ItemCode",                          -- 物料编码
    SUM(T0."InQty"-T0."OutQty") AS "Qty"    -- 净库存数量(入库数量 - 出库数量)
    INTO #StockList                         -- 存入临时表,用于后续筛选有效库存物料
  FROM OIVL T0                               -- OIVL:库存交易主表(存储所有库存进出记录)
  INNER JOIN OITM T1 ON T0."ItemCode"= T1."ItemCode" -- 关联物料主表(OITM),获取物料组信息
  INNER JOIN OWHS T2 ON T0."LocCode"= T2."WhsCode"   -- 关联仓库表(OWHS),获取仓库所属分支信息
  WHERE 1=1
    AND T0."DocDate"<=@EndDate                      -- 交易日期不晚于统计截止日期
    AND (T1."ItmsGrpCod"=@ItmsGrpCod OR ISNULL(@ItmsGrpCod,'')='') -- 物料组筛选(为空时不限制)
    AND (CAST(T2."BPLid" AS NVARCHAR(50))=@BPLId OR ISNULL(@BPLId,'')='') -- 分支筛选(仓库BPLId转字符串匹配,为空时不限制)
  GROUP BY T0."ItemCode"                            -- 按物料编码分组,计算每个物料的总库存
  HAVING SUM(T0."InQty"-T0."OutQty")<>0;            -- 仅保留净库存不为0的物料

  ---02 临时表#StockIn:获取有效库存物料的入库交易明细(含累计入库量和排序ID)
  SELECT 
    T0."ItemCode",                          -- 物料编码
    T0."DocDate",                           -- 入库交易日期
    T0."TransType",                         -- 交易类型(区分不同入库业务,如采购入库、生产入库等)
    T0."CreatedBy",                         -- 制单人
    T0."BASE_REF",                          -- 源单据编号(关联原始业务单据)
    T0."DocLineNum",                        -- 单据行号
    T0."InQty",                             -- 本次入库数量
    -- 按物料编码分区,交易日期倒序、交易序号倒序排序,生成入库记录序号(最新入库为ID=1)
    ROW_NUMBER() OVER(PARTITION BY T0."ItemCode" ORDER BY T0."DocDate" DESC,T0."TransSeq" DESC) AS "ID",
    -- 按物料编码分区,累计入库数量(从最新入库开始累加,用于匹配当前库存)
    SUM(T0."InQty") OVER(PARTITION BY T0."ItemCode" ORDER BY T0."ItemCode",T0."DocDate" DESC,T0."TransSeq" DESC ) AS "SumQty",
    T1."Qty"                                -- 关联#StockList获取该物料的当前总库存
      INTO #StockIn                         -- 存入临时表,用于后续库龄计算
  FROM OIVL T0                               -- OIVL:库存交易主表
  INNER JOIN #StockList T1 ON T0."ItemCode"= T1."ItemCode" -- 仅保留库存非零的物料
  INNER JOIN OWHS T2 ON T0."LocCode"= T2."WhsCode" 
    AND (CAST(T2."BPLid" AS NVARCHAR(100))=@BPLId OR T2."BPLid" IS NULL) -- 分支筛选(含仓库BPLId为空的情况)
  WHERE EXISTS(SELECT 1 FROM #StockList U0 WHERE T0."ItemCode"=U0."ItemCode" ) -- 二次确认库存非零(冗余筛选,提升稳定性)
    AND T0."InQty">0                          -- 仅保留入库记录(排除出库记录)
    AND T0."TransType"<>'67'                  -- 排除交易类型为67的记录(通常为无效或冲销交易)
    AND T0."DocDate"<@EndDate;                -- 交易日期早于截止日期(截止日期当天入库不计入库龄计算)
   
  -- 临时表#MinID:获取每个物料满足当前库存的最小入库记录ID(即覆盖库存所需的最早入库批次)
  SELECT 
    T0."ItemCode",                          -- 物料编码
    MIN(T0.ID) AS "ID",                      -- 满足库存的最小入库记录ID(分界点ID)
    MAX(T0."DocDate") AS "MinDocDate"        -- 该物料的最早入库日期(对应分界点及之前的入库记录)
      INTO #MinID
  FROM #StockIn T0
  WHERE T0."SumQty">=T0."Qty"                -- 累计入库量>=当前库存(找到覆盖库存的所有入库记录)
  GROUP BY T0."ItemCode";
  
  -- 临时表#DATA:计算每个入库批次对应的有效库龄库存(AgaQty)及关键日期
  SELECT 
    T0."ItemCode",                          -- 物料编码
    T0."DocDate",                           -- 入库日期
    T0."TransType",                         -- 交易类型
    T0."CreatedBy",                         -- 制单人
    T0."BASE_REF",                          -- 源单据编号
    T0."DocLineNum",                        -- 单据行号
    T0."InQty",                             -- 本次入库数量
    T0."Qty",                               -- 当前总库存
    T0."SumQty",                            -- 累计入库量
    T0."ID",                                -- 入库记录序号(倒序)
    T1."ID" AS "MinID",                     -- 覆盖库存的最小分界点ID
    -- 有效库龄库存计算:
    -- 1. 序号<分界点ID:该批次入库量全额计入(已完全覆盖库存)
    -- 2. 序号=分界点ID:用总库存 - 上一批次累计入库量(剩余库存计入当前批次)
    CASE WHEN T0."ID"1096 THEN T0."AgaQty" ELSE 0 END) AS "三年以上" -- 库龄>3年的库存(1096=365*3+1)
  FROM #DATA T0                               -- 基于库龄计算中间表聚合
  INNER JOIN OITM T1 ON T0."ItemCode"= T1."ItemCode" -- 关联物料主表,获取物料描述
  INNER JOIN OITB T2 ON T1."ItmsGrpCod" = T2."ItmsGrpCod" -- 关联物料组表,获取物料组名称
  GROUP BY T0."ItemCode",T1."ItemName",T2."ItmsGrpCod",T2."ItmsGrpNam" -- 按物料+物料组分组
  ORDER BY 3,1;                               -- 按物料组编码、物料编码排序输出

  -- 清理临时表(避免占用资源,存储过程结束后临时表会自动删除,此处为冗余处理)
  DROP TABLE IF EXISTS #StockList;
  DROP TABLE IF EXISTS #StockIn;
  DROP TABLE IF EXISTS #MinID;
  DROP TABLE IF EXISTS #DATA;
END
  

评论已关闭