SAP-ABAP:MM 模块库存管理开发:出入库增强、批次管理与库存盘点功能开发

MM 模块库存管理开发:出入库增强、批次管理与库存盘点功能开发

博客简介:覆盖库存管理核心开发场景,讲解 MIGO 入库/出库增强 BAdI 开发、批次自动确定逻辑开发、循环盘点/动态盘点自定义程序开发、库存预警报表开发的核心逻辑,附"入库单自动校验采购订单匹配校验""批次效期自动预警""库龄分析报表"的完整代码,保障库存数据准确性与出入库流程合规性。


"采购订单、物料主数据、采购申请增强你都做过了,MM 模块最后一块拼图------库存管理(MIGO 出入库、批次效期、盘点预警)------本篇用三个贯穿场景完整演示,附 MIGO BAdI 校验代码、批次效期预警程序、库龄分析报表,让你把 MM 模块的增强开发做全。"

本篇覆盖库存管理核心开发场景,讲解 MIGO 入库/出库增强 BAdI 开发、批次自动确定逻辑开发、循环盘点/动态盘点自定义程序开发、库存预警报表开发的核心逻辑,附完整的出入库匹配校验、批次效期预警、库龄分析报表代码。


📖 写在前面

本篇定位

库存管理(Inventory Management)是 MM 模块操作频率最高的功能------几乎每个物料的生命周期都要经过"入库→存储→出库"三个阶段。库存管理的增强需求集中在:MIGO 操作的合规校验、批次效期管控、库存数据质量保障。本篇聚焦三个高频场景,所有代码基于 SAP NetWeaver 7.51。

本篇适合谁读

  • 需要在 MM 模块做 MIGO/批次/盘点增强的开发者
  • 负责库存数据质量的业务分析师
  • 想完善 MM 模块增强体系的架构师

贯穿场景概览

#mermaid-svg-J9pGKqRzVwFvK61n{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-J9pGKqRzVwFvK61n .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-J9pGKqRzVwFvK61n .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-J9pGKqRzVwFvK61n .error-icon{fill:#552222;}#mermaid-svg-J9pGKqRzVwFvK61n .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-J9pGKqRzVwFvK61n .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-J9pGKqRzVwFvK61n .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-J9pGKqRzVwFvK61n .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-J9pGKqRzVwFvK61n .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-J9pGKqRzVwFvK61n .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-J9pGKqRzVwFvK61n .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-J9pGKqRzVwFvK61n .marker{fill:#333333;stroke:#333333;}#mermaid-svg-J9pGKqRzVwFvK61n .marker.cross{stroke:#333333;}#mermaid-svg-J9pGKqRzVwFvK61n svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-J9pGKqRzVwFvK61n p{margin:0;}#mermaid-svg-J9pGKqRzVwFvK61n .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-J9pGKqRzVwFvK61n .cluster-label text{fill:#333;}#mermaid-svg-J9pGKqRzVwFvK61n .cluster-label span{color:#333;}#mermaid-svg-J9pGKqRzVwFvK61n .cluster-label span p{background-color:transparent;}#mermaid-svg-J9pGKqRzVwFvK61n .label text,#mermaid-svg-J9pGKqRzVwFvK61n span{fill:#333;color:#333;}#mermaid-svg-J9pGKqRzVwFvK61n .node rect,#mermaid-svg-J9pGKqRzVwFvK61n .node circle,#mermaid-svg-J9pGKqRzVwFvK61n .node ellipse,#mermaid-svg-J9pGKqRzVwFvK61n .node polygon,#mermaid-svg-J9pGKqRzVwFvK61n .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-J9pGKqRzVwFvK61n .rough-node .label text,#mermaid-svg-J9pGKqRzVwFvK61n .node .label text,#mermaid-svg-J9pGKqRzVwFvK61n .image-shape .label,#mermaid-svg-J9pGKqRzVwFvK61n .icon-shape .label{text-anchor:middle;}#mermaid-svg-J9pGKqRzVwFvK61n .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-J9pGKqRzVwFvK61n .rough-node .label,#mermaid-svg-J9pGKqRzVwFvK61n .node .label,#mermaid-svg-J9pGKqRzVwFvK61n .image-shape .label,#mermaid-svg-J9pGKqRzVwFvK61n .icon-shape .label{text-align:center;}#mermaid-svg-J9pGKqRzVwFvK61n .node.clickable{cursor:pointer;}#mermaid-svg-J9pGKqRzVwFvK61n .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-J9pGKqRzVwFvK61n .arrowheadPath{fill:#333333;}#mermaid-svg-J9pGKqRzVwFvK61n .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-J9pGKqRzVwFvK61n .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-J9pGKqRzVwFvK61n .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J9pGKqRzVwFvK61n .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-J9pGKqRzVwFvK61n .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J9pGKqRzVwFvK61n .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-J9pGKqRzVwFvK61n .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-J9pGKqRzVwFvK61n .cluster text{fill:#333;}#mermaid-svg-J9pGKqRzVwFvK61n .cluster span{color:#333;}#mermaid-svg-J9pGKqRzVwFvK61n div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-J9pGKqRzVwFvK61n .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-J9pGKqRzVwFvK61n rect.text{fill:none;stroke-width:0;}#mermaid-svg-J9pGKqRzVwFvK61n .icon-shape,#mermaid-svg-J9pGKqRzVwFvK61n .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J9pGKqRzVwFvK61n .icon-shape p,#mermaid-svg-J9pGKqRzVwFvK61n .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-J9pGKqRzVwFvK61n .icon-shape .label rect,#mermaid-svg-J9pGKqRzVwFvK61n .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J9pGKqRzVwFvK61n .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-J9pGKqRzVwFvK61n .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-J9pGKqRzVwFvK61n :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 库存管理增强三大场景
场景1:MIGO 出入库增强校验
场景2:批次效期预警 + 自动确定
场景3:库存盘点 + 库龄分析
MIGO_GOODS_MOVEMENT_POSTING

