惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Stack Overflow Blog
Stack Overflow Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
大猫的无限游戏
大猫的无限游戏
爱范儿
爱范儿
WordPress大学
WordPress大学
B
Blog RSS Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
人人都是产品经理
人人都是产品经理
J
Java Code Geeks
酷 壳 – CoolShell
酷 壳 – CoolShell
小众软件
小众软件
MyScale Blog
MyScale Blog
GbyAI
GbyAI
Martin Fowler
Martin Fowler
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 聂微东
The Cloudflare Blog
L
Lohrmann on Cybersecurity
Apple Machine Learning Research
Apple Machine Learning Research
I
InfoQ
Google DeepMind News
Google DeepMind News
S
Securelist
Application and Cybersecurity Blog
Application and Cybersecurity Blog
博客园 - 【当耐特】
Latest news
Latest news
T
Threatpost
量子位
Y
Y Combinator Blog
T
Troy Hunt's Blog
Know Your Adversary
Know Your Adversary
MongoDB | Blog
MongoDB | Blog
罗磊的独立博客
博客园_首页
AWS News Blog
AWS News Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
宝玉的分享
宝玉的分享
Project Zero
Project Zero
V
Visual Studio Blog
F
Fortinet All Blogs
S
Security Affairs
The Register - Security
The Register - Security
G
Google Developers Blog
T
Tenable Blog
L
LINUX DO - 最新话题
The GitHub Blog
The GitHub Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
博客园 - 三生石上(FineUI控件)
T
The Exploit Database - CXSecurity.com
博客园 - Franky

博客园 - seven1314pp

SAP ABAP 客制表-表格维护器-自动带出描述等 SAP ABAP 选择屏幕按钮在同一行 SAP ABAP SQL CASE 套 CASE SAP ABAP ALV 布局 SAP ABAP开发技巧-ALV报表通过函数获取结构或表字段写入FIELDCAT SAP ABAP财务开发必备ABAP语法之 DO VARYING abap 分割字符串 ABAP 读取其他ALV的显示结果 SAP 会计凭证字段可以更改 SAP ABAP弹出对对话框错误信息设计 SAP ABAP 部分增强点 [ABAP] ABAP 三类内表 PYTHON 简单的网页图片爬虫 财务凭证生成过程中增强技术 SAP BTE增强 ABAP FB02 修改会计凭证的抬头文本/行项目文本的函数 ABAP ALV 单元格按钮 SAP日志表 CDHDR和CDPOS SAP abap外部断点 abap screen页签开发注意事项 abap screen表格控件后续增加栏位
SAP ABAP 一个通用的ALV框架(值得收藏)
seven1314pp · 2026-03-20 · via 博客园 - seven1314pp

这个框架包含了 ALV 开发的核心功能:查询 / 新增 / 修改 / 删除、自定义工具栏、数据校验、保存、打印、F4 帮助、推送外围系统等通用能力,你可以基于此快速扩展业务逻辑。

REPORT zalv_general_framework_enhanced.
INCLUDE <cl_alv_control>.  "ALV控制相关的标准INCLUDE
*&---------------------------------------------------------------------*
*& 【通用定义区】- 保留ALV核心通用字段,业务字段按需扩展
*&---------------------------------------------------------------------*
"ALV输出内表类型定义(保留通用控制字段,业务字段示例化)
TYPES: BEGIN OF ts_alv_out,
         "=== ALV通用控制字段(核心保留,无需修改)===
         sele        TYPE ad_mark,        "选择框字段(必选)
         status_doc  TYPE rmpsp_dp_status,"状态显示字段(图标展示)
         status_pus  TYPE char10,         "推送状态字段
         mess        TYPE vec_message,    "消息显示字段
         styl        TYPE lvc_t_styl,     "单元格样式字段(控制编辑/颜色/F4)
         updkz       TYPE updkz_d,        "更新标识(I-新增 U-修改 D-删除)
         "=== 业务字段示例(替换为实际业务字段)===
         znumber     TYPE char20,         "业务单号
         matnr       TYPE matnr,          "物料编码
         maktx       TYPE maktx,          "物料描述
         werks       TYPE werks_d,        "工厂
         lgort       TYPE lgort_d,        "库存地点
         erfmg       TYPE erfmg,          "数量
         erfme       TYPE meins,          "单位
         charg       TYPE charg_d,        "批次
         insmk       TYPE char1,          "库存类型(限制/冻结)
       END OF ts_alv_out.
"打印模板相关类型(保留通用打印结构)
TYPES: BEGIN OF ty_print_head,
        butxt       TYPE t001-butxt,     "公司名称
        title       TYPE char30,         "打印标题
        znumber     TYPE char20,         "业务单号
        lgobe       TYPE t001l-lgobe,    "仓库描述
        zcrdate     TYPE sy-datum,       "创建日期
        department  TYPE char30,         "部门
        zzdr        TYPE char20,         "制单人
        zsum_lfimg  TYPE erfmg,          "合计数量
       END OF ty_print_head.
 
TYPES: BEGIN OF ty_print_item,
        znumber     TYPE char20,         "业务单号
        matnr       TYPE matnr,          "物料编码
        maktx       TYPE maktx,          "物料描述
        erfmg       TYPE erfmg,          "数量
        msehl       TYPE t006a-msehl,    "单位描述
        lgort       TYPE lgort_d,        "库存地点
       END OF ty_print_item.
 
