SAP-ABAP:MM 模块供应商主数据开发:字段扩展、分级管控与同步接口开发

MM 模块供应商主数据开发:字段扩展、分级管控与同步接口开发

博客简介:聚焦供应商全生命周期管理开发,讲解供应商主数据字段扩展、供应商分级校验逻辑开发、供应商主数据与 MDG 系统同步接口开发、供应商黑名单管控逻辑开发的完整流程,附"供应商资质效期自动校验""供应商信用等级自动赋值"的实操代码,实现供应商数据的标准化与跨系统一致性。


"P2P 流程的另一端------供应商主数据,是采购活动的源头。供应商资质过期、信用评级下降、黑名单管控......这些全靠增强实现。本篇聚焦供应商主数据全生命周期,用字段扩展 → 资质效期校验 → 信用等级自动赋值 → MDG 同步四个场景完整演示。"

本篇聚焦供应商全生命周期管理开发,讲解供应商主数据字段扩展、供应商分级校验逻辑开发、供应商主数据与 MDG 系统同步接口开发、供应商黑名单管控逻辑开发的完整流程,附"供应商资质效期自动校验""供应商信用等级自动赋值"的实操代码。


📖 写在前面

本篇定位

供应商主数据(Vendor Master Data)是 MM 模块的基础------PO 创建、GR 入库、MIRO 发票校验都要引用供应商。供应商数据质量直接影响采购活动的效率和合规性。本篇聚焦三个高频增强场景:①字段扩展与资质效期自动校验 ;②供应商信用等级自动赋值 ;③MDG 主数据治理系统同步接口,所有代码基于 SAP NetWeaver 7.51。

本篇适合谁读

  • 需要在 MM 模块做供应商主数据增强的开发者
  • 负责供应商资质和信用管理的业务分析师
  • 搭建 SAP-MDG 主数据同步架构的架构师

贯穿场景概览

#mermaid-svg-4KvdbBgibRNov3CM{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-4KvdbBgibRNov3CM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4KvdbBgibRNov3CM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4KvdbBgibRNov3CM .error-icon{fill:#552222;}#mermaid-svg-4KvdbBgibRNov3CM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4KvdbBgibRNov3CM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4KvdbBgibRNov3CM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4KvdbBgibRNov3CM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4KvdbBgibRNov3CM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4KvdbBgibRNov3CM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4KvdbBgibRNov3CM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4KvdbBgibRNov3CM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4KvdbBgibRNov3CM .marker.cross{stroke:#333333;}#mermaid-svg-4KvdbBgibRNov3CM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4KvdbBgibRNov3CM p{margin:0;}#mermaid-svg-4KvdbBgibRNov3CM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4KvdbBgibRNov3CM .cluster-label text{fill:#333;}#mermaid-svg-4KvdbBgibRNov3CM .cluster-label span{color:#333;}#mermaid-svg-4KvdbBgibRNov3CM .cluster-label span p{background-color:transparent;}#mermaid-svg-4KvdbBgibRNov3CM .label text,#mermaid-svg-4KvdbBgibRNov3CM span{fill:#333;color:#333;}#mermaid-svg-4KvdbBgibRNov3CM .node rect,#mermaid-svg-4KvdbBgibRNov3CM .node circle,#mermaid-svg-4KvdbBgibRNov3CM .node ellipse,#mermaid-svg-4KvdbBgibRNov3CM .node polygon,#mermaid-svg-4KvdbBgibRNov3CM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4KvdbBgibRNov3CM .rough-node .label text,#mermaid-svg-4KvdbBgibRNov3CM .node .label text,#mermaid-svg-4KvdbBgibRNov3CM .image-shape .label,#mermaid-svg-4KvdbBgibRNov3CM .icon-shape .label{text-anchor:middle;}#mermaid-svg-4KvdbBgibRNov3CM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4KvdbBgibRNov3CM .rough-node .label,#mermaid-svg-4KvdbBgibRNov3CM .node .label,#mermaid-svg-4KvdbBgibRNov3CM .image-shape .label,#mermaid-svg-4KvdbBgibRNov3CM .icon-shape .label{text-align:center;}#mermaid-svg-4KvdbBgibRNov3CM .node.clickable{cursor:pointer;}#mermaid-svg-4KvdbBgibRNov3CM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4KvdbBgibRNov3CM .arrowheadPath{fill:#333333;}#mermaid-svg-4KvdbBgibRNov3CM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4KvdbBgibRNov3CM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4KvdbBgibRNov3CM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4KvdbBgibRNov3CM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4KvdbBgibRNov3CM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4KvdbBgibRNov3CM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4KvdbBgibRNov3CM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4KvdbBgibRNov3CM .cluster text{fill:#333;}#mermaid-svg-4KvdbBgibRNov3CM .cluster span{color:#333;}#mermaid-svg-4KvdbBgibRNov3CM 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-4KvdbBgibRNov3CM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4KvdbBgibRNov3CM rect.text{fill:none;stroke-width:0;}#mermaid-svg-4KvdbBgibRNov3CM .icon-shape,#mermaid-svg-4KvdbBgibRNov3CM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4KvdbBgibRNov3CM .icon-shape p,#mermaid-svg-4KvdbBgibRNov3CM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4KvdbBgibRNov3CM .icon-shape .label rect,#mermaid-svg-4KvdbBgibRNov3CM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4KvdbBgibRNov3CM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4KvdbBgibRNov3CM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4KvdbBgibRNov3CM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 供应商主数据增强三大场景
场景1:资质效期自动校验 + 分级管控
场景2:信用等级自动赋值
场景3:MDG 系统同步接口
BADI_BUSINESSPLANT_UPDATE

CI_LFA1 扩展 + 定时 Job
定时 Job + 四维加权算法

信用等级回写 LFA1
ALE/IDoc + RFC 查询接口

SAP ⇄ MDG 双向同步

版本说明:本篇内容基于 SAP NetWeaver 7.51,供应商主数据核心逻辑在 ECC 6.0+ 版本中可用。MDG 同步接口为通用 SAP 主数据集成方案。


一、供应商主数据增强架构

1.1 核心增强点

⚠️ 技术提示 :SAP 供应商主数据在 ECC 中是 LFA1 体系 (供应商主数据),在 S/4HANA 中逐渐迁移到 BUT000 业务伙伴体系 。不同体系下的 BAdI 名称不同,开发前请用 SE18 确认当前系统可用对象。

增强类型 增强名称 触发时机 适用事务码 推荐场景
BAdI BADI_BUSINESSPLANT 供应商保存前 XK01/XK02 字段校验、资质效期检查
BAdI BADI_PARTNER_UPDATE 供应商数据修改时 XK01/XK02 修改后自动同步 MDG
用户出口 EXIT_SAPMF02D_001 供应商保存前(老方式) XK01/XK02 老项目兼容
隐式增强 函数/方法首尾 BAPI_BUSINESSPARTNER_CREATE SE37 BAdI 没覆盖时的补充