入库 PO 匹配 / 出库库存 / 容差 / 效期
定时 Job + BADI_GOITEM

FEFO 先到期先出 / 效期预警
自定义盘点程序 + ALV

30/60/90/180/365 天分级

版本说明:本篇内容基于 SAP NetWeaver 7.51,MIGO_GOODS_MOVEMENT_POSTING BAdI 在 ECC 6.0+ 版本中均可用。


一、库存管理增强架构

1.1 核心增强点

增强类型 增强名称 触发时机 适用事务码 推荐场景
BAdI MIGO_GOODS_MOVEMENT_POSTING 过账前最后一道校验 MIGO 出入库核心校验
BAdI MB_MIGO_BADI MIGO 界面级增强 MIGO 自定义界面逻辑、子屏幕
BAdI BADI_GOITEM MIGO 物料明细级别 MIGO 批次自动确定、效期检查
隐式增强 函数/方法首尾 函数首尾自动生成 MIGO BAdI 没覆盖的补充

推荐优先用 MIGO_GOODS_MOVEMENT_POSTING:功能最完善、参数最全、覆盖所有移动类型。

1.2 MIGO_GOODS_MOVEMENT_POSTING 接口方法

⚠️ 技术要点 :该 BAdI 的接口方法为 CHECK (在 ECC 6.0 中可能显示为 IF_EX_MIGO_GOODS_MOVEMENT_PO~CHECK),传入 IS_GOHEAD(抬头)和 IT_GOITEM(行项目),通过 CV_DONT_POST 阻止过账。

参数名 方向 类型 说明
IS_GOHEAD IMPORTING GOHEAD 抬头数据(移动类型、过账日期、工厂、库存地点)
IT_GOITEM IMPORTING GOITEM_T 行项目表(物料号、数量、批次、供应商、PO 号)
CT_MSEG CHANGING MSEG_T 会计凭证行项目(财务科目自动派生)
CV_DONT_POST CHANGING FLAG 阻止过账标志('X' = 阻止过账)

1.3 库存管理核心表

表名 说明 关键字段
MARD 存储位置库存 MATNR、WERKS、LABST(非限制库存)、INSME(质检)、SPEME(冻结)
MBEW 会计评估库存 MATNR、WERKS、VERPR(移动均价)、STPRS(标准价)
CHDO 批次主数据 MATNR、CHARG、VERBM(可用)、VFDAT(效期)、CLABS(可用量)
MKPF 物料凭证抬头 MBLNR、JAHR、BUDAT、USNAM、CPUTM
MSEG 物料凭证行 MBLNR、MATNR、CHARG、SHKZG(贷/借)、MENGE、EBELN
T001L 工厂/存储位置 WERKS、LGORT

二、MIGO 入库/出库增强校验

2.1 配置表设计

出入库校验规则表

复制代码
表名:ZT_STOCK_VALIDATE_RULE
  MANDT         TYPE MANDT       " 客户端
  BUKRS         TYPE BUKRS       " 公司代码
  WERKS         TYPE WERKS_D     " 工厂
  LGORT         TYPE LGORT_D     " 存储位置(*=全部)
  MATKL         TYPE MATKL       " 物料组(*=全部)
  BWART         TYPE BWART       " 移动类型(101/102/201/202/261/...)
  KIND          TYPE CHAR2       " 规则类型:PO_CHECK/STOCK_CHECK/BATCH_CHECK
  ACTIVE        TYPE FLAG        " 是否激活
  PRIORITY      TYPE NUMC2       " 优先级

容差配置表

复制代码
表名:ZT_STOCK_TOLERANCE
  MANDT         TYPE MANDT       " 客户端
  MATKL         TYPE MATKL       " 物料组(*=全部)
  BWART         TYPE BWART       " 移动类型
  TOL_PCT       TYPE DEC3_2      " 容差百分比(如 110% = 10)
  TOL_QTY       TYPE QUAN        " 容差数量
  CHECK_MODE    TYPE CHAR1       " S=百分比 / Q=数量 / B=两者

2.2 BAdI 实现代码(完整)

⚠️ 关键修正 :BAdI 接口名 IF_EX_MIGO_GOODS_MOVEMENT_PO ,方法名 CHECK (不是 check)。IT_GOITEM 是行项目内表,直接传入。

abap 复制代码
"=======================================================================
" BAdI 实现:ZIMPL_MIGO_VALIDATE
" BAdI:MIGO_GOODS_MOVEMENT_POSTING
" 功能:入库 PO 匹配 + 出库库存检查 + 批次效期 + 超量容差
" 创建日期:2026.09.16
"=======================================================================

CLASS cl_impl_migo_validate DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_ex_migo_goods_movement_po.
  PRIVATE SECTION.
    METHODS validate_goods_receipt
      IMPORTING iv_werks TYPE werks_d
                iv_bukrs TYPE bukrs
                iv_bwart TYPE bwart
                it_item  TYPE goitem_t
      CHANGING  cv_dont_post TYPE flag.

    METHODS validate_goods_issue
      IMPORTING iv_werks TYPE werks_d
                iv_bwart TYPE bwart
                it_item  TYPE goitem_t
      CHANGING  cv_dont_post TYPE flag.

    METHODS validate_batch_expiry
      IMPORTING iv_bwart TYPE bwart
                it_item  TYPE goitem_t
      CHANGING  cv_dont_post TYPE flag.