*&---------------------------------------------------------------------*
*& 【全局变量区】- 保留ALV+打印核心变量
*&---------------------------------------------------------------------*
DATA: go_alv            TYPE REF TO cl_gui_alv_grid,  "ALV网格对象
      ok_code           TYPE sy-ucomm,               "屏幕操作码
      gt_alv_out        TYPE TABLE OF ts_alv_out,    "ALV输出内表
      gt_fcat           TYPE lvc_t_fcat,             "ALV字段目录
      gs_layout         TYPE lvc_s_layo,             "ALV布局
      "打印相关全局变量(保留通用结构)
      gs_print_head     TYPE ty_print_head,
      gt_print_item     TYPE TABLE OF ty_print_item,
      gs_print_item     TYPE ty_print_item.
 
*&---------------------------------------------------------------------*
*& 【选择屏幕区】- 保留通用操作模式+查询条件模板
*&---------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK blk1 WITH FRAME TITLE TEXT-001.
  PARAMETERS: p_werks TYPE werks_d OBLIGATORY,        "工厂(必选)
              p_biztype TYPE char4 AS LISTBOX VISIBLE LENGTH 20. "业务类型下拉框
  SELECT-OPTIONS: s_number FOR gt_alv_out-znumber,    "业务单号
                  s_matnr  FOR gt_alv_out-matnr,     "物料编码
                  s_erdat  FOR sy-datum,             "创建日期
                  s_lgort  FOR gt_alv_out-lgort.     "库存地点
SELECTION-SCREEN END   OF BLOCK blk1.
 
SELECTION-SCREEN BEGIN OF BLOCK blk2 WITH FRAME TITLE TEXT-002.
  PARAMETERS: p_ins TYPE boolean RADIOBUTTON GROUP rg1, "新增模式
              p_mod TYPE boolean RADIOBUTTON GROUP rg1, "修改模式
              p_del TYPE boolean RADIOBUTTON GROUP rg1, "删除模式
              p_que TYPE boolean RADIOBUTTON GROUP rg1 DEFAULT 'X'. "查询模式
SELECTION-SCREEN END   OF BLOCK blk2.
 
*&---------------------------------------------------------------------*
*& 【事件处理类】- 保留ALV核心事件+完整的工具栏/打印事件
*&---------------------------------------------------------------------*
CLASS lcl_alv_event_handler DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      "1. 工具栏自定义(保留新增/删除/保存/打印按钮逻辑)
      handle_toolbar     FOR EVENT toolbar OF cl_gui_alv_grid
        IMPORTING e_object,
      "2. 数据修改完成(保留单元格编辑后联动逻辑模板)
      handle_data_changed_finished FOR EVENT data_changed_finished OF cl_gui_alv_grid
        IMPORTING e_modified et_good_cells,
      "3. 自定义按钮点击(保留新增/删除/保存/打印完整分支)
      handle_user_command FOR EVENT user_command OF cl_gui_alv_grid
        IMPORTING e_ucomm,
      "4. F4帮助事件(保留批次/F4自定义模板)
      handle_onf4 FOR EVENT onf4 OF cl_gui_alv_grid
        IMPORTING e_fieldname e_fieldvalue es_row_no er_event_data,
      "5. 热点点击事件(保留行热点操作模板)
      handle_hotspot_click FOR EVENT hotspot_click OF cl_gui_alv_grid
        IMPORTING e_row_id.