1.2 供应商核心表与追加结构

SAP 表 说明 追加结构 扩展对象
LFA1 供应商一般数据 CI_LFA1 资质、信用、黑名单标记
LFB1 公司代码视图 CI_LFB1 财务相关自定义字段
LFBK 银行数据 CI_LFBK 银行自定义字段
LFM1 采购组织视图 CI_LFM1 采购相关字段
LFM2 采购组织下的供应商主数据 CI_LFM2 采购组织级扩展

⚠️ 常见坑 :供应商的 BUKRS(公司代码)字段在 LFB1 (公司代码视图)中,不在 LFA1 中。开发时涉及公司代码的查询,必须通过 LFB1 关联。

1.3 关键 BAdI 接口方法

BADI_BUSINESSPLANT(供应商更新 BAdI):

方法名 功能 适用场景
BEFORE_UPDATE 更新前 字段校验、资质效期检查
AFTER_UPDATE 更新后 写日志、触发下游通知、同步 MDG
CHECK 保存前校验 综合校验、黑名单检查

二、供应商主数据字段扩展

2.1 CI_LFA1 资质与信用字段扩展

复制代码
SE11 → 创建 CI_LFA1(SAP 预留的供应商追加结构容器)

在 CI_LFA1 中定义自定义字段:
  ┌─────────────────────┬──────────┬──────────┬──────────────────────────────┐
  │ 字段名               │ 数据类型  │ 长度     │ 说明                          │
  ├─────────────────────┼──────────┼──────────┼──────────────────────────────┤
  │ ZISO9001             │ FLAG     │ 1        │ ISO9001 认证标记              │
  │ ZISO9001_NO          │ CHAR     │ 30       │ ISO9001 证书号                │
  │ ZISO9001_EXP         │ DATS     │ 8        │ ISO9001 有效期                │
  │ ZISO14001            │ FLAG     │ 1        │ ISO14001 认证标记             │
  │ ZISO14001_EXP        │ DATS     │ 8        │ ISO14001 有效期               │
  │ ZCCC_FLAG            │ FLAG     │ 1        │ CCC 认证标记                  │
  │ ZCCC_NO              │ CHAR     │ 30       │ CCC 证书号                    │
  │ ZCCC_EXP             │ DATS     │ 8        │ CCC 有效期                    │
  │ ZCREDIT_LEVEL        │ CHAR     │ 1        │ 信用等级(A/B/C/D)           │
  │ ZCREDIT_SCORE        │ DEC      │ 5        │ 信用评分(0-100)             │
  │ ZCREDIT_UPDATE       │ DATS     │ 8        │ 上次信用评级日期               │
  │ ZBLACKLIST_FLAG      │ FLAG     │ 1        │ 黑名单标记                    │
  │ ZBLACKLIST_REASON    │ CHAR     │ 100      │ 黑名单原因                    │
  │ ZBLACKLIST_DATE      │ DATS     │ 8        │ 拉黑日期                      │
  │ ZBLACKLIST_BY        │ CHAR     │ 12       │ 拉黑人                        │
  │ ZUNBLOCK_DATE        │ DATS     │ 8        │ 解封日期(用于临时黑名单)     │
  │ ZVENDOR_CATEGORY     │ CHAR     │ 5        │ 供应商分类(RAW/GEN/SRV)     │
  │ ZVENDOR_GRADE        │ CHAR     │ 1        │ 供应商等级(1/2/3/4 级)       │
  └─────────────────────┴──────────┴──────────┴──────────────────────────────┘

激活 CI_LFA1 → 自动追加到 LFA1 表

2.2 供应商资质配置表

复制代码
表名:ZT_VENDOR_CERT_REQUIREMENT
  MANDT         TYPE MANDT       " 客户端
  MATKL         TYPE MATKL       " 物料组(*=所有物料)
  CERT_TYPE     TYPE CHAR15      " 证书类型:ISO9001/ISO14001/CCC
  REQUIRED      TYPE FLAG        " 是否必填
  WARN_DAYS     TYPE NUMC3       " 提前多少天预警
  BLOCK_ON_EXP  TYPE FLAG        " 过期是否自动屏蔽
  ACTIVE        TYPE FLAG        " 是否激活

  示例数据:
  ┌───────┬───────────┬──────────┬───────────┬────────────┬────────┐
  │ MATKL │ CERT_TYPE │ REQUIRED │ WARN_DAYS │ BLOCK_ON_  │ ACTIVE │
  │       │           │          │           │ EXP        │        │
  ├───────┼───────────┼──────────┼───────────┼────────────┼────────┤
  │ *     │ ISO9001   │ X        │ 30        │ X          │ X      │
  │ Z001  │ CCC       │ X        │ 30        │ X          │ X      │
  │ *     │ ISO14001  │          │ 60        │            │ X      │
  └───────┴───────────┴──────────┴───────────┴────────────┴────────┘

三、资质效期自动校验

3.1 BADI_BUSINESSPLANT 实现代码

⚠️ 关键修正

  1. CI_LFA1 追加字段需通过 ASSIGN COMPONENT 动态访问(编译期可能识别不到新字段)
  2. 供应商的 BUKRS(公司代码)在 LFB1 表中,不在 LFA1
  3. 更新函数内部不要写 COMMIT WORK,由框架统一提交
abap 复制代码
"=======================================================================
" BAdI 实现:ZIMPL_VENDOR_CERT_CHECK
" BAdI:BADI_BUSINESSPLANT
" 功能:供应商资质效期校验
" 触发:XK01/XK02 供应商保存前
" 创建日期:2026.09.20
"=======================================================================

CLASS cl_impl_vendor_cert_check DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_ex_businesspartner.
  PRIVATE SECTION.
    METHODS get_custom_field
      IMPORTING iv_field_name TYPE string
                is_lfa1       TYPE lfa1
      RETURNING VALUE(rv_value) TYPE string.
ENDCLASS.