ENDCLASS.


CLASS cl_impl_migo_validate IMPLEMENTATION.

  METHOD if_ex_migo_goods_movement_po~check.

    DATA: lv_bwart TYPE bwart,
          lv_werks TYPE werks_d,
          lv_lgort TYPE lgort_d,
          lv_bukrs TYPE bukrs.

    lv_bwart = is_gohead-bwart.
    lv_werks = is_gohead-werks.
    lv_lgort = is_gohead-lgort.
    lv_bukrs = is_gohead-bukrs.

    " 步骤1:入库校验(101/103/105)
    IF lv_bwart IN ('101', '103', '105').
      me->validate_goods_receipt(
        EXPORTING iv_werks = lv_werks
                  iv_bukrs = lv_bukrs
                  iv_bwart = lv_bwart
                  it_item  = it_goitem
        CHANGING  cv_dont_post = cv_dont_post ).
    ENDIF.

    " 步骤2:出库校验(201/202/261/262/541/542)
    IF lv_bwart IN ('201', '202', '261', '262', '541', '542').
      me->validate_goods_issue(
        EXPORTING iv_werks = lv_werks
                  iv_bwart = lv_bwart
                  it_item  = it_goitem
        CHANGING  cv_dont_post = cv_dont_post ).
    ENDIF.

    " 步骤3:批次效期检查(涉及批次的移动类型)
    IF lv_bwart IN ('101', '102', '201', '261', '301', '302', '311', '312').
      me->validate_batch_expiry(
        EXPORTING iv_bwart = lv_bwart
                  it_item  = it_goitem
        CHANGING  cv_dont_post = cv_dont_post ).
    ENDIF.

  ENDMETHOD.


  METHOD validate_goods_receipt.

    DATA: ls_item    TYPE goitem,
          lv_po_menge TYPE menge_d,
          lv_tol_pct TYPE dec3_2,
          lv_tol_check TYPE dec3_2.

    LOOP AT it_item INTO ls_item.

      IF ls_item-ebeln IS INITIAL.
        CONTINUE.
      ENDIF.

      " 查询 PO 行项目原数量
      SELECT SINGLE menge INTO @lv_po_menge
        FROM ekpo
        WHERE ebeln = @ls_item-ebeln
          AND ebelp = @ls_item-ebelp.

      IF sy-subrc <> 0.
        MESSAGE e001(zmm_stock_enh) WITH ls_item-ebeln ls_item-ebelp.
        cv_dont_post = 'X'.
        CONTINUE.
      ENDIF.

      " 从容差配置表读取
      SELECT SINGLE tol_pct INTO @lv_tol_pct
        FROM zt_stock_tolerance
        WHERE matkl = @ls_item-matkl
          AND bwart = @iv_bwart.

      IF sy-subrc <> 0.
        lv_tol_pct = 10.
      ENDIF.

      lv_tol_check = lv_po_menge * ( 100 + lv_tol_pct ) / 100.

      IF ls_item-erfmg > lv_tol_check.
        MESSAGE e002(zmm_stock_enh)
          WITH ls_item-ebeln lv_po_menge lv_tol_pct ls_item-erfmg.
        cv_dont_post = 'X'.
      ELSEIF ls_item-erfmg > lv_po_menge.
        MESSAGE w003(zmm_stock_enh)
          WITH ls_item-ebeln lv_po_menge ls_item-erfmg.
      ENDIF.

    ENDLOOP.

  ENDMETHOD.


  METHOD validate_goods_issue.

    DATA: ls_item  TYPE goitem,
          lt_matnr TYPE TABLE OF matnr.

    " 收集物料号
    LOOP AT it_item INTO ls_item.
      APPEND ls_item-matnr TO lt_matnr.
    ENDLOOP.
    SORT lt_matnr.
    DELETE ADJACENT DUPLICATES FROM lt_matnr.

    IF lt_matnr IS INITIAL.
      RETURN.
    ENDIF.

    " 批量查询库存
    SELECT matnr, werks, lgort, labst, insme, speme
      INTO TABLE @DATA(lt_mard)
      FROM mard
      FOR ALL ENTRIES IN @lt_matnr
      WHERE matnr = @lt_matnr
        AND werks = @iv_werks.

    LOOP AT it_item INTO ls_item.

      DATA: ls_mard TYPE mard.
      READ TABLE lt_mard INTO ls_mard
        WITH KEY matnr = ls_item-matnr
                 werks = iv_werks
                 lgort = ls_item-lgort.

      DATA(lv_available) = COND meinsm(
        WHEN sy-subrc = 0
        THEN ls_mard-labst + ls_mard-insme - ls_mard-speme
        ELSE 0 ).

      IF lv_available < ls_item-erfmg.
        DATA(lv_short) = ls_item-erfmg - lv_available.
        MESSAGE e005(zmm_stock_enh)
          WITH ls_item-matnr ls_item-lgort ls_item-erfmg lv_available lv_short.
        cv_dont_post = 'X'.
      ENDIF.

    ENDLOOP.

  ENDMETHOD.


  METHOD validate_batch_expiry.

    DATA: ls_item  TYPE goitem,
          lv_today TYPE datum,
          lv_30days TYPE datum.

    lv_today  = sy-datum.
    lv_30days = sy-datum + 30.

    " 收集批次号
    DATA: lt_batch TYPE TABLE OF charg_d.
    LOOP AT it_item INTO ls_item.
      IF ls_item-charg IS NOT INITIAL.
        APPEND ls_item-charg TO lt_batch.
      ENDIF.
    ENDLOOP.
    SORT lt_batch.
    DELETE ADJACENT DUPLICATES FROM lt_batch.

    IF lt_batch IS INITIAL.
      RETURN.
    ENDIF.

    " 批量查询批次效期
    SELECT matnr, charg, vfdat, clabs
      INTO TABLE @DATA(lt_chdo)
      FROM chdo
      FOR ALL ENTRIES IN @lt_batch
      WHERE charg = @lt_batch-table_line.

    LOOP AT it_item INTO ls_item.

      IF ls_item-charg IS INITIAL.
        CONTINUE.
      ENDIF.

      DATA: ls_chdo TYPE chdo.
      READ TABLE lt_chdo INTO ls_chdo
        WITH KEY matnr = ls_item-matnr
                 charg = ls_item-charg.

      IF sy-subrc <> 0 OR ls_chdo-vfdat IS INITIAL.
        CONTINUE.
      ENDIF.

      IF ls_chdo-vfdat < lv_today.
        IF iv_bwart IN ('201', '202', '261', '541', '542').
          MESSAGE e006(zmm_stock_enh)
            WITH ls_item-matnr ls_item-charg ls_chdo-vfdat.
          cv_dont_post = 'X'.
        ELSE.
          MESSAGE w007(zmm_stock_enh)
            WITH ls_item-matnr ls_item-charg ls_chdo-vfdat.
        ENDIF.

      ELSEIF ls_chdo-vfdat <= lv_30days.
        MESSAGE w008(zmm_stock_enh)
          WITH ls_item-matnr ls_item-charg ls_chdo-vfdat.
      ENDIF.

    ENDLOOP.

  ENDMETHOD.