ENDCLASS.
CLASS lcl_alv_event_handler IMPLEMENTATION.
  "1. 工具栏自定义 - 保留按操作模式显示按钮的通用逻辑
  METHOD handle_toolbar.
    "删除默认本地按钮
    DELETE e_object->mt_toolbar WHERE function CS 'LOCAL'.
    
    "按操作模式添加自定义按钮
    CASE abap_true.
      WHEN p_ins.  "新增模式:新增+删除+保存
        e_object->mt_toolbar = VALUE #( BASE e_object->mt_toolbar
                                        ( butn_type = 3 )  "分隔符
                                        ( function = 'CRE'  icon = 'ICON_CREATE'  text = '新增' )
                                        ( function = 'DEL'  icon = 'ICON_DELETE'  text = '删除' )
                                        ( function = 'SAVE' icon = 'ICON_SAVE'   text = '保存' ) ).
      WHEN p_mod.  "修改模式:保存
        e_object->mt_toolbar = VALUE #( BASE e_object->mt_toolbar
                                        ( butn_type = 3 )
                                        ( function = 'SAVE' icon = 'ICON_SAVE'   text = '保存' ) ).
      WHEN p_del.  "删除模式:删除+保存
        e_object->mt_toolbar = VALUE #( BASE e_object->mt_toolbar
                                        ( butn_type = 3 )
                                        ( function = 'DEL'  icon = 'ICON_DELETE'  text = '删除' )
                                        ( function = 'SAVE' icon = 'ICON_SAVE'   text = '保存' ) ).
      WHEN p_que.  "查询模式:打印
        e_object->mt_toolbar = VALUE #( BASE e_object->mt_toolbar
                                        ( butn_type = 3 )
                                        ( function = 'PRINT' icon = 'ICON_PRINT' text = '打印' ) ).
    ENDCASE.
  ENDMETHOD.
  "2. 数据修改完成 - 保留通用联动逻辑模板
  METHOD handle_data_changed_finished.
    CHECK e_modified EQ abap_true.  "仅处理有修改的数据
    
    "读取第一个修改的单元格
    READ TABLE et_good_cells ASSIGNING FIELD-SYMBOL(<ls_cell>) INDEX 1.
    CHECK sy-subrc EQ 0.
    
    "读取对应行数据
    READ TABLE gt_alv_out ASSIGNING FIELD-SYMBOL(<ls_alv>) INDEX <ls_cell>-row_id.
    CHECK sy-subrc EQ 0.
    "=== 通用联动逻辑模板(替换为实际业务)===
    CASE <ls_cell>-fieldname.
      WHEN 'MATNR'.  "物料编码修改后自动填充描述+单位
        "示例:物料描述联动
        SELECT SINGLE maktx FROM makt INTO <ls_alv>-maktx
          WHERE matnr = <ls_alv>-matnr AND spras = sy-langu.
        "示例:物料单位联动
        SELECT SINGLE meins FROM mara INTO <ls_alv>-erfme WHERE matnr = <ls_alv>-matnr.
      
      WHEN 'CHARG'.  "批次修改后联动(示例)
        "SELECT SINGLE ... FROM 批次表 INTO <ls_alv>-... WHERE matnr = <ls_alv>-matnr AND charg = <ls_alv>-charg.
      
      WHEN 'WERKS' OR 'LGORT' OR 'INSMK'.  "工厂/库存地/库存类型联动库存数量
        "SELECT SINGLE labst/speme FROM mard INTO <ls_alv>-menge WHERE ...
    ENDCASE.
    "标记修改模式的更新标识
    IF p_mod = abap_true.
      <ls_alv>-updkz = 'U'.
    ENDIF.
 
    "刷新ALV(保持行/列稳定)
    go_alv->refresh_table_display( is_stable = VALUE lvc_s_stbl( row = abap_true col = abap_true ) ).
  ENDMETHOD.
  "3. 自定义按钮点击 - 保留完整的按钮处理分支
  METHOD handle_user_command.
    "检查未保存的修改(防止数据丢失)
    go_alv->check_changed_data( ).
    
    "标记选中行
    PERFORM frm_mark_selected_rows USING 'X'.
 
    "按按钮功能处理
    CASE e_ucomm.
      WHEN 'CRE'.  "新增
        PERFORM frm_create_new_row.
      WHEN 'DEL'.  "删除
        PERFORM frm_delete_selected_rows.
      WHEN 'SAVE'. "保存
        PERFORM frm_check_data.  "数据校验
        PERFORM frm_save_data.   "数据保存
      WHEN 'PRINT'. "打印
        PERFORM frm_print_data.  "打印处理
    ENDCASE.
 
    "刷新ALV+重置选中行
    go_alv->refresh_table_display( is_stable = VALUE lvc_s_stbl( row = abap_true col = abap_true ) ).
    PERFORM frm_mark_selected_rows USING ''.
  ENDMETHOD.
  "4. F4帮助事件 - 保留批次F4模板
  METHOD handle_onf4.
    CASE e_fieldname.
      WHEN 'CHARG'.  "批次F4帮助(通用模板)
        PERFORM frm_open_batch_f4 USING es_row_no-row_id.
      WHEN OTHERS.
    ENDCASE.
    er_event_data->m_event_handled = abap_true.  "标记事件已处理
  ENDMETHOD.
 
  "5. 热点点击事件 - 保留通用模板
  METHOD handle_hotspot_click.
    "示例:点击热点行跳转到详情屏幕
    "READ TABLE gt_alv_out ASSIGNING FIELD-SYMBOL(<ls_alv>) INDEX e_row_id-index.
    "IF sy-subrc EQ 0.
    "  SET PARAMETER ID 'MAT' FIELD <ls_alv>-matnr.
    "  CALL TRANSACTION 'MM03' AND SKIP FIRST SCREEN.
    "ENDIF.
  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& 【初始化事件】- 保留下拉框+日期默认值通用逻辑
*&---------------------------------------------------------------------*
INITIALIZATION.
  "1. 设置日期默认值(近30天)
  s_erdat-low = sy-datum - 30.
  s_erdat-high = sy-datum.
  s_erdat-sign = 'I'.
  s_erdat-option = 'BT'.
  INSERT s_erdat INTO TABLE s_erdat.
 
  "2. 初始化业务类型下拉框(通用模板)
  PERFORM frm_init_biztype_dropdown.
*&---------------------------------------------------------------------*
*& 【选择屏幕输出】- 保留下拉框赋值通用逻辑
*&---------------------------------------------------------------------*
AT SELECTION-SCREEN OUTPUT.
  "重新赋值下拉框(权限过滤模板)
  PERFORM frm_init_biztype_dropdown.
 
*&---------------------------------------------------------------------*
*& 【选择屏幕校验】- 保留通用校验模板
*&---------------------------------------------------------------------*
AT SELECTION-SCREEN.
  "1. 查询模式下单号必输校验(示例)
  IF p_que = abap_true AND s_number[] IS INITIAL AND p_biztype = 'Z001'.
    MESSAGE '该业务类型查询时单号不能为空!' TYPE 'E'.
  ENDIF.
  "2. 日期区间校验(通用模板)
  LOOP AT s_erdat ASSIGNING FIELD-SYMBOL(<ls_date>) WHERE sign = 'I' AND option = 'BT'.
    IF <ls_date>-high - <ls_date>-low > 120.
      MESSAGE '查询日期区间不能超过120天!' TYPE 'E'.
    ENDIF.
  ENDLOOP.
 