CLASS cl_impl_vendor_cert_check IMPLEMENTATION.

  METHOD if_ex_businesspartner~before_update.

    DATA: lv_lifnr       TYPE lifnr,
          lv_iso9001     TYPE flag,
          lv_iso9001_exp TYPE datum,
          lv_ccc_flag    TYPE flag,
          lv_ccc_exp     TYPE datum,
          lv_today       TYPE datum.

    " 获取供应商号(BAdI 参数名因版本而异,通常为 IS_BUSINESSPARTNER 或 IV_PARTNER)
    lv_lifnr = is_lfa1-lifnr.

    IF lv_lifnr IS INITIAL.
      RETURN.
    ENDIF.

    lv_today = sy-datum.

    " 通过动态 ASSIGN 访问 CI_LFA1 追加字段
    lv_iso9001     = me->get_custom_field( iv_field_name = 'ZISO9001'     is_lfa1 = is_lfa1 ).
    lv_iso9001_exp = me->get_custom_field( iv_field_name = 'ZISO9001_EXP' is_lfa1 = is_lfa1 ).
    lv_ccc_flag    = me->get_custom_field( iv_field_name = 'ZCCC_FLAG'    is_lfa1 = is_lfa1 ).
    lv_ccc_exp     = me->get_custom_field( iv_field_name = 'ZCCC_EXP'     is_lfa1 = is_lfa1 ).

    " ================================================================
    " 校验1:资质效期已过期 → 警告
    " ================================================================
    IF lv_iso9001 = 'X' AND lv_iso9001_exp IS NOT INITIAL.

      SELECT SINGLE warn_days, block_on_exp
        INTO (@DATA(lv_warn_days), @DATA(lv_block_exp))
        FROM zt_vendor_cert_requirement
        WHERE cert_type = 'ISO9001'
          AND active    = 'X'.

      IF sy-subrc <> 0.
        lv_warn_days = 30.
      ENDIF.

      DATA(lv_warn_date) = lv_today + lv_warn_days.

      IF lv_iso9001_exp < lv_today.
        MESSAGE w001(zmm_vendor_enh)
          WITH lv_lifnr 'ISO9001' lv_iso9001_exp.
      ELSEIF lv_iso9001_exp <= lv_warn_date.
        MESSAGE w002(zmm_vendor_enh)
          WITH lv_lifnr 'ISO9001' lv_iso9001_exp.
      ENDIF.
    ENDIF.

    " CCC 效期检查(同理)
    IF lv_ccc_flag = 'X' AND lv_ccc_exp IS NOT INITIAL.

      SELECT SINGLE warn_days
        INTO @DATA(lv_warn_days2)
        FROM zt_vendor_cert_requirement
        WHERE cert_type = 'CCC'
          AND active    = 'X'.

      IF sy-subrc <> 0.
        lv_warn_days2 = 30.
      ENDIF.

      DATA(lv_warn_date2) = lv_today + lv_warn_days2.

      IF lv_ccc_exp < lv_today.
        MESSAGE w001(zmm_vendor_enh) WITH lv_lifnr 'CCC' lv_ccc_exp.
      ELSEIF lv_ccc_exp <= lv_warn_date2.
        MESSAGE w002(zmm_vendor_enh) WITH lv_lifnr 'CCC' lv_ccc_exp.
      ENDIF.
    ENDIF.

    " ================================================================
    " 校验2:黑名单供应商自动解封逻辑
    " ================================================================
    DATA(lv_blacklist) = me->get_custom_field(
      iv_field_name = 'ZBLACKLIST_FLAG'
      is_lfa1       = is_lfa1 ).

    DATA(lv_unblock_date) = me->get_custom_field(
      iv_field_name = 'ZUNBLOCK_DATE'
      is_lfa1       = is_lfa1 ).

    IF lv_blacklist = 'X'.

      " 临时黑名单(设置了解封日期且今天已过解封日)→ 自动解封
      IF lv_unblock_date IS NOT INITIAL AND lv_unblock_date <= lv_today.

        UPDATE lfa1
           SET zblacklist_flag   = ''
         WHERE lifnr = @lv_lifnr.

        MESSAGE i003(zmm_vendor_enh) WITH lv_lifnr.

      ELSEIF lv_unblock_date IS INITIAL.
        " 永久黑名单 → 阻止保存
        MESSAGE e004(zmm_vendor_enh)
          WITH lv_lifnr
               me->get_custom_field( iv_field_name = 'ZBLACKLIST_REASON'
                                      is_lfa1       = is_lfa1 ).
      ENDIF.
    ENDIF.

  ENDMETHOD.


  METHOD if_ex_businesspartner~after_update.

    " 保存后写日志 + 触发 MDG 同步通知
    PERFORM write_vendor_update_log
      USING is_lfa1-lifnr.

  ENDMETHOD.


  METHOD get_custom_field.

    FIELD-SYMBOLS: <fs_value> TYPE any.

    ASSIGN COMPONENT iv_field_name OF STRUCTURE is_lfa1 TO <fs_value>.

    IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
      rv_value = <fs_value>.
    ELSE.
      rv_value = ''.
    ENDIF.

  ENDMETHOD.

ENDCLASS.

消息类配置(SE91)

复制代码
消息类:ZMM_VENDOR_ENH
  001:供应商 &1 证书 &2 已过期(有效期 &3),请尽快续办
  002:供应商 &1 证书 &2 30 天内到期(有效期 &3),请及时续办
  003:供应商 &1 已自动解封(黑名单到期)
  004:供应商 &1 在黑名单中(原因:&2),不允许修改
  005:供应商 &1 MDG 同步通知已发送

四、供应商信用等级自动赋值

4.1 信用等级评估算法设计

复制代码
供应商信用评分算法(0-100 分制):

  维度1:履约率(权重 30%)
    → 100% 履约 = 100 分
    → 每低于 1% -3 分,最低 0 分

  维度2:付款及时性(权重 25%)
    → 平均付款天数 ≤ 标准天数 = 100 分
    → 每超出 1 天 -2 分,最低 0 分

  维度3:质量合格率(权重 25%)
    → 质量问题率 0% = 100 分
    → 每 0.1% 问题 -2.5 分,最低 0 分

  维度4:业务规模稳定性(权重 20%)
    → 过去 12 个月有 PO = 100 分
    → 无 PO = 0 分
    → PO 金额趋势稳定 = 80 分
    → PO 金额上升 = 90 分

  综合评分 = 维度1×30% + 维度2×25% + 维度3×25% + 维度4×20%

  信用等级映射:
    A 级:85-100 分  (优质供应商,优先合作)
    B 级:70-84 分   (合格供应商,正常合作)
    C 级:50-69 分   (待改进供应商,加强监控)
    D 级:0-49 分    (风险供应商,限制合作/考虑淘汰)

4.2 信用评估后台 Job 程序

⚠️ 关键修正

  1. EKKO 没有 EBELP 字段(行项目号在 EKPO),也没有 ZTERM 作为凭证日期。凭证日期字段是 BEDAT
  2. 供应商的 BUKRSLFB1 中,不在 LFA1。
  3. SELECT SINGLE ... INTO TABLE 语法错误,单行不能用 INTO TABLE。
  4. MSEG-BWART 退货用 102(冲销)/122(退货),拒收用 123 等,101/105 是收货。