ENDCLASS.

2.3 消息类配置(SE91)

复制代码
消息类:ZMM_STOCK_ENH
  001:采购订单 &1 行项目 &2 不存在
  002:入库超量!&1 订单数量 &2 容差 &3% 实际 &4
  003:入库数量 &3 超过订单数量 &2(在容差 &3% 内)
  004:存储位置 &2 不存在(工厂 &1)
  005:库存不足!物料 &1 位置 &2 需要 &3 可用 &4 短缺 &5
  006:批次 &2 已过期(效期 &3),不允许出库!
  007:批次 &2 已过期(效期 &3),请确认入库是否正确
  008:批次 &2 效期 &3,30 天内到期请注意

三、批次自动确定与效期预警

3.1 MIGO 出库批次自动确定(FEFO 规则)

FEFO = First Expired, First Out(先到期先出)。在 MIGO 出库时自动选择效期最早的批次。

abap 复制代码
"=======================================================================
" BAdI 实现:ZIMPL_BATCH_AUTO_DETERMINE
" BAdI:BADI_GOITEM
" 功能:出库时自动按 FEFO 规则选择批次
" 触发:MIGO 出库操作时,SAP 标准批次确定失败时调用
"=======================================================================

CLASS cl_impl_batch_auto_determine IMPLEMENTATION.

  METHOD if_ex_badi_goitem~determine_batch.

    DATA: lv_matnr TYPE matnr,
          lv_werks TYPE werks_d,
          lv_lgort TYPE lgort_d,
          lv_need  TYPE menge_d,
          lv_bwart TYPE bwart.

    lv_matnr = is_mseg-matnr.
    lv_werks = is_mseg-werks.
    lv_lgort = is_mseg-lgort.
    lv_need  = is_mseg-menge.
    lv_bwart = is_mseg-bwart.

    " 只在出库场景自动确定批次
    IF lv_bwart NOT IN ('201', '202', '261', '541', '542', '311', '312').
      RETURN.
    ENDIF.

    " 查询该物料所有可用批次(按效期升序)
    SELECT chdo~matnr, chdo~charg, chdo~vfdat, chdo~clabs, mard~lgort
      INTO TABLE @DATA(lt_batch)
      FROM chdo
      INNER JOIN mard ON mard~matnr = chdo~matnr
      WHERE chdo~matnr = @lv_matnr
        AND chdo~clabs > 0
        AND chdo~verbm = 'X'
        AND mard~werks = @lv_werks
        AND mard~lgort = @lv_lgort
      ORDER BY chdo~vfdat ASCENDING.

    IF lt_batch IS INITIAL.
      cv_charg = space.
      RETURN.
    ENDIF.

    " 选择效期最早的、库存足够的批次
    DATA(lv_remaining) = lv_need.
    LOOP AT lt_batch INTO DATA(ls_batch).
      IF ls_batch-clabs >= lv_remaining.
        cv_charg = ls_batch-charg.
        RETURN.
      ELSE.
        lv_remaining = lv_remaining - ls_batch-clabs.
      ENDIF.
    ENDLOOP.

    cv_charg = space.

  ENDMETHOD.

ENDCLASS.

3.2 批次效期预警程序

abap 复制代码
"=======================================================================
" 后台 Job 程序:ZMM_BATCH_EXPIRY_ALERT
" 功能:扫描所有批次效期,30 天内到期 → 预警;已过期 → 锁定
" 定时执行:每天凌晨 1 点
"=======================================================================

REPORT zmm_batch_expiry_alert.

SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001.
  PARAMETERS: p_days  TYPE i DEFAULT 30,
              p_test  AS CHECKBOX,
              p_werks TYPE werks_d OBLIGATORY DEFAULT '1000'.
SELECTION-SCREEN END OF BLOCK b1.

DATA: lt_batch    TYPE TABLE OF zt_batch_effective,
      lt_expired  TYPE TABLE OF zt_batch_effective,
      lt_alert    TYPE TABLE OF zt_batch_effective,
      lt_results  TYPE TABLE OF zt_expiry_result,
      lv_today    TYPE datum,
      lv_alert    TYPE datum.