*&---------------------------------------------------------------------*
*& 【主程序入口】- 保留完整的通用流程
*&---------------------------------------------------------------------*
START-OF-SELECTION.
  "1. 权限检查(通用模板)
  PERFORM frm_check_authority.
  
  "2. 获取业务数据(核心业务逻辑)
  PERFORM frm_get_business_data.
  
  "3. 显示ALV(通用逻辑)
  PERFORM frm_display_alv.
*&---------------------------------------------------------------------*
*& 【FORM:初始化业务类型下拉框】- 保留通用下拉框逻辑
*&---------------------------------------------------------------------*
FORM frm_init_biztype_dropdown.
  DATA: lt_values TYPE vrm_values,
        lt_filtered TYPE vrm_values.
  "1. 查询业务类型配置表(替换为实际表)
  "SELECT zbiztype AS key, zbizname AS text FROM zt_biztype INTO TABLE lt_values WHERE werks = p_werks.
  "2. 权限过滤(通用模板)
  LOOP AT lt_values ASSIGNING FIELD-SYMBOL(<ls_val>).
    "AUTHORITY-CHECK OBJECT 'Z_BIZ' ID 'BIZTYPE' FIELD <ls_val>-key.
    "IF sy-subrc = 0.
    "  APPEND <ls_val> TO lt_filtered.
    "ENDIF.
  ENDLOOP.
 
  "3. 赋值下拉框
  CALL FUNCTION 'VRM_SET_VALUES'
    EXPORTING
      id              = 'P_BIZTYPE'
      values          = lt_filtered
    EXCEPTIONS
      id_illegal_name = 1.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:权限检查】- 保留通用权限模板
*&---------------------------------------------------------------------*
FORM frm_check_authority.
  "示例:工厂权限检查
  AUTHORITY-CHECK OBJECT 'M_MSEG_WWR'
    ID 'WERKS' FIELD p_werks.
  IF sy-subrc <> 0.
    MESSAGE '无工厂' && p_werks && '的操作权限!' TYPE 'E'.
  ENDIF.
 
  "示例:业务类型权限检查
  "AUTHORITY-CHECK OBJECT 'Z_BIZ' ID 'BIZTYPE' FIELD p_biztype.
  "IF sy-subrc <> 0 AND p_biztype IS NOT INITIAL.
  "  MESSAGE '无该业务类型的操作权限!' TYPE 'E'.
  "ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:获取业务数据】- 保留通用查询模板
*&---------------------------------------------------------------------*
FORM frm_get_business_data.
  "清空输出内表
  CLEAR gt_alv_out.
 
  "=== 通用查询逻辑模板(替换为实际业务表)===
  IF p_ins = abap_true.
    "新增模式:初始化空行(按业务类型填充默认值)
    "PERFORM frm_init_new_row_by_biztype.
  ELSE.
    "查询/修改/删除模式:从业务表取数
    SELECT znumber matnr maktx werks lgort erfmg erfme charg insmk
      FROM zt_biz_main  "替换为实际业务表
      INTO CORRESPONDING FIELDS OF TABLE gt_alv_out
      WHERE werks = p_werks
        AND znumber IN s_number
        AND matnr IN s_matnr
        AND lgort IN s_lgort
        AND erdat IN s_erdat.
    "补充ALV控制字段默认值
    LOOP AT gt_alv_out ASSIGNING FIELD-SYMBOL(<ls_alv>).
      <ls_alv>-status_doc = icon_green_light.  "默认绿色状态
      <ls_alv>-styl = VALUE #( ( fieldname = 'CHARG' style = alv_style_f4 + alv_style_color_positive ) ).
    ENDLOOP.
  ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:显示ALV】- 保留完整的ALV初始化逻辑
*&---------------------------------------------------------------------*
FORM frm_display_alv.
  IF gt_alv_out IS INITIAL AND p_ins <> abap_true.
    MESSAGE '无符合条件的数据!' TYPE 'S'.
    RETURN.
  ENDIF.
  "1. 构建字段目录
  PERFORM frm_build_fieldcatalog.
 
  "2. 设置ALV布局
  gs_layout-cwidth_opt = abap_true.    "列宽自适应
  gs_layout-sel_mode = 'D'.           "多行选择
  gs_layout-stylefname = 'STYL'.      "单元格样式字段
  gs_layout-zebra = abap_true.        "斑马纹
  gs_layout-box_fname = 'SELE'.       "选择框字段名
 
  "3. 调用ALV屏幕
  CALL SCREEN 100.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:构建字段目录】- 保留完整的字段属性控制
