REPORT YR_SEP_12. CONSTANTS PQR TYPE P DECIMALS 4 VALUE '1.2356'. Write: / 'The value of PQR is:', PQR.
输出:
1
The value of PQR is: 1.2356
2. 复杂常量
由多个字段组成的结构化常量。
1 2 3 4 5
BEGIN OF EMPLOYEE, Name(25) TYPE C VALUE 'Management Team', Organization(40) TYPE C VALUE 'Tutorials Point Ltd', Place(10) TYPE C VALUE 'India', END OF EMPLOYEE.
💡代码解析
EMPLOYEE是一个由Name、Organization和Place字段组成的复杂常量。
3. 参考常量
引用其他对象或数据的常量。
1
CONSTANTS null_pointer TYPE REF TO object VALUE IS INITIAL.
📌参考常量用途
我们可以在比较中使用常量引用,或者我们可以将其传递给过程。
🔧 常量使用示例
实际应用场景
1. 配置常量
1 2 3 4 5
" 应用程序配置常量 CONSTANTS: MAX_RETRIES TYPE I VALUE 3, TIMEOUT_SECS TYPE I VALUE 30, DEBUG_MODE TYPE C VALUE 'X'.
2. 业务规则常量
1 2 3 4 5
" 业务规则 CONSTANTS: TAX_RATE TYPE P DECIMALS 2 VALUE '0.18', DISCOUNT_THRESHOLD TYPE I VALUE 1000, CURRENCY_CODE TYPE C VALUE 'CNY'.
3. 系统常量
1 2 3 4 5
" 系统参数 CONSTANTS: COMPANY_CODE TYPE C VALUE '1000', FISCAL_YEAR TYPE N VALUE '2024', DECIMAL_SEPARATOR TYPE C VALUE '.'.
DATA: date TYPE D, -- 日期类型 time TYPE T, -- 时间类型 year TYPE I, -- 年份 month TYPE I, -- 月份 day TYPE I, -- 日期 hour TYPE I, -- 小时 minute TYPE I, -- 分钟 second TYPE I. -- 秒数
DATA: lv_timestamp TYPE TIMESTAMP, lv_date TYPE D, lv_time TYPE T, lv_tzone TYPE TIMEZONE VALUE 'CET'. -- 中欧时间
-- 获取当前时间戳 GET TIME STAMP FIELD lv_timestamp.
-- 转换为指定时区 CONVERT TIME STAMP lv_timestamp TIME ZONE lv_tzone INTO lv_date lv_time.
Write: / 'Time in CET:', lv_date, lv_time.
📊 实际应用场景
1. 业务日期处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
" 计算工作日 DATA: ld_start_date TYPE D VALUE '20240301', ld_end_date TYPE D, ld_work_days TYPE I.
ld_end_date = ld_start_date + 30. -- 项目截止日期
" 计算工作日(排除周末) DO 31 TIMES. IF ld_start_date+6(2) CO '012345' AND " 周一到周五 ld_start_date+6(2) <> '00'. " 不是周末 ADD 1 TO ld_work_days. ENDIF. ADD 1 TO ld_start_date. ENDDO.
Write: / 'Total Work Days:', ld_work_days.
2. 时间戳记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14
" 记录操作时间戳 DATA: lv_timestamp TYPE TIMESTAMP, lv_operation_log TYPE TABLE OF tsmd_log.
" 获取操作开始时间 GET TIME STAMP FIELD lv_timestamp.
" 记录日志 APPEND VALUE #( uname = sy-uname timestampl = lv_timestamp action = 'CREATE_ORDER' ) TO lv_operation_log.
3. 日期验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
" 验证日期有效性 DATA: ld_input_date TYPE D VALUE '20240230', lv_valid TYPE ABAP_BOOL.
" 检查日期是否有效 CALL FUNCTION 'DATE_CHECK_PLAUSIBILITY' EXPORTING date = ld_input_date IMPORTING plausible = lv_valid.
IF lv_valid = abap_true. Write: / 'Date is valid'. ELSE. Write: / 'Date is invalid'. ENDIF.
⚠️ 注意事项
1. 日期范围
1 2 3 4 5 6
" ❌ 危险:可能产生无效日期 DATA: ld_date TYPE D. ld_date = '20241301'. -- 无效的月份
" ✅ 安全:使用系统日期 ld_date = sy-datum.
2. 时区转换
1 2 3 4 5 6 7 8
" 注意时区对时间计算的影响 DATA: lv_utc_time TYPE T VALUE '235959', lv_local_time TYPE T.
REPORT zuserexit1. TABLES: tstc, tstct, tadir, trdir, tfdir, enlfdir, modsapt, modact. DATA: jtab LIKE tadir OCCURS 0 WITH HEADER LINE, field1(30), v_devclass LIKE tadir-devclass.
PARAMETERS: p_tcode LIKE tstc-tcode OBLIGATORY.
SELECT SINGLE * FROM tstc WHERE tcode EQ p_tcode. IF sy-subrc EQ 0. SELECT SINGLE * FROM tadir WHERE pgmid = 'R3TR' AND object = 'PROG' AND obj_name = tstc-pgmna. MOVE tadir-devclass TO v_devclass.
IF sy-subrc NE 0. SELECT SINGLE * FROM trdir WHERE name = tstc-pgmna. IF trdir-subc EQ 'F'.
SELECT SINGLE * FROM tfdir WHERE pname = tstc-pgmna.
SELECT SINGLE * FROM enlfdir WHERE funcname = tfdir-funcname.
SELECT SINGLE * FROM tadir WHERE pgmid = 'R3TR' AND object = 'FUGR' AND obj_name = enlfdir-area. MOVE tadir-devclass TO v_devclass. ENDIF. ENDIF.
SELECT * FROM tadir INTO TABLE jtab WHERE pgmid = 'R3TR' AND object = 'SMOD' AND devclass = v_devclass.
SELECT SINGLE * FROM tstct WHERE sprsl EQ sy-langu AND tcode EQ p_tcode. FORMAT COLOR COL_POSITIVE INTENSIFIED OFF.
IF NOT jtab[] IS INITIAL. WRITE:/(95) sy-uline. FORMAT COLOR COL_HEADING INTENSIFIED ON. WRITE:/1 sy-vline, 2 'Exit Name', 21 sy-vline , 22 'Description', 95 sy-vline. WRITE:/(95) sy-uline. LOOP AT jtab. SELECT SINGLE * FROM modsapt WHERE sprsl = sy-langu AND name = jtab-obj_name. FORMAT COLOR COL_NORMAL INTENSIFIED OFF. WRITE:/1 sy-vline, 2 jtab-obj_name HOTSPOT ON, 21 sy-vline , 22 modsapt-modtext, 95 sy-vline. ENDLOOP. WRITE:/(95) sy-uline. DESCRIBE TABLE jtab. SKIP. FORMAT COLOR COL_TOTAL INTENSIFIED ON. WRITE:/ 'No of Exits:' , sy-tfill. ELSE. FORMAT COLOR COL_NEGATIVE INTENSIFIED ON. WRITE:/(95) 'User Exit doesn''t exist'. ENDIF. ELSE. FORMAT COLOR COL_NEGATIVE INTENSIFIED ON. WRITE:/(95) 'Transaction Code Does Not Exist'. ENDIF.
AT LINE-SELECTION. GET CURSOR FIELD field1. CHECK field1(4) EQ 'JTAB'. SET PARAMETER ID 'MON' FIELD sy-lisel+1(10). CALL TRANSACTION 'SMOD' AND SKIP FIRST SCREEN.
执行示例
处理时,输入事务代码 ME01,然后按 F8(执行)按钮。
💥使用方法
运行程序,输入要查询的事务代码
按 F8 执行
程序会列出该事务代码关联的所有用户出口
点击出口名称可跳转到 SMOD 进行查看
🏗️ SAP增强技术对比
graph TD
A[SAP增强技术] --> B[用户出口<br>User Exit]
A --> C[客户出口<br>Customer Exit]
A --> D[BADI<br>Business Add-In]
B --> B1["仅SD模块<br>修改标准程序"]
C --> C1["模块化增强<br>函数模块调用"]
D --> D1["面向对象<br>可多次实现<br>支持过滤器"]
TYPES: BEGIN OF ts_doc_key, bukrs TYPE bkpf-bukrs, belnr TYPE bkpf-belnr, gjahr TYPE bkpf-gjahr, END OF ts_doc_key, BEGIN OF ts_doc_header, bukrs TYPE bkpf-bukrs, belnr TYPE bkpf-belnr, gjahr TYPE bkpf-gjahr, awtyp TYPE bkpf-awtyp, awsys TYPE bkpf-awsys, awkey TYPE bkpf-awkey, END OF ts_doc_header.
DATA: lt_doc_key TYPE SORTED TABLE OF ts_doc_key WITH UNIQUE KEY bukrs belnr gjahr. DATA: lt_header1 TYPE STANDARD TABLE OF ts_doc_header WITH EMPTY KEY, lt_header2 TYPE STANDARD TABLE OF ts_doc_header WITH EMPTY KEY.
" 准备10个凭证键 SELECT bukrs, belnr, gjahr FROM bkpf INTO TABLE @lt_doc_key UP TO 10 ROWS WHERE awtyp = 'BKPF'.
" 使用 concat 连接字段匹配 AWKEY SELECT a~bukrs, a~belnr, a~gjahr, a~awtyp, a~awsys, a~awkey FROM bkpf AS a INNER JOIN @lt_doc_key AS b ON a~awkey = concat( concat( b~belnr, b~bukrs ), b~gjahr ) WHERE a~awtyp = 'BKPF' INTO TABLE @lt_header1.
方法2: 分别匹配字段(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
" 分别匹配各字段,性能更优 SELECT a~bukrs, a~belnr, a~gjahr, a~awtyp, a~awsys, a~awkey FROM bkpf AS a INNER JOIN @lt_doc_key AS b ON a~bukrs = b~bukrs AND a~belnr = b~belnr AND a~gjahr = b~gjahr WHERE a~awtyp = 'BKPF' INTO TABLE @lt_header2.
* 主程序 REPORT z_main_program. INCLUDE Z_GLOBAL_CONSTANTS. " 包含常量定义 INCLUDE Z_GLOBAL_FUNCTIONS. " 包含函数定义
START-OF-SELECTION. DATA: lv_result TYPE i. * 使用包含的函数 lv_result = calculate_discount( 1000, 10 ). WRITE: / 'Discount Amount:', lv_result.
3. 子程序示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
REPORT z_subroutine_example.
* 定义子程序 FORM calculate_tax USING iv_amount TYPE p CHANGING cv_tax_amount TYPE p. DATA: lv_rate TYPE p VALUE 0.10. cv_tax_amount = iv_amount * lv_rate. ENDFORM.
* 调用子程序 DATA: lv_amount TYPE p VALUE 1000, lv_tax TYPE p.
PERFORM calculate_tax USING lv_amount CHANGING lv_tax.
WRITE: / 'Tax Amount:', lv_tax.
🎯 模块化最佳实践
1. 模块设计原则
graph LR
A[模块设计原则] --> B[单一职责]
A --> C[高内聚低耦合]
A --> D[接口清晰]
A --> E[功能独立]
CLASS class1 DEFINITION. PUBLIC SECTION. DATA: text1 TYPE char25 VALUE 'Public Data'. METHODS meth1. PROTECTED SECTION. DATA: text2 TYPE char25 VALUE 'Protected Data'. PRIVATE SECTION. DATA: text3 TYPE char25 VALUE 'Private Data'. ENDCLASS.
CLASS class1 Definition. PUBLIC Section. Data text1 TYPE char25 Value 'This is CLASS Attribute'. Methods method1. ENDCLASS.
CLASS class1 Implementation. Method method1. Data text1 TYPE char25 Value 'This is METHOD Attribute'. Write: / ME->text1, / text1. EndMethod. ENDCLASS.
START-OF-Selection. Data objectx TYPE REF TO class1. Create Object objectx. CALL Method objectx->method1.
* 类定义 CLASS zcl_car DEFINITION. PUBLIC SECTION. " 公共属性 DATA: make TYPE string, model TYPE string, speed TYPE i, max_speed TYPE i VALUE 200. " 公共方法 METHODS: constructor IMPORTING iv_make TYPE string iv_model TYPE string, accelerate IMPORTING iv_amount TYPE i RAISING cx_speed_limit, brake IMPORTING iv_amount TYPE i, get_speed RETURNING(value(rv_speed)) TYPE i. PROTECTED SECTION. " 受保护属性 DATA: engine_on TYPE abap_bool, fuel TYPE p LENGTH 16 DECIMALS 2 VALUE 50.0. PRIVATE SECTION. " 私有方法 METHODS: check_fuel RAISING cx_insufficient_fuel, update_fuel IMPORTING iv_consumption TYPE p. ENDCLASS.
CLASS num_counter DEFINITION. PUBLIC SECTION. INTERFACES my_interface1. METHODS add_number. PRIVATE SECTION. DATA num TYPE i. ENDCLASS.
CLASS num_counter IMPLEMENTATION. METHOD my_interface1~msg. WRITE: / 'The number is', num. ENDMETHOD. METHOD add_number. ADD 7 TO num. ENDMETHOD. ENDCLASS.
CLASS drive1 DEFINITION. PUBLIC SECTION. INTERFACES my_interface1. METHODS speed1. PRIVATE SECTION. DATA wheel1 TYPE i. ENDCLASS.
CLASS drive1 IMPLEMENTATION. METHOD my_interface1~msg. WRITE: / 'Total number of wheels is', wheel1. ENDMETHOD. METHOD speed1. ADD 4 TO wheel1. ENDMETHOD. ENDCLASS.
START-OF-SELECTION. DATA object1 TYPE REF TO num_counter. CREATE OBJECT object1. CALL METHOD object1->add_number. CALL METHOD object1->my_interface1~msg.
DATA object2 TYPE REF TO drive1. CREATE OBJECT object2. CALL METHOD object2->speed1. CALL METHOD object2->my_interface1~msg.
📝 输出结果
1 2
The number is 7 Total number of wheels is 4
🔍 代码解析
graph TB
A[接口 my_interface1] -->|实现| B[num_counter]
A -->|实现| C[drive1]
B --> D[add_number方法]
B --> E[msg实现]
C --> F[speed1方法]
C --> G[msg实现]
graph LR
A[调用方] --> B[接口]
B --> C[实现类1]
B --> D[实现类2]
B --> E[实现类3]
调用方只需要知道接口,而不需要知道具体的实现类。
🛡️ 3. 标准化契约
接口定义了统一的契约,确保所有实现类都遵循相同的规范。
🎯 使用场景
1. 策略模式实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
INTERFACE strategy. METHODS: execute IMPORTING data TYPE any. ENDINTERFACE.
class_algo_a DEFINITION. PUBLIC SECTION. INTERFACES strategy. PRIVATE SECTION. METHODS: algo_a_specific IMPORTING data TYPE any. ENDCLASS.
class_algo_b DEFINITION. PUBLIC SECTION. INTERFACES strategy. PRIVATE SECTION. METHODS: algo_b_specific IMPORTING data TYPE any. ENDCLASS.
2. 工厂模式支持
1 2 3 4 5 6 7 8 9 10
INTERFACE factory. CLASS-METHODS: create_object RETURNING VALUE(obj) TYPE ref to any. ENDINTERFACE.
class_product_factory DEFINITION. PUBLIC SECTION. INTERFACES factory. PRIVATE SECTION. METHODS: create_product RETURNING VALUE(obj) TYPE ref to any. ENDCLASS.
3. 事件处理
1 2 3 4 5 6 7 8 9 10 11 12
INTERFACE event_handler. METHODS: handle_event IMPORTING event_type TYPE string data TYPE any. ENDINTERFACE.
class_ui_controller DEFINITION. PUBLIC SECTION. INTERFACES event_handler. PRIVATE SECTION. METHODS: process_event IMPORTING event_type TYPE string data TYPE any. ENDCLASS.