START-OF-SELECTION.

  lv_today = sy-datum.
  lv_alert = sy-datum + p_days.

  " 步骤1:查询所有效期 30 天内到期或已过期的批次
  SELECT chdo~matnr, chdo~charg, chdo~vfdat, chdo~clabs, chdo~verbm, mara~matkl
    INTO TABLE @lt_batch
    FROM chdo
    INNER JOIN mara ON mara~matnr = chdo~matnr
    WHERE chdo~vfdat <= @lv_alert
      AND chdo~vfdat IS NOT NULL
      AND chdo~verbm = 'X'.

  IF lt_batch IS INITIAL.
    WRITE: / '没有效期即将到期的批次'.
    STOP.
  ENDIF.

  " 步骤2:分类
  LOOP AT lt_batch INTO DATA(ls_batch).
    IF ls_batch-vfdat < lv_today.
      APPEND ls_batch TO lt_expired.
    ELSE.
      APPEND ls_batch TO lt_alert.
    ENDIF.
  ENDLOOP.

  " 步骤3:处理已过期批次
  IF lt_expired IS NOT INITIAL AND p_test <> 'X'.
    PERFORM lock_expired_batches USING lt_expired CHANGING lt_results.
  ENDIF.

  " 步骤4:处理即将到期批次(发预警邮件)
  IF lt_alert IS NOT INITIAL.
    PERFORM send_alert_email USING lt_alert.
  ENDIF.

  " 步骤5:输出汇总
  PERFORM output_summary USING lt_expired lt_alert lt_results p_test.


"=======================================================================
" 子程序 1:锁定已过期批次
"=======================================================================
FORM lock_expired_batches
  USING    it_expired  TYPE TABLE OF zt_batch_effective
  CHANGING ct_results  TYPE TABLE OF zt_expiry_result.

  LOOP AT it_expired INTO DATA(ls_exp).

    DATA(lv_guid) = cl_system_uuid=>create_uuid_c32_static( ).

    UPDATE chdo
       SET verbm     = ''
     WHERE matnr = @ls_exp-matnr
       AND charg = @ls_exp-charg.

    APPEND VALUE #(
      guid   = lv_guid
      matnr  = ls_exp-matnr
      charg  = ls_exp-charg
      vfdat  = ls_exp-vfdat
      status = 'LOCKED'
      msg    = '批次已锁定(效期过期)'
      ts     = timestampl_current( ) ) TO ct_results.

  ENDLOOP.

  COMMIT WORK.

ENDFORM.


"=======================================================================
" 子程序 2:发预警邮件
"=======================================================================
FORM send_alert_email
  USING it_alert TYPE TABLE OF zt_batch_effective.

  DATA: lv_email_body TYPE string.

  lv_email_body =
    |<html><body><h3>批次效期预警({ p_days } 天内到期)</h3>|
    && |<table border="1"><tr>|
    && |<th>物料号</th><th>批次</th><th>效期</th><th>剩余天数</th><th>数量</th>|
    && |</tr>|.

  LOOP AT it_alert INTO DATA(ls_alert).
    DATA(lv_remain) = ls_alert-vfdat - sy-datum.
    lv_email_body = lv_email_body
      && |<tr><td>{ ls_alert-matnr }</td><td>{ ls_alert-charg }</td>|
      && |<td>{ ls_alert-vfdat }</td><td>{ lv_remain }天</td>|
      && |<td>{ ls_alert-clabs }</td></tr>|.
  ENDLOOP.

  lv_email_body = lv_email_body && |</table></body></html>|.

  TRY.
      DATA(lo_message) = cl_document_bcs=>create_document(
        i_type    = 'HTML'
        i_text    = lv_email_body
        i_subject = |【预警】批次效期 { p_days } 天内到期 - { sy-datum }| ).

      DATA(lo_sender)    = cl_sapuser_bcs=>create( sy-uname ).
      DATA(lo_recipient) = cl_sapuser_bcs=>create( sy-uname ).

      DATA(lo_bcs) = cl_bcs=>create( ).
      lo_bcs->set_document( lo_message ).
      lo_bcs->set_sender( lo_sender ).
      lo_bcs->add_recipient( lo_recipient ).

      IF lo_bcs->send( ) = 'X'.
        WRITE: / '预警邮件发送成功'.
      ENDIF.

    CATCH cx_bcs INTO DATA(lx_bcs).
      WRITE: / |邮件发送异常:{ lx_bcs->get_text( ) }|.
  ENDTRY.

ENDFORM.


"=======================================================================
" 子程序 3:输出汇总
"=======================================================================
FORM output_summary
  USING it_expired TYPE TABLE OF zt_batch_effective
        it_alert   TYPE TABLE OF zt_batch_effective
        it_results TYPE TABLE OF zt_expiry_result
        iv_test    TYPE flag.

  WRITE: / '='(60).
  WRITE: /  |批次效期预警扫描完成|.
  WRITE: / '='(60).
  WRITE: /  |工厂:{ p_werks }|.
  WRITE: /  |预警天数:{ p_days }|.
  WRITE: /  |扫描时间:{ timestampl_current( ) }|.
  WRITE: / '-'(60).
  WRITE: /  |已过期批次:{ lines( it_expired ) } 个|.
  WRITE: /  |即将到期批次:{ lines( it_alert ) } 个|.

  IF iv_test = 'X'.
    WRITE: /  |【测试模式】未锁定任何批次|.
  ELSE.
    WRITE: /  |已锁定批次:{ lines( it_results ) } 个|.
  ENDIF.
  WRITE: / '='(60).

ENDFORM.