abap 复制代码
"=======================================================================
" 后台 Job 程序:ZMM_VENDOR_CREDIT_SCORE
" 功能:自动计算供应商信用等级 + 回写到 LFA1
" 定时执行:每月 1 号凌晨 3 点
"=======================================================================

REPORT zmm_vendor_credit_score.

SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001.
  PARAMETERS: p_test   AS CHECKBOX,                    " 测试模式
              p_bukrs  TYPE bukrs OBLIGATORY DEFAULT '1000',
              p_lifnr  TYPE lifnr,                     " 单个供应商(空=全部)
              p_months TYPE numc2 DEFAULT '12'.        " 统计月数
SELECTION-SCREEN END OF BLOCK b1.

TYPES: BEGIN OF ty_vendor_result,
         lifnr         TYPE lifnr,
         bukrs         TYPE bukrs,
         name1         TYPE name1_gp,
         score_pct     TYPE dec5_2,          " 履约率得分
         score_pay     TYPE dec5_2,          " 付款及时性得分
         score_quality TYPE dec5_2,          " 质量合格率得分
         score_volume  TYPE dec5_2,          " 业务规模得分
         score_total   TYPE dec5_2,          " 综合得分
         credit_level  TYPE char1,           " 信用等级 A/B/C/D
         credit_date   TYPE datum,           " 评级日期
       END OF ty_vendor_result.

DATA: gt_results TYPE TABLE OF ty_vendor_result,
      gv_count   TYPE i VALUE 0.

START-OF-SELECTION.

  PERFORM get_vendor_list USING p_bukrs p_lifnr CHANGING gt_results.

  IF gt_results IS INITIAL.
    WRITE: / '没有符合条件的供应商'.
    STOP.
  ENDIF.

  " 计算统计起止日期(回溯 p_months 个月)
  DATA: lv_from_date TYPE datum,
        lv_to_date   TYPE datum.

  lv_to_date = sy-datum.
  lv_from_date = lv_to_date - ( p_months * 30 ).

  " 逐供应商计算信用评分
  LOOP AT gt_results INTO DATA(ls_result).

    gv_count = gv_count + 1.

    IF gv_count MOD 10 = 0.
      CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
        EXPORTING
          percentage = ( gv_count * 100 / lines( gt_results ) )
          text       = |计算中 { gv_count } / { lines( gt_results ) }|.
    ENDIF.

    " 计算四个维度得分
    PERFORM calc_delivery_rate USING ls_result-lifnr lv_from_date lv_to_date
      CHANGING ls_result-score_pct.

    PERFORM calc_payment_timeliness USING ls_result-lifnr lv_from_date lv_to_date
      CHANGING ls_result-score_pay.

    PERFORM calc_quality_rate USING ls_result-lifnr lv_from_date lv_to_date
      CHANGING ls_result-score_quality.

    PERFORM calc_volume_stability USING ls_result-lifnr lv_from_date lv_to_date
      CHANGING ls_result-score_volume.

    " 加权计算综合分
    ls_result-score_total = ls_result-score_pct     * '0.30'
                          + ls_result-score_pay     * '0.25'
                          + ls_result-score_quality * '0.25'
                          + ls_result-score_volume  * '0.20'.

    " 映射信用等级
    CASE.
      WHEN ls_result-score_total >= 85. ls_result-credit_level = 'A'.
      WHEN ls_result-score_total >= 70. ls_result-credit_level = 'B'.
      WHEN ls_result-score_total >= 50. ls_result-credit_level = 'C'.
      WHEN OTHERS.                      ls_result-credit_level = 'D'.
    ENDCASE.

    ls_result-credit_date = sy-datum.
    MODIFY gt_results FROM ls_result.

  ENDLOOP.

  " 回写到 LFA1(测试模式不回写)
  IF p_test <> 'X'.

    LOOP AT gt_results INTO DATA(ls_update).
      UPDATE lfa1
         SET zcredit_level  = @ls_update-credit_level,
             zcredit_score  = @ls_update-score_total,
             zcredit_update = @ls_update-credit_date
       WHERE lifnr = @ls_update-lifnr.
    ENDLOOP.

    COMMIT WORK.

  ENDIF.

  PERFORM output_summary USING gt_results p_test.


"=======================================================================
" 子程序1:获取供应商列表(修正:BUKRS 在 LFB1 中,不在 LFA1)
"=======================================================================
FORM get_vendor_list
  USING    iv_bukrs TYPE bukrs
           iv_lifnr TYPE lifnr
  CHANGING ct_data  TYPE TABLE OF ty_vendor_result.

  IF iv_lifnr IS NOT INITIAL.
    " 单供应商查询:LFA1 JOIN LFB1 获取公司代码
    SELECT lfa1~lifnr, lfb1~bukrs, lfa1~name1
      INTO TABLE @ct_data
      FROM lfa1
      INNER JOIN lfb1 ON lfb1~lifnr = lfa1~lifnr
      WHERE lfa1~lifnr = @iv_lifnr
        AND lfb1~bukrs = @iv_bukrs.
  ELSE.
    SELECT lfa1~lifnr, lfb1~bukrs, lfa1~name1
      INTO TABLE @ct_data
      FROM lfa1
      INNER JOIN lfb1 ON lfb1~lifnr = lfa1~lifnr
      WHERE lfb1~bukrs = @iv_bukrs.
  ENDIF.

ENDFORM.


"=======================================================================
" 子程序2:计算履约率(30% 权重)
" 修正:EKKO-BEDAT 才是凭证日期;EBELP 是数字,不能等于 'NEU'
"=======================================================================
FORM calc_delivery_rate
  USING    iv_lifnr TYPE lifnr
           iv_from  TYPE datum
           iv_to    TYPE datum
  CHANGING cv_score  TYPE dec5_2.

  DATA: lv_po_total TYPE i,
        lv_po_deliv TYPE i,
        lv_pct_rate TYPE dec5_2.

  " PO 总数(标准 PO 有 EKPO 行项目)
  SELECT COUNT( DISTINCT ekko~ebeln ) INTO @lv_po_total
    FROM ekko
    INNER JOIN ekpo ON ekpo~ebeln = ekko~ebeln
    WHERE ekko~lifnr = @iv_lifnr
      AND ekko~bedat BETWEEN @iv_from AND @iv_to.

  IF lv_po_total = 0.
    cv_score = 0.
    RETURN.
  ENDIF.

  " 有入库记录的 PO 数
  SELECT COUNT( DISTINCT ekko~ebeln ) INTO @lv_po_deliv
    FROM ekko
    INNER JOIN ekpo ON ekpo~ebeln = ekko~ebeln
    INNER JOIN mseg ON mseg~ebeln = ekpo~ebeln
                   AND mseg~ebelp = ekpo~ebelp
    WHERE ekko~lifnr = @iv_lifnr
      AND ekko~bedat BETWEEN @iv_from AND @iv_to
      AND mseg~bwart IN ('101', '105').

  lv_pct_rate = lv_po_deliv * 100 / lv_po_total.

  " 每低于 1% -3 分
  cv_score = lv_pct_rate * 3.

  IF cv_score > 100.
    cv_score = 100.
  ELSEIF cv_score < 0.
    cv_score = 0.
  ENDIF.