*&---------------------------------------------------------------------*
FORM frm_build_fieldcatalog.
  CLEAR gt_fcat.
  "=== 通用字段目录模板(按操作模式控制编辑属性)===
  gt_fcat = VALUE #(
    "选择框
    ( fieldname = 'SELE'      coltext = '选择'        seltext_m = '选择'        checkbox = abap_true )
    "状态图标
    ( fieldname = 'STATUS_DOC' coltext = '状态'       seltext_m = '状态'       outputlen = 10 )
    "推送状态
    ( fieldname = 'STATUS_PUS' coltext = '推送状态'   seltext_m = '推送状态'   outputlen = 10 )
    "消息
    ( fieldname = 'MESS'      coltext = '消息'        seltext_m = '消息'        outputlen = 50 )
    "业务单号(查询模式只读)
    ( fieldname = 'ZNUMBER'   coltext = '业务单号'    seltext_m = '业务单号'    outputlen = 20
      edit = COND #( WHEN p_ins = abap_true THEN abap_true ELSE abap_false ) )
    "物料编码(新增/修改可编辑)
    ( fieldname = 'MATNR'     coltext = '物料编码'    seltext_m = '物料编码'    outputlen = 18
      edit = COND #( WHEN p_ins OR p_mod THEN abap_true ELSE abap_false )
      f4availabl = abap_true )  "启用F4
    "物料描述(只读)
    ( fieldname = 'MAKTX'     coltext = '物料描述'    seltext_m = '物料描述'    outputlen = 40 )
    "工厂(只读)
    ( fieldname = 'WERKS'     coltext = '工厂'        seltext_m = '工厂'        outputlen = 4 )
    "库存地点(新增/修改可编辑)
    ( fieldname = 'LGORT'     coltext = '库存地点'    seltext_m = '库存地点'    outputlen = 4
      edit = COND #( WHEN p_ins OR p_mod THEN abap_true ELSE abap_false ) )
    "数量(新增/修改可编辑)
    ( fieldname = 'ERFMG'     coltext = '数量'        seltext_m = '数量'        outputlen = 10
      edit = COND #( WHEN p_ins OR p_mod THEN abap_true ELSE abap_false ) )
    "单位(只读)
    ( fieldname = 'ERFME'     coltext = '单位'        seltext_m = '单位'        outputlen = 3 )
    "批次(启用F4,样式控制)
    ( fieldname = 'CHARG'     coltext = '批次'        seltext_m = '批次'        outputlen = 10
      f4availabl = abap_true )
    "库存类型(下拉框)
    ( fieldname = 'INSMK'     coltext = '库存类型'    seltext_m = '库存类型'    outputlen = 10
      drdn_hndl = 1 drdn_alias = abap_true )
  ).
 
  "补充字段参考信息(自动读取数据元素属性)
  DATA(lt_dfies) = cl_salv_data_descr=>read_structdescr(
    CAST cl_abap_structdescr( cl_abap_structdescr=>describe_by_data( VALUE ts_alv_out( ) ) )
  ).
  SORT lt_dfies BY fieldname.
  LOOP AT gt_fcat ASSIGNING FIELD-SYMBOL(<fs_fcat>).
    READ TABLE lt_dfies ASSIGNING FIELD-SYMBOL(<fs_dfies>)
      WITH KEY fieldname = <fs_fcat>-fieldname BINARY SEARCH.
    IF sy-subrc EQ 0.
      <fs_fcat>-ref_table = <fs_dfies>-reftable.
      <fs_fcat>-ref_field = <fs_dfies>-reffield.
      <fs_fcat>-datatype = <fs_dfies>-datatype.
      <fs_fcat>-inttype = <fs_dfies>-inttype.
    ENDIF.
  ENDLOOP.
  "设置库存类型下拉框(通用模板)
  PERFORM frm_set_insmk_dropdown.
ENDFORM.
 
*&---------------------------------------------------------------------*
*& 【FORM:设置库存类型下拉框】- 保留通用下拉框逻辑
*&---------------------------------------------------------------------*
FORM frm_set_insmk_dropdown.
  DATA: lt_dral TYPE lvc_t_dral.
 
  "填充下拉框数据
  lt_dral = VALUE #(
    handle = 1
    ( value = '非限制库存' int_value = '' )
    ( value = '冻结库存'   int_value = 'S' )
  ).
  "赋值给ALV
  go_alv->set_drop_down_table( it_drop_down_alias = lt_dral ).
ENDFORM.
 
*&---------------------------------------------------------------------*
*& 【FORM:标记选中行】- 保留通用逻辑
*&---------------------------------------------------------------------*
FORM frm_mark_selected_rows USING pv_flag TYPE char1.
  DATA: lt_selected_rows TYPE lvc_t_row.
 
  IF pv_flag = 'X'.
    "获取选中行并标记
    go_alv->get_selected_rows( IMPORTING et_index_rows = lt_selected_rows ).
    MODIFY gt_alv_out FROM VALUE #( sele = '' ) TRANSPORTING sele WHERE sele = 'X'.
    LOOP AT lt_selected_rows ASSIGNING FIELD-SYMBOL(<ls_row>).
      READ TABLE gt_alv_out INDEX <ls_row>-index ASSIGNING FIELD-SYMBOL(<ls_alv>).
      IF sy-subrc EQ 0.
        <ls_alv>-sele = 'X'.
      ENDIF.
    ENDLOOP.
  ELSE.
    "清空选中标记
    MODIFY gt_alv_out FROM VALUE #( sele = '' ) TRANSPORTING sele WHERE sele = 'X'.
  ENDIF.
ENDFORM.
 
*&---------------------------------------------------------------------*
*& 【FORM:新增行】- 保留通用逻辑+业务类型默认值
*&---------------------------------------------------------------------*
FORM frm_create_new_row.
  APPEND INITIAL LINE TO gt_alv_out ASSIGNING FIELD-SYMBOL(<ls_new>).
  
  "填充默认值
  <ls_new>-werks = p_werks.          "默认工厂
  <ls_new>-updkz = 'I'.              "新增标识
  <ls_new>-status_doc = icon_yellow_light. "新增行黄色状态
  <ls_new>-styl = VALUE #( ( fieldname = 'CHARG' style = alv_style_f4 + alv_style_color_positive ) ).
 
  "按业务类型填充默认值(模板)
  "CASE p_biztype.
  "  WHEN 'Z001'. <ls_new>-bwart = '101'.
  "  WHEN 'Z002'. <ls_new>-bwart = '102'.
  "ENDCASE.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:删除选中行】- 保留按模式处理的通用逻辑
*&---------------------------------------------------------------------*
FORM frm_delete_selected_rows.
  LOOP AT gt_alv_out ASSIGNING FIELD-SYMBOL(<ls_alv>) WHERE sele = 'X'.
    IF p_ins = abap_true.
      "新增模式:直接删除
      DELETE gt_alv_out INDEX sy-tabix.
    ELSE.
      "修改/删除模式:标记删除+状态更新
      <ls_alv>-updkz = 'D'.
      <ls_alv>-status_doc = icon_red_light.
      "状态校验(模板)
      "IF <ls_alv>-zstatus = '1'."已提交审批
      "  MESSAGE '已提交审批的单据不允许删除!' TYPE 'S' DISPLAY LIKE 'E'.
      "  CONTINUE.
      "ENDIF.
    ENDIF.
  ENDLOOP.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:数据校验】- 保留通用校验模板
*&---------------------------------------------------------------------*
FORM frm_check_data.
  DATA: lv_error TYPE boolean.
  LOOP AT gt_alv_out ASSIGNING FIELD-SYMBOL(<ls_alv>) WHERE updkz <> ''.
    CLEAR lv_error.
    "1. 必输项校验
    IF <ls_alv>-matnr IS INITIAL.
      <ls_alv>-mess = '物料编码不能为空!'.
      <ls_alv>-status_doc = icon_red_light.
      lv_error = abap_true.
    ENDIF.
    IF <ls_alv>-erfmg IS INITIAL OR <ls_alv>-erfmg <= 0.
      <ls_alv>-mess = '数量必须大于0!'.
      <ls_alv>-status_doc = icon_red_light.
      lv_error = abap_true.
    ENDIF.
 
    "2. 业务规则校验(模板)
    "IF <ls_alv>-bwart = '101' AND <ls_alv>-ebeln IS INITIAL.
    "  <ls_alv>-mess = '101移动类型必须输入采购订单!'.
    "  <ls_alv>-status_doc = icon_red_light.
    "  lv_error = abap_true.
    "ENDIF.
 
    "3. 库存校验(模板)
    "SELECT SINGLE labst FROM mard INTO DATA(lv_stock)
    "  WHERE matnr = <ls_alv>-matnr AND werks = <ls_alv>-werks AND lgort = <ls_alv>-lgort.
    "IF <ls_alv>-erfmg > lv_stock AND <ls_alv>-bwart = '201'.
    "  <ls_alv>-mess = '库存不足,当前库存:' && lv_stock.
    "  <ls_alv>-status_doc = icon_red_light.
    "  lv_error = abap_true.
    "ENDIF.
 
    IF lv_error = abap_true.
      MESSAGE <ls_alv>-mess TYPE 'S' DISPLAY LIKE 'E'.
      EXIT.
    ENDIF.
  ENDLOOP.
 
  IF lv_error = abap_true.
    MESSAGE '数据校验不通过,请修正后重试!' TYPE 'E'.
  ENDIF.
ENDFORM.
 
*&---------------------------------------------------------------------*
*& 【FORM:数据保存】- 保留完整的批量保存模板
*&---------------------------------------------------------------------*
FORM frm_save_data.
  DATA: lt_biz_main TYPE TABLE OF zt_biz_main,  "替换为实际业务表
        lv_msg      TYPE char100.
  "按业务单号分组保存(批量处理模板)
  LOOP AT gt_alv_out ASSIGNING FIELD-SYMBOL(<ls_alv>)
                     WHERE updkz <> ''
                     GROUP BY ( znumber = <ls_alv>-znumber ).
 
    "清空临时表
    CLEAR lt_biz_main.
    "收集分组数据
    LOOP AT GROUP <ls_alv> ASSIGNING FIELD-SYMBOL(<ls_group>).
      "填充修改人/修改时间(通用)
      <ls_group>-zmodify = sy-uname.
      <ls_group>-zmfdata = sy-datum.
      <ls_group>-zmftime = sy-uzeit.
      "按更新标识处理
      CASE <ls_group>-updkz.
        WHEN 'I'.  "新增
          INSERT CORRESPONDING FIELDS OF <ls_group> INTO TABLE lt_biz_main.
        WHEN 'U'.  "修改
          UPDATE zt_biz_main FROM <ls_group> WHERE znumber = <ls_group>-znumber AND zitem = <ls_group>-zitem.
        WHEN 'D'.  "删除
          DELETE zt_biz_main WHERE znumber = <ls_group>-znumber AND zitem = <ls_group>-zitem.
      ENDCASE.
    ENDLOOP.
    "批量新增(如果有)
    IF lt_biz_main IS NOT INITIAL.
      INSERT zt_biz_main FROM TABLE lt_biz_main.
    ENDIF.
 
    "提交事务
    COMMIT WORK AND WAIT.
    "更新ALV状态
    MODIFY gt_alv_out FROM VALUE #( status_doc = icon_green_light mess = '' )
                  TRANSPORTING status_doc mess
                  WHERE znumber = <ls_alv>-znumber.
 
    "推送审批(模板)
    "PERFORM frm_send_approval USING <ls_alv>-znumber CHANGING lv_msg.
    "IF lv_msg IS NOT INITIAL.
    "  MODIFY gt_alv_out FROM VALUE #( status_pus = icon_red_light mess = lv_msg )
    "                TRANSPORTING status_pus mess
    "                WHERE znumber = <ls_alv>-znumber.
    "ELSE.
    "  MODIFY gt_alv_out FROM VALUE #( status_pus = icon_green_light mess = '推送成功' )
    "                TRANSPORTING status_pus mess
    "                WHERE znumber = <ls_alv>-znumber.
    "ENDIF.
  ENDLOOP.
  MESSAGE '数据保存成功!' TYPE 'S'.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:批次F4帮助】- 保留通用F4模板
*&---------------------------------------------------------------------*
FORM frm_open_batch_f4 USING pv_rowid TYPE int4.
  DATA: gt_batch TYPE TABLE OF ty_batch,  "自定义批次结构
        gs_batch TYPE ty_batch,
        lv_matnr TYPE matnr,
        lv_werks TYPE werks_d.
 
  "读取当前行物料/工厂
  READ TABLE gt_alv_out ASSIGNING FIELD-SYMBOL(<ls_alv>) INDEX pv_rowid.
  IF sy-subrc <> 0 OR <ls_alv>-matnr IS INITIAL.
    MESSAGE '请先输入物料编码!' TYPE 'S' DISPLAY LIKE 'E'.
    RETURN.
  ENDIF.
  lv_matnr = <ls_alv>-matnr.
  lv_werks = <ls_alv>-werks.
  "查询批次数据(通用模板)
  SELECT charg clabs FROM mchb
    INTO CORRESPONDING FIELDS OF TABLE gt_batch
    WHERE matnr = lv_matnr AND werks = lv_werks AND clabs > 0.
 
  IF gt_batch IS INITIAL.
    MESSAGE '该物料无可用批次!' TYPE 'S'.
    RETURN.
  ENDIF.
 
  "调用批次选择屏幕(模板)
  "CALL SCREEN 200 STARTING AT 10 5 ENDING AT 80 20.
  "CHECK line_exists( gt_batch[ sele = 'X' ] ).
  "赋值选中批次
  "READ TABLE gt_batch INTO gs_batch WITH KEY sele = 'X'.
  "<ls_alv>-charg = gs_batch-charg.
ENDFORM.
 
*&---------------------------------------------------------------------*
*& 【FORM:打印处理】- 保留完整的SMARTFORM调用模板
*&---------------------------------------------------------------------*
FORM frm_print_data.
  DATA: lv_fm_name TYPE rs38l_fnam,
        lv_formname TYPE tdsfname VALUE 'ZFORM_BIZ_PRINT',  "替换为实际打印模板名
        gs_ctrl TYPE ssfctrlop,
        gs_output TYPE ssfcompop,
        gt_job_info TYPE ssfcrescl,
        lt_znumber TYPE TABLE OF char20.
  "1. 校验选中数据
  SELECT DISTINCT znumber FROM gt_alv_out INTO TABLE lt_znumber WHERE sele = 'X'.
  IF lt_znumber IS INITIAL.
    MESSAGE '请选择需要打印的数据!' TYPE 'E'.
    RETURN.
  ENDIF.
 
  "2. 获取SMARTFORM函数名
  CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
    EXPORTING
      formname           = lv_formname
    IMPORTING
      fm_name            = lv_fm_name
    EXCEPTIONS
      no_form            = 1
      no_function_module = 2
      OTHERS             = 3.
  IF sy-subrc <> 0.
    MESSAGE '打印模板不存在!' TYPE 'E'.
    RETURN.
  ENDIF.
  "3. 设置打印参数
  gs_ctrl-preview = abap_true.    "预览模式
  gs_ctrl-no_dialog = abap_false. "显示打印对话框
  gs_output-tddest = 'LP01'.      "默认打印机
  "4. 打开打印任务
  CALL FUNCTION 'SSF_OPEN'
    EXPORTING
      control_parameters = gs_ctrl
      output_options     = gs_output
    EXCEPTIONS
      formatting_error   = 1
      internal_error     = 2
      send_error         = 3
      user_canceled      = 4
      OTHERS             = 5.
  IF sy-subrc <> 0.
    MESSAGE '打印任务打开失败!' TYPE 'E'.
    RETURN.
  ENDIF.
 
  "5. 循环打印选中的单号
  LOOP AT lt_znumber INTO DATA(lv_znumber).
    "填充打印表头
    PERFORM frm_fill_print_head USING lv_znumber CHANGING gs_print_head.
    "填充打印表体
    PERFORM frm_fill_print_item USING lv_znumber CHANGING gt_print_item.
    "调用SMARTFORM
    CALL FUNCTION lv_fm_name
      EXPORTING
        control_parameters = gs_ctrl
        output_options     = gs_output
        is_head            = gs_print_head
      TABLES
        it_item            = gt_print_item
      EXCEPTIONS
        formatting_error   = 1
        internal_error     = 2
        send_error         = 3
        user_canceled      = 4
        OTHERS             = 5.
    IF sy-subrc <> 0.
      MESSAGE '打印单号' && lv_znumber && '失败!' TYPE 'E'.
      EXIT.
    ENDIF.
  ENDLOOP.
 
  "6. 关闭打印任务
  CALL FUNCTION 'SSF_CLOSE'
    IMPORTING
      job_output_info  = gt_job_info
    EXCEPTIONS
      formatting_error = 1
      internal_error   = 2
      send_error       = 3
      OTHERS           = 4.
  IF gt_job_info-outputdone = abap_true.
    MESSAGE '打印任务执行完成!' TYPE 'S'.
  ELSE.
    MESSAGE '打印任务执行失败!' TYPE 'E'.
  ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:填充打印表头】- 保留通用模板
*&---------------------------------------------------------------------*
FORM frm_fill_print_head USING pv_znumber TYPE char20
                        CHANGING ps_print_head TYPE ty_print_head.
  "1. 查询公司名称
  SELECT SINGLE butxt FROM t001 INTO ps_print_head-butxt WHERE bukrs = '1000'.  "替换为实际公司代码
  "2. 查询业务单头信息
  "SELECT SINGLE zcrdate zcreator FROM zt_biz_head INTO CORRESPONDING FIELDS OF ps_print_head
  "  WHERE znumber = pv_znumber.
 
  "3. 查询仓库描述
  "SELECT SINGLE lgobe FROM t001l INTO ps_print_head-lgobe WHERE werks = p_werks AND lgort = ps_print_head-lgort.
 
  "4. 查询制单人信息
  "SELECT SINGLE name department FROM user_addr INTO (ps_print_head-zzdr, ps_print_head-department)
  "  WHERE bname = ps_print_head-zcreator.
  "5. 填充固定值
  ps_print_head-znumber = pv_znumber.
  ps_print_head-title = '业务单打印凭证'.
  ps_print_head-zsum_lfimg = 0.  "初始化合计数量
ENDFORM.
*&---------------------------------------------------------------------*
*& 【FORM:填充打印表体】- 保留通用模板
*&---------------------------------------------------------------------*
FORM frm_fill_print_item USING pv_znumber TYPE char20
                        CHANGING pt_print_item TYPE TABLE OF ty_print_item.
  DATA: lv_sum TYPE erfmg.
  "清空表体
  CLEAR gt_print_item.
 
  "查询打印表体数据
  LOOP AT gt_alv_out INTO DATA(ls_alv) WHERE znumber = pv_znumber.
    "填充表体字段
    gs_print_item-znumber = ls_alv-znumber.
    gs_print_item-matnr = ls_alv-matnr.
    gs_print_item-maktx = ls_alv-maktx.
    gs_print_item-erfmg = ls_alv-erfmg.
    gs_print_item-lgort = ls_alv-lgort.
 
    "查询单位描述
    SELECT SINGLE msehl FROM t006a INTO gs_print_item-msehl
      WHERE msehi = ls_alv-erfme AND spras = sy-langu.
    APPEND gs_print_item TO gt_print_item.
    CLEAR gs_print_item.
    "累加合计数量
    lv_sum = lv_sum + ls_alv-erfmg.
  ENDLOOP.
 
  "更新合计数量到表头
  gs_print_head-zsum_lfimg = lv_sum.
ENDFORM.
*&---------------------------------------------------------------------*
*& 【屏幕100 PBO】- 保留完整的ALV初始化
*&---------------------------------------------------------------------*
MODULE status_100 OUTPUT.
  SET PF-STATUS 'STATUS_100'.  "创建包含退出/返回的状态
  SET TITLEBAR 'TITLE_100' WITH 'ALV通用框架 - 业务单管理'.
ENDMODULE.
 
MODULE init_alv OUTPUT.
  IF go_alv IS NOT BOUND.
    "创建ALV对象
    go_alv = NEW cl_gui_alv_grid(
      i_lifetime = cl_gui_control=>lifetime_dynpro
      i_parent   = cl_gui_container=>screen0
    ).
    "注册F4字段
    go_alv->register_f4_for_fields( VALUE #( 
      ( fieldname = 'MATNR' register = abap_true internal = abap_true )
      ( fieldname = 'CHARG' register = abap_true internal = abap_true ) ) ).
 
    "首次显示ALV
    go_alv->set_table_for_first_display(
      EXPORTING
        is_layout            = gs_layout
        i_save               = 'A'  "允许保存布局
        is_variant           = VALUE disvariant( report = sy-cprog 
                                               variant = COND #( WHEN p_ins THEN 'INS'
                                                               WHEN p_mod THEN 'MOD'
                                                               WHEN p_del THEN 'DEL'
                                                               ELSE 'QUE' ) )
        it_toolbar_excluding = VALUE #( ( cl_gui_alv_grid=>mc_fg_edit ) )
      CHANGING
        it_outtab            = gt_alv_out
        it_fieldcatalog      = gt_fcat
    ).
 
    "注册事件
    SET HANDLER:
      lcl_alv_event_handler=>handle_toolbar FOR go_alv,
      lcl_alv_event_handler=>handle_data_changed_finished FOR go_alv,
      lcl_alv_event_handler=>handle_user_command FOR go_alv,
      lcl_alv_event_handler=>handle_onf4 FOR go_alv,
      lcl_alv_event_handler=>handle_hotspot_click FOR go_alv.
    "激活工具栏+编辑事件
    go_alv->set_toolbar_interactive( ).
    go_alv->register_edit_event( cl_gui_alv_grid=>mc_evt_modified ).
  ELSE.
    "刷新ALV
    go_alv->refresh_table_display( is_stable = VALUE lvc_s_stbl( row = abap_true col = abap_true ) ).
  ENDIF.
ENDMODULE.
*&---------------------------------------------------------------------*
*& 【屏幕100 PAI】- 保留通用退出逻辑
*&---------------------------------------------------------------------*
MODULE user_command_100 INPUT.
  CASE ok_code.
    WHEN '&F03' OR '&F15' OR '&EXIT' OR '&CANC'.
      LEAVE TO SCREEN 0.
    WHEN OTHERS.
  ENDCASE.
ENDMODULE.