四、库龄分析与库存盘点报表

4.1 库龄分析报表

abap 复制代码
"=======================================================================
" 报表程序:ZMM_STOCK_AGEING
" 功能:库存库龄分析(30/60/90/180/365 天分段)
" 数据源:MARD(库存)+ MKPF/MSEG(最后一次出入库日期)
"=======================================================================

REPORT zmm_stock_ageing.

SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001.
  PARAMETERS: p_werks TYPE werks_d OBLIGATORY DEFAULT '1000'.
  SELECT-OPTIONS: s_matkl FOR mara-matkl.
SELECTION-SCREEN END OF BLOCK b1.

TYPES: BEGIN OF ty_ageing_result,
         matnr          TYPE matnr,
         meins          TYPE meins,
         matkl          TYPE matkl,
         matkl_bez      TYPE wwgbe,
         labst          TYPE labst,
         insme          TYPE insme,
         speme          TYPE speme,
         total_stock    TYPE meinsm,
         last_move_date TYPE datum,
         ageing_days    TYPE i,
         ageing_level   TYPE char2,
       END OF ty_ageing_result.

DATA: gt_results TYPE TABLE OF ty_ageing_result.

START-OF-SELECTION.
  PERFORM get_stock_data CHANGING gt_results.
  PERFORM calc_ageing   CHANGING gt_results.
  PERFORM display_alv   USING    gt_results.


"=======================================================================
" 子程序 1:获取库存数据
"=======================================================================
FORM get_stock_data CHANGING ct_data TYPE TABLE OF ty_ageing_result.

  SELECT mard~matnr, mard~labst, mard~insme, mard~speme,
         mara~matkl, mara~meins, twt~wwgbe
    INTO TABLE @ct_data
    FROM mard
    INNER JOIN mara ON mara~matnr = mard~matnr
    LEFT JOIN twt   ON twt~matkl = mara~matkl
    WHERE mard~werks = @p_werks
      AND mara~matkl IN @s_matkl.

  LOOP AT ct_data INTO DATA(ls_data).
    ls_data-total_stock = ls_data-labst + ls_data-insme - ls_data-speme.
    MODIFY ct_data FROM ls_data.
  ENDLOOP.

  IF ct_data IS INITIAL.
    RETURN.
  ENDIF.

  " 收集物料号
  DATA: lt_matnr TYPE TABLE OF matnr.
  LOOP AT ct_data INTO DATA(ls_d).
    APPEND ls_d-matnr TO lt_matnr.
  ENDLOOP.
  SORT lt_matnr.
  DELETE ADJACENT DUPLICATES FROM lt_matnr.

  " 查询每个物料最新移动日期
  SELECT mseg~matnr, mkpf~budat AS last_move
    INTO TABLE @DATA(lt_last_move)
    FROM mseg
    INNER JOIN mkpf ON mkpf~mblnr = mseg~mblnr AND mkpf~jahr = mseg~jahr
    FOR ALL ENTRIES IN @lt_matnr
    WHERE mseg~matnr = @lt_matnr-table_line
      AND mseg~werks = @p_werks
    ORDER BY mseg~matnr, mkpf~budat DESCENDING.

  SORT lt_last_move BY matnr DESCENDING.
  DELETE ADJACENT DUPLICATES FROM lt_last_move COMPARING matnr.

  LOOP AT ct_data INTO DATA(ls_data2).
    READ TABLE lt_last_move INTO DATA(ls_move)
      WITH KEY matnr = ls_data2-matnr.
    IF sy-subrc = 0.
      ls_data2-last_move_date = ls_move-last_move.
    ELSE.
      SELECT SINGLE erdat INTO @DATA(lv_erdat)
        FROM mara WHERE matnr = @ls_data2-matnr.
      ls_data2-last_move_date = lv_erdat.
    ENDIF.
    MODIFY ct_data FROM ls_data2.
  ENDLOOP.

ENDFORM.


"=======================================================================
" 子程序 2:计算库龄分级
"=======================================================================
FORM calc_ageing CHANGING ct_data TYPE TABLE OF ty_ageing_result.

  DATA(lv_today) = sy-datum.

  LOOP AT ct_data INTO DATA(ls_data).

    IF ls_data-last_move_date IS INITIAL.
      ls_data-ageing_days  = 9999.
      ls_data-ageing_level = '06'.
      MODIFY ct_data FROM ls_data.
      CONTINUE.
    ENDIF.

    ls_data-ageing_days = lv_today - ls_data-last_move_date.

    CASE.
      WHEN ls_data-ageing_days <= 30.  ls_data-ageing_level = '01'.
      WHEN ls_data-ageing_days <= 60.  ls_data-ageing_level = '02'.
      WHEN ls_data-ageing_days <= 90.  ls_data-ageing_level = '03'.
      WHEN ls_data-ageing_days <= 180. ls_data-ageing_level = '04'.
      WHEN ls_data-ageing_days <= 365. ls_data-ageing_level = '05'.
      WHEN OTHERS.                     ls_data-ageing_level = '06'.
    ENDCASE.

    MODIFY ct_data FROM ls_data.

  ENDLOOP.

ENDFORM.


"=======================================================================
" 子程序 3:ALV 显示
"=======================================================================
FORM display_alv USING it_data TYPE TABLE OF ty_ageing_result.

  DATA: lo_alv TYPE REF TO cl_salv_table.

  IF it_data IS INITIAL.
    WRITE: / '没有库存数据'.
    STOP.
  ENDIF.

  TRY.
      cl_salv_table=>factory(
        IMPORTING r_salv_table = lo_alv
        CHANGING  t_table      = it_data ).

      lo_alv->get_columns( )->set_optimize( 'X' ).

      " 设置列标题
      lo_alv->get_columns( )->get_column( 'AGEING_DAYS'  )->set_medium_text( '库龄天数' ).
      lo_alv->get_columns( )->get_column( 'AGEING_LEVEL' )->set_medium_text( '库龄分级' ).

      lo_alv->display( ).

    CATCH cx_salv_msg INTO DATA(lx_msg).
      MESSAGE lx_msg TYPE 'E'.
  ENDTRY.