ENDFORM.


"=======================================================================
" 子程序3:付款及时性(25% 权重)
"=======================================================================
FORM calc_payment_timeliness
  USING    iv_lifnr TYPE lifnr
           iv_from  TYPE datum
           iv_to    TYPE datum
  CHANGING cv_score  TYPE dec5_2.

  DATA: lv_avg_days   TYPE dec5_2,
        lv_total_days TYPE i,
        lv_cnt        TYPE i,
        lv_standard   TYPE i VALUE 30.

  " 读取付款凭证(清账日期 - 发票日期 = 实际付款天数)
  SELECT bkpf~bldat AS invoice_date,
         bseg~augdt AS clear_date
    INTO TABLE @DATA(lt_pay)
    FROM bkpf
    INNER JOIN bseg ON bseg~belnr = bkpf~belnr AND bseg~bukrs = bkpf~bukrs
    WHERE bseg~lifnr = @iv_lifnr
      AND bkpf~bldat BETWEEN @iv_from AND @iv_to
      AND bseg~augdt IS NOT NULL.

  " 计算平均付款天数
  LOOP AT lt_pay INTO DATA(ls_pay).
    DATA(lv_diff) = ls_pay-clear_date - ls_pay-invoice_date.
    IF lv_diff >= 0.
      lv_total_days = lv_total_days + lv_diff.
      lv_cnt        = lv_cnt + 1.
    ENDIF.
  ENDLOOP.

  IF lv_cnt = 0.
    cv_score = 50.  " 无付款记录给中等分
    RETURN.
  ENDIF.

  lv_avg_days = lv_total_days / lv_cnt.

  IF lv_avg_days <= lv_standard.
    cv_score = 100.
  ELSE.
    cv_score = 100 - ( lv_avg_days - lv_standard ) * 2.
  ENDIF.

  IF cv_score < 0.
    cv_score = 0.
  ELSEIF cv_score > 100.
    cv_score = 100.
  ENDIF.

ENDFORM.


"=======================================================================
" 子程序4:质量合格率(25% 权重)
"=======================================================================
FORM calc_quality_rate
  USING    iv_lifnr TYPE lifnr
           iv_from  TYPE datum
           iv_to    TYPE datum
  CHANGING cv_score  TYPE dec5_2.

  DATA: lv_total_qty  TYPE menge_d,
        lv_return_qty TYPE menge_d.

  " 总入库数量
  SELECT SUM( mseg~menge ) INTO @lv_total_qty
    FROM mseg
    INNER JOIN ekko ON ekko~ebeln = mseg~ebeln
    WHERE ekko~lifnr = @iv_lifnr
      AND ekko~bedat BETWEEN @iv_from AND @iv_to
      AND mseg~bwart IN ('101', '105').

  " 退货/拒收数量(102=冲销入库;122=退货;123=拒收)
  SELECT SUM( mseg~menge ) INTO @lv_return_qty
    FROM mseg
    INNER JOIN ekko ON ekko~ebeln = mseg~ebeln
    WHERE ekko~lifnr = @iv_lifnr
      AND ekko~bedat BETWEEN @iv_from AND @iv_to
      AND mseg~bwart IN ('102', '122', '123').

  IF lv_total_qty = 0.
    cv_score = 50.
    RETURN.
  ENDIF.

  DATA(lv_rej_rate) = lv_return_qty * 100 / lv_total_qty.

  " 每 0.1% 问题 -2.5 分
  cv_score = 100 - ( lv_rej_rate * 25 ).

  IF cv_score < 0.
    cv_score = 0.
  ELSEIF cv_score > 100.
    cv_score = 100.
  ENDIF.

ENDFORM.


"=======================================================================
" 子程序5:业务规模稳定性(20% 权重)
"=======================================================================
FORM calc_volume_stability
  USING    iv_lifnr TYPE lifnr
           iv_from  TYPE datum
           iv_to    TYPE datum
  CHANGING cv_score  TYPE dec5_2.

  DATA: lv_po_count TYPE i,
        lv_prev_po  TYPE i.

  " 过去 N 个月的 PO 数量
  SELECT COUNT( DISTINCT ekko~ebeln ) INTO @lv_po_count
    FROM ekko
    INNER JOIN ekpo ON ekpo~ebeln = ekko~ebeln
    WHERE ekko~lifnr = @iv_lifnr
      AND ekko~bedat BETWEEN @iv_from AND @iv_to.

  " 之前 N 个月的 PO 数量(对比趋势)
  DATA: lv_prev_from TYPE datum,
        lv_prev_to   TYPE datum.
  lv_prev_from = iv_from - ( p_months * 2 * 30 ).
  lv_prev_to   = iv_from - 1.

  SELECT COUNT( DISTINCT ekko~ebeln ) INTO @lv_prev_po
    FROM ekko
    INNER JOIN ekpo ON ekpo~ebeln = ekko~ebeln
    WHERE ekko~lifnr = @iv_lifnr
      AND ekko~bedat BETWEEN @lv_prev_from AND @lv_prev_to.

  IF lv_po_count + lv_prev_po = 0.
    cv_score = 0.
    RETURN.
  ENDIF.

  IF lv_prev_po = 0.
    cv_score = 100.    " 新供应商,过去无 PO,现在有 → 满分
  ELSEIF lv_po_count >= lv_prev_po.
    cv_score = 90.     " 数量稳定或上升 → 90 分
  ELSE.
    DATA(lv_pct_change) = ( lv_prev_po - lv_po_count ) * 100 / lv_prev_po.
    cv_score = 80 - lv_pct_change / 5.
  ENDIF.

  IF cv_score < 0.
    cv_score = 0.
  ELSEIF cv_score > 100.
    cv_score = 100.
  ENDIF.

ENDFORM.