ENDFORM.

常见问题与排查

  • Q1:MIGO_GOODS_MOVEMENT_POSTING 和 MB_MIGO_BADI 选哪个?

    A:功能定位不同。MIGO_GOODS_MOVEMENT_POSTING过账前最后一步 触发,适合做最终校验(本篇选这个);MB_MIGO_BADIMIGO 界面加载/用户操作时触发,适合做界面增强、子屏幕、动态字段修改等交互逻辑。两者可以组合使用。

  • Q2:MIGO 校验中设置 CV_DONT_POST = 'X',SAP 会 ROLLBACK 吗?

    A:会。设置 CV_DONT_POST = 'X' 后,SAP 会:①阻止物料凭证过账;②回滚本次 MIGO 操作中的所有数据修改;③给出 E 类型消息提示。但 MIGO 界面不会关闭,用户可以修正数据后重新提交------这比直接抛出 DUMP 友好得多。

  • Q3:批次自动确定 BADI_GOITEM~DETERMINE_BATCH 什么时候被调用?

    A:SAP 标准批次确定(MIGO 界面自动选批次、或用户按"批次确定"按钮)失败时,才会调用你的 BAdI 实现。也就是说:SAP 内部逻辑先试一遍,试不出来再找你。如果你想完全接管批次确定,需要实现 SAP 的增强配置(事务码 OMSJ)。

  • Q4:库龄分析报表中,"最后一次物料移动日期"怎么查才高效?

    A:MKPF/MSEG 是大表(几千万行),不能全表扫。推荐做法:① FOR ALL ENTRIES 只查你需要的物料号;② ORDER BY BUDAT DESCENDING + DELETE ADJACENT DUPLICATES 去重取最新;③如果还慢,建议建索引(MSEG~MATNR+WERKS+BUDAT)或做 CQE 视图。

  • Q5:批次效期扫描为什么用后台 Job,不在 MIGO 中做?

    A:三个原因:① MIGO 操作是前台同步 ,效期检查如果需要复杂计算会拖慢用户操作;② 后台 Job 是异步批量,可以一次处理全工厂所有批次;③ 预警邮件这种通知逻辑不应该在用户操作中做------用户只想知道"这次 MIGO 为什么被挡了",不想收到一堆邮件。

  • Q6:出入库校验中,工厂和存储位置的权限怎么加?

    A:MIGO 本身有标准权限检查(M_MSEG_WWA/M_MSEG_WWP),增强中不需要重复检查。但如果需要更细粒度 的权限(比如某些人只能操作特定存储位置),可以在 MIGO_GOODS_MOVEMENT_POSTING 中读取用户的权限对象(AS03/AS04),不满足则 E 消息阻止。


总结

库存管理三大增强场景速查

场景 实现方式 BAdI / 函数 触发时机
MIGO 出入库校验 MIGO_GOODS_MOVEMENT_POSTING CHECK 方法 过账前最后校验
批次效期预警 定时 Job ZMM_BATCH_EXPIRY_ALERT 每天凌晨
库龄分析报表 ALV 报表 ZMM_STOCK_AGEING 用户执行

关键代码避坑速查

错误做法 正确做法
BAdI 接口方法名 if_ex_migo_goods_movement_po~check CHECK(大写)
库存字段来源 marc~labst(不存在) mard~labst(MARD 表)
批次效期字段 mchb~vfdat(不存在) chdo~vfdat(CHDO 表)
库龄批量查询 循环中 SELECT FOR ALL ENTRIES + ORDER BY DESCENDING
更新函数提交 COMMIT WORK 不写,让框架提交
CV_DONT_POST 后 未中断循环 每次都检查并 CONTINUE

MM 模块增强全景(8 篇中的 4 篇已完成)

#mermaid-svg-XNHvqWwfb7ktlfiq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XNHvqWwfb7ktlfiq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XNHvqWwfb7ktlfiq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XNHvqWwfb7ktlfiq .error-icon{fill:#552222;}#mermaid-svg-XNHvqWwfb7ktlfiq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XNHvqWwfb7ktlfiq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XNHvqWwfb7ktlfiq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XNHvqWwfb7ktlfiq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XNHvqWwfb7ktlfiq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XNHvqWwfb7ktlfiq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XNHvqWwfb7ktlfiq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XNHvqWwfb7ktlfiq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XNHvqWwfb7ktlfiq .marker.cross{stroke:#333333;}#mermaid-svg-XNHvqWwfb7ktlfiq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XNHvqWwfb7ktlfiq p{margin:0;}#mermaid-svg-XNHvqWwfb7ktlfiq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XNHvqWwfb7ktlfiq .cluster-label text{fill:#333;}#mermaid-svg-XNHvqWwfb7ktlfiq .cluster-label span{color:#333;}#mermaid-svg-XNHvqWwfb7ktlfiq .cluster-label span p{background-color:transparent;}#mermaid-svg-XNHvqWwfb7ktlfiq .label text,#mermaid-svg-XNHvqWwfb7ktlfiq span{fill:#333;color:#333;}#mermaid-svg-XNHvqWwfb7ktlfiq .node rect,#mermaid-svg-XNHvqWwfb7ktlfiq .node circle,#mermaid-svg-XNHvqWwfb7ktlfiq .node ellipse,#mermaid-svg-XNHvqWwfb7ktlfiq .node polygon,#mermaid-svg-XNHvqWwfb7ktlfiq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XNHvqWwfb7ktlfiq .rough-node .label text,#mermaid-svg-XNHvqWwfb7ktlfiq .node .label text,#mermaid-svg-XNHvqWwfb7ktlfiq .image-shape .label,#mermaid-svg-XNHvqWwfb7ktlfiq .icon-shape .label{text-anchor:middle;}#mermaid-svg-XNHvqWwfb7ktlfiq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XNHvqWwfb7ktlfiq .rough-node .label,#mermaid-svg-XNHvqWwfb7ktlfiq .node .label,#mermaid-svg-XNHvqWwfb7ktlfiq .image-shape .label,#mermaid-svg-XNHvqWwfb7ktlfiq .icon-shape .label{text-align:center;}#mermaid-svg-XNHvqWwfb7ktlfiq .node.clickable{cursor:pointer;}#mermaid-svg-XNHvqWwfb7ktlfiq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XNHvqWwfb7ktlfiq .arrowheadPath{fill:#333333;}#mermaid-svg-XNHvqWwfb7ktlfiq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XNHvqWwfb7ktlfiq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XNHvqWwfb7ktlfiq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XNHvqWwfb7ktlfiq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XNHvqWwfb7ktlfiq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XNHvqWwfb7ktlfiq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XNHvqWwfb7ktlfiq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XNHvqWwfb7ktlfiq .cluster text{fill:#333;}#mermaid-svg-XNHvqWwfb7ktlfiq .cluster span{color:#333;}#mermaid-svg-XNHvqWwfb7ktlfiq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XNHvqWwfb7ktlfiq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XNHvqWwfb7ktlfiq rect.text{fill:none;stroke-width:0;}#mermaid-svg-XNHvqWwfb7ktlfiq .icon-shape,#mermaid-svg-XNHvqWwfb7ktlfiq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XNHvqWwfb7ktlfiq .icon-shape p,#mermaid-svg-XNHvqWwfb7ktlfiq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XNHvqWwfb7ktlfiq .icon-shape .label rect,#mermaid-svg-XNHvqWwfb7ktlfiq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XNHvqWwfb7ktlfiq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XNHvqWwfb7ktlfiq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XNHvqWwfb7ktlfiq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MM 模块增强开发体系
已完成(4 篇)
待完成(4 篇)
采购订单增强

ME_PROCESS_PO_CUST
物料主数据增强

BADI_MATERIAL_CHECK + 批量导入
采购申请增强

ME_PROCESS_REQ_CUST + 自动转单
库存管理增强

MIGO + 批次 + 库龄(本篇)
第五篇:发票校验增强

MIRO 三单匹配 + 自动过账
第六篇:供应商主数据增强

字段扩展 + 分级管控 + 同步
第七篇:MM 报表开发

采购执行 + 库存分析 + 供应商绩效
第八篇:MM 接口开发

SRM / WMS / MDG 多系统对接

本篇与下一篇的衔接

本篇(MM 库存管理) 下一篇(MM 发票校验)
MIGO 入库/出库校验 MIRO 发票三单匹配
MIGO_GOODS_MOVEMENT_POSTING EXIT_SAPLMRMH_014(用户出口)
PO 匹配 + 库存检查 PO + GR + Invoice 三单匹配
批次效期 FEFO 发票金额容差 + 税额校验

下一篇预告:《MM 模块发票校验开发:三单匹配、增强校验与财务过账逻辑实现》------采购流程的最后一环:发票校验(MIRO)。下一篇讲解 MIRO 发票校验增强开发、采购订单/入库单/发票三单匹配逻辑开发、发票自动过账到 FI 模块的接口开发、发票校验差异处理逻辑开发的方法,附"发票金额超订单容差自动拦截""税额自动校验"的实现代码。

作者 :爱喝水的鱼丶

版本记录:2026 年 9 月

💬 你在库存管理上做过哪些增强?MIGO 增强最容易踩的坑是什么?FEFO 批次自动确定和人工选批次相比,你的项目用哪种方式?欢迎分享你的库存管理增强经验。

相关推荐
AI服务老曹2 小时前
告警回调不是配上就能跑:性能优化里的关键参数(视频分析接入飞书告警实践)
性能优化·ai视频分析告警接口·告警回调
Cx330❀1 天前
【Linux网络】网络层 IP 协议:从网段划分到内核源码解析
大数据·linux·服务器·网络·tcp/ip·性能优化·langchain
goyeer1 天前
20.[SAP ABAP] DATA与TYPES异同
sap·abap·fico·fi·co·mm
yume_sibai1 天前
12-Rust 性能优化指南(性能分析 + 内存优化 + SIMD + 并发优化 + 编译器优化 + 基准测试)
开发语言·性能优化·rust
爱喝水的鱼丶1 天前
SAP-ABAP:SAP MM 模块物料主数据批量导入与增强开发:字段扩展、校验逻辑与批量导入工具开发
开发语言·性能优化·sap·abap·增强·经验交流·mm
goyeer1 天前
21.[SAP ABAP] DATA关键字详解
sap·abap·fi·mm·opensql
RobinDevNotes1 天前
用 Profile 揪出大模型训练性能瓶颈
python·性能优化
爱丶不疚1 天前
Electron: 你是否需要对 Preload 开启 nodeIntegration?
安全·性能优化·electron
修电脑的猫2 天前
在中国使用 Claude Code 解决 403 错误(VS CODE)
ai·sap