"=======================================================================
" 子程序6:输出汇总 + ALV
"=======================================================================
FORM output_summary
  USING it_results TYPE TABLE OF ty_vendor_result
        iv_test    TYPE flag.

  DATA: lv_cnt_a TYPE i, lv_cnt_b TYPE i, lv_cnt_c TYPE i, lv_cnt_d TYPE i.

  LOOP AT it_results INTO DATA(ls_r).
    CASE ls_r-credit_level.
      WHEN 'A'. lv_cnt_a = lv_cnt_a + 1.
      WHEN 'B'. lv_cnt_b = lv_cnt_b + 1.
      WHEN 'C'. lv_cnt_c = lv_cnt_c + 1.
      WHEN 'D'. lv_cnt_d = lv_cnt_d + 1.
    ENDCASE.
  ENDLOOP.

  WRITE: / '================================================'.
  WRITE: /  |供应商信用评估完成|.
  WRITE: / '================================================'.
  WRITE: /  |评估供应商总数:{ lines( it_results ) }|.
  WRITE: /  |A 级(优秀):  { lv_cnt_a }|.
  WRITE: /  |B 级(合格):  { lv_cnt_b }|.
  WRITE: /  |C 级(待改进):{ lv_cnt_c }|.
  WRITE: /  |D 级(风险):  { lv_cnt_d }|.
  WRITE: / '------------------------------------------------'.
  IF iv_test = 'X'.
    WRITE: /  |【测试模式】未回写信用等级|.
  ELSE.
    WRITE: /  |信用等级已回写到 LFA1 表|.
  ENDIF.
  WRITE: / '================================================'.

  " ALV 详细结果
  TRY.
      cl_salv_table=>factory(
        IMPORTING r_salv_table = DATA(lo_alv)
        CHANGING  t_table      = it_results ).

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

    CATCH cx_salv_msg.
  ENDTRY.

ENDFORM.

五、供应商 MDG 同步接口开发

5.1 同步方案选型

方式 适用场景 优点 缺点 推荐度
ALE / IDoc 异步单向推送 解耦、失败可重发 配置复杂、有延迟 ★★★★★
RFC 实时请求/响应 同步、双向交互 耦合高、需要 VPN ★★★★☆
OData 跨平台/移动端 RESTful、无状态 需要额外授权 ★★★☆☆
SOAP/WSDL MDG 标准集成 SAP 官方推荐 性能一般 ★★★★☆

推荐:ALE/IDoc(主同步)+ RFC(查询接口)组合

5.2 IDoc 出站同步实现

abap 复制代码
"=======================================================================
" BAdI 实现:ZIMPL_VENDOR_IDOC_OUT
" BAdI:BADI_PARTNER_UPDATE(供应商更新后触发)
" 功能:供应商更新后 → IDoc 推送到 MDG
" 方式:ALE / IDoc(消息类型:CREMAS / ZVENDOR)
"=======================================================================

CLASS cl_impl_vendor_idoc_out IMPLEMENTATION.

  METHOD if_ex_partner_update~after_update.

    DATA: lv_lifnr TYPE lifnr.
    lv_lifnr = iv_businesspartner.

    " 步骤1:读取完整的供应商数据
    DATA: ls_lfa1 TYPE lfa1.
    DATA: lt_lfb1 TYPE TABLE OF lfb1.
    DATA: lt_lfbk TYPE TABLE OF lfbk.

    SELECT SINGLE * FROM lfa1 INTO @ls_lfa1 WHERE lifnr = @lv_lifnr.
    SELECT * FROM lfb1 INTO TABLE @lt_lfb1 WHERE lifnr = @lv_lifnr.
    SELECT * FROM lfbk INTO TABLE @lt_lfbk WHERE lifnr = @lv_lifnr.

    IF ls_lfa1 IS INITIAL.
      RETURN.
    ENDIF.

    " 步骤2:构造 IDoc 数据记录(自定义结构 ZVENDOR_MASTER)
    DATA: lt_idoc_data TYPE TABLE OF zvendor_master,
          ls_idoc_data TYPE zvendor_master.

    ls_idoc_data-mandt        = ls_lfa1-mandt.
    ls_idoc_data-lifnr        = ls_lfa1-lifnr.
    ls_idoc_data-name1        = ls_lfa1-name1.
    ls_idoc_data-land1        = ls_lfa1-land1.
    ls_idoc_data-ort01        = ls_lfa1-ort01.
    ls_idoc_data-pstlz        = ls_lfa1-pstlz.
    ls_idoc_data-stras        = ls_lfa1-stras.
    ls_idoc_data-telf1        = ls_lfa1-telf1.
    ls_idoc_data-stcd1        = ls_lfa1-stcd1.          " 税号
    ls_idoc_data-stcd2        = ls_lfa1-stcd2.
    ls_idoc_data-zcredit_level = ls_lfa1-zcredit_level.
    ls_idoc_data-zblacklist   = ls_lfa1-zblacklist_flag.
    ls_idoc_data-ziso_exp     = ls_lfa1-ziso9001_exp.
    ls_idoc_data-laeda        = ls_lfa1-laeda.
    ls_idoc_data-laeur        = ls_lfa1-laeur.

    " 公司代码级数据
    LOOP AT lt_lfb1 INTO DATA(ls_b1).
      ls_idoc_data-bukrs = ls_b1-bukrs.
      ls_idoc_data-akont = ls_b1-akont.
      ls_idoc_data-zterm = ls_b1-zterm.
      APPEND ls_idoc_data TO lt_idoc_data.
    ENDLOOP.

    " 步骤3:调用标准 ALE IDoc 生成函数
    DATA: lv_edidc TYPE edidc.
    DATA: lt_edid  TYPE TABLE OF edid.
    DATA: lt_edidd TYPE TABLE OF edidd.
    DATA: ls_edidd TYPE edidd.

    " EDIDC(控制记录)
    lv_edidc-mandt   = sy-mandt.
    lv_edidc-rectyp  = '2'.
    lv_edidc-statem  = '0'.
    lv_edidc-doctyp  = 'ZVENDOR'.        " 自定义消息类型
    lv_edidc-cimtyp  = 'MANDT'.
    lv_edidc-rcvp    = 'MDGCLNT800'.
    lv_edidc-rcvprt  = 'RECEIVE'.
    lv_edidc-rcvmprt = 'RECEIVE'.
    lv_edidc-sndp    = 'SAPCLNT800'.
    lv_edidc-sndprt  = 'SEND'.
    lv_edidc-sndmprt = 'SEND'.
    lv_edidc-direct  = '1'.              " 立即发送

    " EDIDD(数据记录)------注意:sdata 是引用类型,需通过指针传递
    ls_edidd-segnam = 'ZSEG_VENDOR'.
    ls_edidd-sdata  = CONV zseg_vendor( lt_idoc_data ).
    APPEND ls_edidd TO lt_edidd.

    " 调用 MASTER_IDOC_DISTRIBUTE
    CALL FUNCTION 'MASTER_IDOC_DISTRIBUTE'
      EXPORTING
        master_idoc_control = lv_edidc
      TABLES
        communication_idoc_control = lt_edid
        master_idoc_data           = lt_edidd.

    IF sy-subrc = 0.
      " 成功 → 写应用日志
      DATA(lv_log_handle) = VALUE balloghndl( ).
      CALL FUNCTION 'BAL_LOG_CREATE'
        EXPORTING
          i_s_log     = VALUE #( object    = 'ZMDG_SYNC'
                                 subobject = 'VENDOR'
                                 extnumber = CONV balnrext( lv_lifnr ) )
        IMPORTING
          e_log_handle = lv_log_handle.

      CALL FUNCTION 'BAL_LOG_MSG_ADD'
        EXPORTING
          i_log_handle = lv_log_handle
          i_s_msg      = VALUE #( msgty = 'S'
                                  msgid = 'ZMM_VENDOR_ENH'
                                  msgno = '005'
                                  msgv1 = lv_lifnr ).

      CALL FUNCTION 'BAL_LOG_SAVE'
        EXPORTING
          i_log_handle = lv_log_handle.

      COMMIT WORK.

    ELSE.
      " 失败 → 记录日志,后续重试
      PERFORM log_idoc_error USING lv_lifnr lt_edid.
    ENDIF.

  ENDMETHOD.

ENDCLASS.

5.3 RFC 查询接口

abap 复制代码
"=======================================================================
" RFC 函数:Z_VENDOR_GET_CREDIT
" 标记:Remote-Enabled(SE37 属性)
" 功能:MDG 查询 SAP 供应商信用等级
"=======================================================================

FUNCTION z_vendor_get_credit.
  IMPORTING
    iv_lifnr        TYPE lifnr
  EXPORTING
    ev_credit_level TYPE char1
    ev_credit_score TYPE dec5_2
    ev_msgtype      TYPE char1
    ev_message      TYPE char255
  EXCEPTIONS
    not_found.

  SELECT SINGLE lifnr, zcredit_level, zcredit_score, zcredit_update
    INTO @DATA(ls_vendor)
    FROM lfa1
    WHERE lifnr = @iv_lifnr.

  IF sy-subrc <> 0.
    ev_msgtype = 'E'.
    ev_message = '供应商不存在'.
    RAISE not_found.
  ENDIF.

  ev_credit_level = ls_vendor-zcredit_level.
  ev_credit_score = ls_vendor-zcredit_score.
  ev_msgtype      = 'S'.
  ev_message      = |供应商 { iv_lifnr } 信用等级 { ls_vendor-zcredit_level }|.

ENDFUNCTION.

六、常见问题与排查

  • Q1:BADI_BUSINESSPLANT 和 EXIT_SAPMF02D_001 选哪个?

    A:功能定位不同。BADI_BUSINESSPLANT面向对象 BAdI (支持多实现、过滤器),在供应商主数据保存的标准逻辑之后触发;EXIT_SAPMF02D_001老式过程式出口(只有一个 INCLUDE、不支持多实现),在保存前触发。新开发用 BAdI,老项目兼容可以同时关注两者。

  • Q2:供应商资质效期过期了,要不要 E 消息阻止保存?

    A:大多数业务场景不建议硬性阻止 ------影响太大(可能导致紧急采购无法进行)。推荐分级处理:①效期已过 → 警告 W 消息 + 自动拉入临时黑名单;②30 天内到期 → 提示 W 消息;③永久黑名单 → E 消息阻止修改。临时黑名单可以设置解封日期(ZUNBLOCK_DATE),过期自动解封。

  • Q3:信用评估算法的权重怎么选?

    A:权重反映业务侧重------采购量大的企业应该履约率权重最高(50%+) ,质量敏感的企业(如医药)质量合格率权重最高 。本篇的 30/25/25/20 是通用权重,建议做成配置表可调整 。另外建议月度评估,季度/年度重新校准权重。

  • Q4:供应商 IDoc 推送到 MDG,失败了怎么重试?

    A:ALE/IDoc 有完整的状态管理:①BD87 查看 IDoc 状态(3=成功/2=错误);②BD88 重发失败 IDoc;③如果是自定义 BAdI 触发的推送,建议写一个批量重试 Job (Z_VENDOR_IDOC_RETRY),每天扫描失败记录后重发。重要的是幂等设计------MDG 接收端必须保证重复推送不会产生重复数据。

  • Q5:供应商黑名单的 CI_LFA1 扩展字段和 ZT_VENDOR_BLACKLIST 配置表有什么区别?

    A:CI_LFA1供应商主数据的一部分 ------LFA1 直接存黑名单标记,查起来方便,随供应商一起同步到 MDG;ZT_VENDOR_BLACKLIST独立的管控表 ------可以加更多维度(黑名单类型、生效范围、创建人等),还可以做审批流程。推荐两者结合:CI_LFA1 存当前状态,ZT_VENDOR_BLACKLIST 存历史记录和审批信息。

  • Q6:信用等级自动赋值后,怎么在 PO/MIRO 中自动检查?

    A:在我们之前开发的 ME_PROCESS_PO_CUST(PO)和 EXIT_SAPLMRMH_014(MIRO)增强中,增加对 LFA1-ZCREDIT_LEVEL 的检查:①PO/MIRO 保存时,根据供应商的信用等级做不同处理;②D 级供应商 → E 消息阻止;③C 级 → W 消息警告 + 强制选择冻结标记;④A/B 级 → 正常。这体现了增强代码的协同复用


七、总结

供应商主数据增强三大场景速查

场景 实现方式 触发点 核心参数
资质效期校验 BADI_BUSINESSPLANT 保存前 IS_BUSINESSPARTNER + CI_LFA1
信用等级自动赋值 定时 Job 每月 PO/MIRO/MSEG 统计 + 算法评分
MDG 同步 BADI_PARTNER_UPDATE + IDoc 保存后 ZVENDOR 自定义结构

关键代码避坑速查

错误做法 正确做法
供应商公司代码 lfa1~bukrs lfb1~bukrs(在 LFB1 表中)
凭证日期字段 ekko~zterm ekko~bedat(ZTERM 是付款条件)
行项目号比较 ekko~ebelp = 'NEU' EBELP 是数字,不是字符;应改用其他条件
单行查询 SELECT SINGLE ... INTO TABLE SELECT SINGLE ... INTO (单变量)
追加字段访问 is_lfa1-ziso9001 ASSIGN COMPONENT 'ZISO9001' OF STRUCTURE is_lfa1
更新函数提交 COMMIT WORK 不写,让框架统一提交
退货移动类型 只用 102 102/122/123 都要考虑

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

#mermaid-svg-dlmi0b0U3dTgR0Ht{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-dlmi0b0U3dTgR0Ht .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dlmi0b0U3dTgR0Ht .error-icon{fill:#552222;}#mermaid-svg-dlmi0b0U3dTgR0Ht .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dlmi0b0U3dTgR0Ht .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dlmi0b0U3dTgR0Ht .marker.cross{stroke:#333333;}#mermaid-svg-dlmi0b0U3dTgR0Ht svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dlmi0b0U3dTgR0Ht p{margin:0;}#mermaid-svg-dlmi0b0U3dTgR0Ht .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dlmi0b0U3dTgR0Ht .cluster-label text{fill:#333;}#mermaid-svg-dlmi0b0U3dTgR0Ht .cluster-label span{color:#333;}#mermaid-svg-dlmi0b0U3dTgR0Ht .cluster-label span p{background-color:transparent;}#mermaid-svg-dlmi0b0U3dTgR0Ht .label text,#mermaid-svg-dlmi0b0U3dTgR0Ht span{fill:#333;color:#333;}#mermaid-svg-dlmi0b0U3dTgR0Ht .node rect,#mermaid-svg-dlmi0b0U3dTgR0Ht .node circle,#mermaid-svg-dlmi0b0U3dTgR0Ht .node ellipse,#mermaid-svg-dlmi0b0U3dTgR0Ht .node polygon,#mermaid-svg-dlmi0b0U3dTgR0Ht .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dlmi0b0U3dTgR0Ht .rough-node .label text,#mermaid-svg-dlmi0b0U3dTgR0Ht .node .label text,#mermaid-svg-dlmi0b0U3dTgR0Ht .image-shape .label,#mermaid-svg-dlmi0b0U3dTgR0Ht .icon-shape .label{text-anchor:middle;}#mermaid-svg-dlmi0b0U3dTgR0Ht .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dlmi0b0U3dTgR0Ht .rough-node .label,#mermaid-svg-dlmi0b0U3dTgR0Ht .node .label,#mermaid-svg-dlmi0b0U3dTgR0Ht .image-shape .label,#mermaid-svg-dlmi0b0U3dTgR0Ht .icon-shape .label{text-align:center;}#mermaid-svg-dlmi0b0U3dTgR0Ht .node.clickable{cursor:pointer;}#mermaid-svg-dlmi0b0U3dTgR0Ht .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dlmi0b0U3dTgR0Ht .arrowheadPath{fill:#333333;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dlmi0b0U3dTgR0Ht .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dlmi0b0U3dTgR0Ht .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dlmi0b0U3dTgR0Ht .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dlmi0b0U3dTgR0Ht .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dlmi0b0U3dTgR0Ht .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dlmi0b0U3dTgR0Ht .cluster text{fill:#333;}#mermaid-svg-dlmi0b0U3dTgR0Ht .cluster span{color:#333;}#mermaid-svg-dlmi0b0U3dTgR0Ht 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-dlmi0b0U3dTgR0Ht .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dlmi0b0U3dTgR0Ht rect.text{fill:none;stroke-width:0;}#mermaid-svg-dlmi0b0U3dTgR0Ht .icon-shape,#mermaid-svg-dlmi0b0U3dTgR0Ht .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dlmi0b0U3dTgR0Ht .icon-shape p,#mermaid-svg-dlmi0b0U3dTgR0Ht .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dlmi0b0U3dTgR0Ht .icon-shape .label rect,#mermaid-svg-dlmi0b0U3dTgR0Ht .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dlmi0b0U3dTgR0Ht .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dlmi0b0U3dTgR0Ht .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dlmi0b0U3dTgR0Ht :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MM 模块增强开发体系
已完成 P2P 全链路(6 篇)
待完成(2 篇)
采购订单增强

ME_PROCESS_PO_CUST
物料主数据增强

BADI_MATERIAL_CHECK
采购申请增强

ME_PROCESS_REQ_CUST
库存管理增强

MIGO_GOODS_MOVEMENT_POSTING
发票校验增强

EXIT_SAPLMRMH_014
供应商主数据增强

BADI_BUSINESSPLANT(本篇)
第七篇:MM 报表开发
第八篇:MM 接口开发

P2P 全链路增强闭环:供应商主数据 → PR(采购申请)→ PO(采购订单)→ GR(入库)→ IR(发票校验),形成完整的采购到付款增强体系。

本篇与下一篇的衔接

本篇(MM 供应商主数据) 下一篇(MM 报表开发)
BADI_BUSINESSPLANT ALV 报表 SALV + CL_SALV_TABLE
CI_LFA1 追加结构 MKPF/MSEG/EKKO/EKPO 报表数据源
信用等级算法 + IDoc 同步 采购执行 + 库龄 + 供应商绩效分析
主数据治理 业务数据洞察

下一篇预告:《MM 模块报表开发:采购执行、库存分析与供应商绩效报表实战》------MM 模块的最后一篇专题:报表开发。下一篇讲解采购执行跟踪报表、库存账龄分析报表、供应商交付绩效分析报表、采购价格趋势分析报表的开发逻辑,结合 ALV 展示、数据钻取、批量导出等功能实现,附完整的报表开发代码模板。

作者 :爱喝水的鱼丶

版本记录:2026 年 9 月

💬 你在供应商主数据增强上做过哪些事情?信用等级自动评估你们是用算法算的还是人工评的?供应商资质效期过期你们是警告还是直接屏蔽?欢迎分享你的供应商管理经验。

相关推荐
贾伟康1 小时前
【HarmonyOS 7新能力|050】Core File Kit mmap工程封装:把接入逻辑放进可维护的分层结构
性能优化·harmonyos·arkts·文件系统·mmap
小小数媒成员1 小时前
UGUI 性能优化(详细,全,深入)
开发语言·unity·游戏引擎
大黄说说1 小时前
告别卡顿:Android UI 性能优化,布局层级过度绘制实战排查
开发语言
千谦阙听1 小时前
【 C++篇】:模板初阶——泛型编程、函数模板与类模板
开发语言·c++·学习·visual studio
SEO_juper1 小时前
2026年用Python检测网站薄内容与重复页:用向量相似度找出“搜索引擎眼中一样“的页面(附完整代码)
开发语言·前端·seo·独立站·谷歌优化
老王爱玩车1 小时前
第6讲:数组和函数实践-----控制台扫雷
c语言·开发语言·学习
贾伟康1 小时前
【HarmonyOS 7新能力|045】LazyLayoutAlgorithm工程封装:把接入逻辑放进可维护的分层结构
性能优化·harmonyos·arkts·arkui·懒加载
m0_734571761 小时前
深入理解C++ 多态<三>(Polymorphism)模板方法模式
开发语言·c++
君顾11 小时前
24小时自助健身房系统开发实战:从0到1完整技术架构指南
java·开发语言·健身房