上一章讲了Numbering:Booking ID/Booking Supplement ID,ABAP不太懂 的朋友还需要点儿时间理解一下。
SAP学习笔记 - 开发53 - RAP开发 Managed App Numbering 实战 Numbering- Booking ID/Booking Supplement ID-CSDN博客
SAP学习笔记 - 开发54 - RAP开发 Managed App Modify Entity - Short Form,Long Form,Dynamic Form-CSDN博客
SAP学习笔记 - 开发55 - RAP开发 Managed App 到目前为止的代码总结,Trial 代码恢复-CSDN博客
本章继续将RAP的知识。
目录
(1) features: {instance | global}
g),use action copyTravel - Z04_PV_Travel_M
下面是详细内容。
1,Processor 和 Approver 是什么?
- Processor:Create individual travel instances,creates and manages individual flights,
and adds supplements to flight bookings.
- Approver:Verification of the recorded travel data entered by the processor
也就是说之前讲的那些章节,都是属于 Processor 范围。
接下来来看看 Approver部分。

1-1,Approver
具体来说是什么呢?
- Accept Travel Action:比如将 Overall status 设为 A:Accepted
- Reject Travel Action:比如将 Overall status 设为 X:Rejected
这两个动作是属于在某一个既存的instance上进行操作,属于 Non-Factory。
其实就是在List Report画面上加两个按钮,一个叫 Accept Travel,一个叫 Reject Travel。
1-2,Processor
这里要列举一个之前没涉及到操作,就是行拷贝,因为产生了新 instance,属于 Factory。
当然其实也就是在List Report上面加一个 Copy Travel 按钮。
当然拷贝的时候,它会连同association 的Booking,Booking Supplement 一起拷贝了。
上面说到了 Factory,Non-Factory 这两个概念,它们都是RAP actions。
下面来具体看一下什么是RAP actions。
2,RAP actions
在 RAP (Restful Application Programming) 开发中,Actions(动作)是用于处理用户交互逻辑的核心机制。RAP 主要支持三种类型的 Actions:
- Non-factory actions(非工厂动作)
Custom logic that changes existing entity instances.
- Factory actions(工厂动作)
It can be used to create RAP BO entity instances.
- Save actions(保存动作)
It can be non-factory actions or factory actions executed during the RAP save sequence.
在RAP(RESTful Application Programming Model)开发中,action是用于执行特定业务逻辑的操作,它们定义在行为定义(Behavior Definition)中,并集成到业务对象(Business Object)中。RAP action主要分为三种类型:Factory、Non-factory和Save。每种类型都有其特定用途和实现方式。下面逐一说明它们的定义、使用场景、示例代码(使用ABAP语言),以及注意事项。
2-1. Factory Action
- 定义:Factory Action用于创建新的业务对象实例。它类似于“工厂模式”,在运行时动态生成一个新实体(entity)。例如,创建一个新的订单或客户记录。Factory Action通常返回新创建的实例标识符(如键值)。
- 使用场景:
- 当需要从零开始创建一个新的业务对象时。
- 适用于初始化复杂对象,例如在用户界面中点击“新建”按钮时触发。
- 不能用于修改现有实例;只专注于创建。
- 示例代码: 在行为定义文件中定义Factory Action:
define behavior for ZOrder " 定义业务对象行为
...
action factory Create; " 声明一个名为Create的Factory Action
...
enddefine.
在行为实现中编写逻辑:
method Create. " 实现Create方法
" 创建新订单实例的逻辑
data: ls_order type ZOrder. " 假设ZOrder是业务对象结构
ls_order-order_id = cl_system_uuid=>create_uuid_c32( ). " 生成唯一ID
ls_order-status = 'New'.
" 将新实例保存到临时缓冲区
modify entities of ZOrder in local mode
create set FIELDS with value #( ( %cid = 'NEW_ORDER' %data = ls_order ) ).
" 返回新创建的实例
mapped-order = value #( ( %cid = 'NEW_ORDER' %key = ls_order-order_id ) ).
endmethod.
- 注意事项:
- Factory Action必须与
create操作关联,不能用于更新或删除。 - 在RAP框架中,新实例在事务提交前存在于临时存储中,需要结合Save Action来持久化。
- Factory Action必须与
下面是 Factory 的语法结构,跟 Non-Factory很相似(各选项详情请看 Non-Factory Section)
[internal][static [default]] factory action
{
[features: {instance | global}]
[precheck]
[authorization:none]
[authorization:update]
[authorization:global]
[authorization:instance]
[lock:none]
}
ActionName [external 'ExternalName']
[InputParameter]
[cardinality]
- [cardinality]:作用:指定创建实体的数量。
- TO 1:创建单个实体(默认)
- TO MANY:创建多个实体
2-2. Non-factory Action
- 定义:Non-factory Action用于执行非创建性的业务操作,例如更新、删除、状态变更或自定义逻辑。它不生成新实例,而是作用于现有业务对象。Non-factory Action更灵活,可以处理多种业务规则。
- 使用场景:
- 修改现有实例的属性,如更新订单状态或删除记录。
- 执行自定义业务逻辑,如审批流程、计算或验证。
- 当操作不需要创建新实体时使用。
- 示例代码: 在行为定义中声明Non-factory Action:
define behavior for ZOrder
...
action non_factory UpdateStatus; " 声明一个名为UpdateStatus的Non-factory Action
...
enddefine.
在行为实现中编写逻辑:
method UpdateStatus. " 实现UpdateStatus方法
" 更新订单状态的逻辑
data: lt_keys type table for update ZOrder. " 输入参数:待更新的键值
data: ls_data type ZOrder.
" 从输入中获取键值
read entities of ZOrder in local mode
entity Order
fields ( status ) with lt_keys
result data(lt_orders).
" 更新状态
loop at lt_orders assigning field-symbol(<fs_order>).
<fs_order>-status = 'Approved'. " 设置新状态
endloop.
" 应用更新
modify entities of ZOrder in local mode
update set fields with value lt_orders.
" 返回成功消息
reported-order = value #( for order in lt_orders ( %key = order-order_id %msg = new_message( id 'ZMESSAGE' number '001' severity 'success' ) ) ).
endmethod.
- 注意事项:
- Non-factory Action可以接收参数,并在运行时验证输入。
- 它通常与
update或delete操作结合,但不限于此。 - 在UI层,Non-factory Action可通过OData服务暴露为API端点。
以下是 RAP 开发中 Non-factory Action 的语法结构:
[internal][static] [repeatable] action
[(
[features: {instance | global}]
[precheck]
[authorization:none]
[authorization:update]
[authorization:global]
[authorization:instance]
[lock:none]
)]
a. 基本修饰符
(1) internal
-
含义:标记为内部使用的 Action,不会暴露给 OData 服务。
-
用途:仅在业务对象内部调用(如其他 Action 或方法中),外部 API 无法访问。
-
示例:
-
INTERNAL ACTION validate_data...
(2) static
-
含义:静态 Action,不依赖特定实体实例。
-
用途:无需实体键值就能调用,适合全局操作(如计算、配置读取)。
-
示例:abap
-
STATIC ACTION calculate_tax...
(3) repeatable
-
含义:允许重复执行,即使业务对象状态未改变。
-
用途:适用于幂等操作(如刷新数据、重新计算)。
-
对比:默认情况下,RAP 会阻止重复执行相同操作。
b. 括号内选项
(1) features: {instance | global}
-
作用:定义 Action 的作用范围。
-
instance:操作绑定到特定实体实例(默认值)。
-
-
" 需要传入实体键(如 order_id) POST /Orders(123)/approve -
global:跨实例操作,无需指定实体键。 -
" 无需实体键 POST /batch_approve
(2) precheck
-
含义:启用预检查机制。
-
用途:在正式执行前运行验证逻辑(如权限/数据校验)。
-
实现:
-
METHODS approve_precheck FOR PRECHECK...
(3) authorization 选项组
-
作用:控制权限验证级别:
选项 含义 适用场景 none跳过所有权限检查 公开操作(如查询汇率) update检查更新权限 修改数据的操作(如状态变更) global检查全局权限 跨实例操作(如批量处理) instance检查实例级权限(默认) 单实体操作(如审批订单)
(4) lock:none
-
含义:禁用自动锁机制。
-
用途:避免框架自动锁定实体,适用于只读操作。
-
对比:默认情况下,RAP 会锁定相关实体防止并发冲突。
c. 完整示例
INTERNAL STATIC REPEATABLE ACTION features: global precheck authorization:none lock:none
RecalculateAllTax [external 'RecalcTax']:
IMPORTING
iv_tax_rate TYPE buzei,
EXPORTING
ev_count TYPE i.
d. 选项解析:
-
INTERNAL:仅内部调用,不暴露 API -
STATIC:无需实体键 -
REPEATABLE:允许重复执行 -
features: global:跨实例操作 -
precheck:启用预检查 -
authorization:none:跳过权限验证 -
lock:none:禁用自动锁
e. 最佳实践建议
e-1. 安全敏感操作:
使用 authorization:instance + precheck 双重验证
ACTION approve_order features: instance precheck authorization:update...
e-2. 高性能只读操作:
组合 lock:none + authorization:none
STATIC ACTION get_config lock:none authorization:none...
e-3. 批量处理:
使用 features:global + static
STATIC ACTION bulk_update features: global...
e-4. 免重复提交:
默认禁用 repeatable,除非明确需要(如刷新操作)
这些选项提供了细粒度的控制,使 Non-factory Actions 能灵活适应不同业务场景的需求。
2-3. Save Action
- 定义:Save Action用于在事务结束时持久化所有更改(包括Factory和Non-factory Action的修改)到数据库。它是RAP框架的核心部分,确保数据一致性。Save Action不是直接由开发者定义的操作,而是由RAP引擎自动处理,开发者只需在行为定义中启用它。
- 使用场景:
- 当需要提交事务时,例如用户点击“保存”按钮后。
- 将临时缓冲区中的创建、更新或删除操作写入数据库。
- 在事务提交前执行最终验证或后处理逻辑。
- 示例代码: 在行为定义中启用Save Action:
define behavior for ZOrder
persistent table ZORDER_TABLE " 指定持久化表
...
save; " 启用Save Action
...
enddefine.
在行为实现中,开发者可以定义save_modified方法处理自定义逻辑(可选):
method save_modified. " 覆盖RAP的保存方法
" 自定义验证或后处理逻辑
data(lt_changes) = get_changes( ). " 获取所有待保存的更改
" 检查数据一致性
loop at lt_changes assigning field-symbol(<fs_change>).
if <fs_change>-status = 'Invalid'.
" 如果无效,回滚事务
failed = value #( ( %key = <fs_change>-order_id ) ).
reported = value #( ( %key = <fs_change>-order_id %msg = new_message( id 'ZMESSAGE' number '002' severity 'error' ) ) ).
endif.
endloop.
" 如果无错误,调用父类方法执行实际保存
if failed is initial.
super->save_modified( ).
endif.
endmethod.
- 注意事项:
- Save Action是自动触发的,开发者不能直接调用它;它由RAP框架在事务提交时执行。
- 它确保了ACID属性(原子性、一致性、隔离性、持久性)。
- 如果未启用Save Action,所有修改只存在于内存中,不会持久化。
2-4.总结
- 关系与区别:Factory Action专注于创建新实例,Non-factory Action处理修改和自定义逻辑,而Save Action负责最终保存所有更改。三者协作实现完整的业务事务:Factory和Non-factory在临时缓冲区操作,Save在提交时持久化。
- 最佳实践:
- 在设计时,明确每种action的职责:Factory用于创建,Non-factory用于业务规则,Save用于持久化。
- 使用行为定义工具(如ADT)来定义action,确保与OData服务集成。
- 测试时,注意事务边界:Save Action触发后,更改不可逆。
- 附加说明:在RAP开发中,这些action通过CDS行为定义暴露,支持Fiori应用的无缝集成。如果有具体场景或代码问题,可以提供更多细节,我会进一步解释!
下面在系统上做一下操作,看怎么实现这个RAP actions。
3,定义4个 RAP actions
- acceptTravel/ rejectTravel :在既存 instance上操作,属于Non-factory Actions(非工厂动作)。
- copyTravel :虽然参照了既存 instance,但是会产生新 instance,属于 Factory Action
- reCalcTotalPrice:动态计算总价,这个不是谁调用的,而实价格变了之后就会调用来重新计算
它也是 Non-Factory,而且也不会开放给外部,所以是Internal 的
action acceptTravel result[1] $self;
action rejectTravel result[1] $self;
factory action copyTravel [1];
internal action reCalcTotalPrice;

按下 Ctrl + 1,然后选择全部创建,就是一次性在Behavior Pool 里面全部创建该4个方法
METHODS acceptTravel FOR MODIFY
IMPORTING keys FOR ACTION Z04_DV_Travel_M~acceptTravel RESULT result.
METHODS copyTravel FOR MODIFY
IMPORTING keys FOR ACTION Z04_DV_Travel_M~copyTravel.
METHODS reCalcTotalPrice FOR MODIFY
IMPORTING keys FOR ACTION Z04_DV_Travel_M~reCalcTotalPrice.
METHODS rejectTravel FOR MODIFY
IMPORTING keys FOR ACTION Z04_DV_Travel_M~rejectTravel RESULT result.
下面来实现这些个RAP actions
4,RAP action - copyTravel
因为 Travel_M,Booking_M,Booksuppl_M 这三个表是关联的,所以咱们这里的Copy Travel,实际上是连着子表一起拷贝的。
4-1,Travel_M 的Copy
流程如下:
- 画面选择1条,然后点Copy Travel 按钮
- 这样 keys 内置的变量(也是一个内表)就会传过来拷贝元的 Travel ID
- 根据拷贝元的 Travel ID,就可以抽出 Travel_M,Booking_M,Booksuppl_M的关联数据
- 设定抽出的数据到临时变量里,这里有几点要注意
- 在新规一条数据的时候,key(比如Travel ID)尚未确定之前,RAP 会借用 cid,所以需要设定cid
- 对于子对象(比如Booking_M),不仅需要cid,还需要 cid_ref,以关联父对象

a),定义变量
为了后面 Modify 的时候传入用:
* Define create table variable
DATA: it_travel TYPE TABLE FOR CREATE Z04_DV_Travel_M,
it_booking_cba TYPE TABLE FOR CREATE Z04_DV_Travel_M\_Booking,
it_booksuppl_cba TYPE TABLE FOR CREATE Z04_DV_Booking_M\_BookingSupplement.
b),根据 keys 内表,查询关联数据
* Read original keys which need to copy
==>这2行是为了判断keys 内表中,不应该有cid 为空的数据,因为拷贝元是既存数据嘛,肯定有 cid的。
READ TABLE keys ASSIGNING FIELD-SYMBOL(<ls_keys>) WITH KEY %cid = ' '.
==》Assert 判断是否未取到值,如果不幸keys内表里有cid未赋值的数据,就Dump(挂掉)
ASSERT <ls_keys> IS NOT ASSIGNED.
READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel_r)
FAILED DATA(lt_travel_failed).
READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M BY \_Booking
ALL FIELDS WITH CORRESPONDING #( lt_travel_r )
RESULT DATA(lt_booking_r). ==> 这里没有加 Failed 的原因是有可能数据只有 Travel,没有Booking,所以不能算错;下面的Booksuppl 也是一样的。
READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Booking_M BY \_BookingSupplement
ALL FIELDS WITH CORRESPONDING #( lt_booking_r )
RESULT DATA(lt_booksuppl_r).
c),赋值取到的关联数据到变量
总体来说就是3重循环,对,就是那个循环套循环,那好像也没办法,不能怎么出力所有数据呢?
* Loop the lt_travel_r, lt_booking_r, lt_booksuppl_r and append data to create table variable
LOOP AT lt_travel_r ASSIGNING FIELD-SYMBOL(<ls_travel_r>).
* APPEND INITIAL LINE TO it_travel ASSIGNING FIELD-SYMBOL(<ls_travel>).
* <ls_travel>-%cid = keys[ KEY entity TravelId = <ls_travel_r>-TravelId ]-%cid.
* <ls_travel>-%data = CORRESPONDING #( <ls_travel_r> EXCEPT TravelId ).
APPEND VALUE #( %cid = keys[ KEY entity TravelId = <ls_travel_r>-TravelId ]-%cid
%data = CORRESPONDING #( <ls_travel_r> EXCEPT TravelId ) )
TO it_travel ASSIGNING FIELD-SYMBOL(<ls_travel>).
==》下面这几行的意思是,copy 过来之后,你也不一定全要一样的是吧,要想改变个别字段值也是可以的。
<ls_travel>-BeginDate = cl_abap_context_info=>get_system_date( ).
<ls_travel>-EndDate = cl_abap_context_info=>get_system_date( ) + 30.
<ls_travel>-OverallStatus = 'O'.
==》这行就是给 cid_ref 赋值,看着好像就只append 一行的样子,
但是其实它的结构是 %target 成员本身是一个内表,里面装的就是 Booking_M 的数据
APPEND VALUE #( %cid_ref = <ls_travel>-%cid )
TO it_booking_cba ASSIGNING FIELD-SYMBOL(<it_booking>).
LOOP AT lt_booking_r ASSIGNING FIELD-SYMBOL(<ls_booking_r>)
USING KEY entity
WHERE TravelId = <ls_travel_r>-TravelId.
APPEND VALUE #( %cid = <ls_travel>-%cid && <ls_booking_r>-BookingId
%data = CORRESPONDING #( <ls_booking_r> EXCEPT TravelId ) )
TO <it_booking>-%target ASSIGNING FIELD-SYMBOL(<ls_booking_n>).
<ls_booking_n>-BookingStatus = 'N'.
APPEND VALUE #( %cid_ref = <ls_booking_n>-%cid )
TO it_booksuppl_cba ASSIGNING FIELD-SYMBOL(<ls_booksuppl>).
LOOP AT lt_booksuppl_r ASSIGNING FIELD-SYMBOL(<ls_booksuppl_r>)
USING KEY entity
WHERE TravelId = <ls_travel_r>-TravelId
AND BookingId = <ls_booking_r>-BookingId.
APPEND VALUE #( %cid = <ls_travel>-%cid && <ls_booking_r>-BookingId && <ls_booksuppl_r>-BookingSupplementId
%data = CORRESPONDING #( <ls_booksuppl_r> EXCEPT TravelId BookingId ) )
TO <ls_booksuppl>-%target.
ENDLOOP.
ENDLOOP.
ENDLOOP.
d),Modify 更新到DB
* Modify Data to DB
MODIFY ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M
CREATE FIELDS ( AgencyId CustomerId BeginDate EndDate BookingFee TotalPrice CurrencyCode OverallStatus Description )
WITH it_travel
ENTITY Z04_DV_Travel_M
CREATE BY \_Booking
FIELDS ( BookingId BookingDate CustomerId CarrierId ConnectionId FlightDate FlightPrice CurrencyCode BookingStatus )
WITH it_booking_cba
ENTITY Z04_DV_Booking_M
CREATE BY \_BookingSupplement
FIELDS ( BookingSupplementId SupplementId Price CurrencyCode )
WITH it_booksuppl_cba
MAPPED DATA(it_mapped).
e),设定 mapped 变量
点 copyTravel 函数,然后按 F2,能看出来 Changing 部分,就是输出变量
传出去之后,前端就可以接收到。
其实除了mapped变量,还有failed, reported 变量,咱们这里就割爱了,可以根据需要设定。

f),完整的 copyTravel 代码
METHOD copyTravel.
* Define create table variable
DATA: it_travel TYPE TABLE FOR CREATE Z04_DV_Travel_M,
it_booking_cba TYPE TABLE FOR CREATE Z04_DV_Travel_M\_Booking,
it_booksuppl_cba TYPE TABLE FOR CREATE Z04_DV_Booking_M\_BookingSupplement.
* Read original keys which need to copy
READ TABLE keys ASSIGNING FIELD-SYMBOL(<ls_keys>) WITH KEY %cid = ' '.
ASSERT <ls_keys> IS NOT ASSIGNED.
READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel_r)
FAILED DATA(lt_travel_failed).
READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M BY \_Booking
ALL FIELDS WITH CORRESPONDING #( lt_travel_r )
RESULT DATA(lt_booking_r).
READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Booking_M BY \_BookingSupplement
ALL FIELDS WITH CORRESPONDING #( lt_booking_r )
RESULT DATA(lt_booksuppl_r).
* Loop the lt_travel_r, lt_booking_r, lt_booksuppl_r and append data to create table variable
LOOP AT lt_travel_r ASSIGNING FIELD-SYMBOL(<ls_travel_r>).
* APPEND INITIAL LINE TO it_travel ASSIGNING FIELD-SYMBOL(<ls_travel>).
* <ls_travel>-%cid = keys[ KEY entity TravelId = <ls_travel_r>-TravelId ]-%cid.
* <ls_travel>-%data = CORRESPONDING #( <ls_travel_r> EXCEPT TravelId ).
APPEND VALUE #( %cid = keys[ KEY entity TravelId = <ls_travel_r>-TravelId ]-%cid
%data = CORRESPONDING #( <ls_travel_r> EXCEPT TravelId ) )
TO it_travel ASSIGNING FIELD-SYMBOL(<ls_travel>).
<ls_travel>-BeginDate = cl_abap_context_info=>get_system_date( ).
<ls_travel>-EndDate = cl_abap_context_info=>get_system_date( ) + 30.
<ls_travel>-OverallStatus = 'O'.
APPEND VALUE #( %cid_ref = <ls_travel>-%cid )
TO it_booking_cba ASSIGNING FIELD-SYMBOL(<it_booking>).
LOOP AT lt_booking_r ASSIGNING FIELD-SYMBOL(<ls_booking_r>)
USING KEY entity
WHERE TravelId = <ls_travel_r>-TravelId.
APPEND VALUE #( %cid = <ls_travel>-%cid && <ls_booking_r>-BookingId
%data = CORRESPONDING #( <ls_booking_r> EXCEPT TravelId ) )
TO <it_booking>-%target ASSIGNING FIELD-SYMBOL(<ls_booking_n>).
<ls_booking_n>-BookingStatus = 'N'.
APPEND VALUE #( %cid_ref = <ls_booking_n>-%cid )
TO it_booksuppl_cba ASSIGNING FIELD-SYMBOL(<ls_booksuppl>).
LOOP AT lt_booksuppl_r ASSIGNING FIELD-SYMBOL(<ls_booksuppl_r>)
USING KEY entity
WHERE TravelId = <ls_travel_r>-TravelId
AND BookingId = <ls_booking_r>-BookingId.
APPEND VALUE #( %cid = <ls_travel>-%cid && <ls_booking_r>-BookingId && <ls_booksuppl_r>-BookingSupplementId
%data = CORRESPONDING #( <ls_booksuppl_r> EXCEPT TravelId BookingId ) )
TO <ls_booksuppl>-%target.
ENDLOOP.
ENDLOOP.
ENDLOOP.
* Modify Data to DB
MODIFY ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M
CREATE FIELDS ( AgencyId CustomerId BeginDate EndDate BookingFee TotalPrice CurrencyCode OverallStatus Description )
WITH it_travel
ENTITY Z04_DV_Travel_M
CREATE BY \_Booking
FIELDS ( BookingId BookingDate CustomerId CarrierId ConnectionId FlightDate FlightPrice CurrencyCode BookingStatus )
WITH it_booking_cba
ENTITY Z04_DV_Booking_M
CREATE BY \_BookingSupplement
FIELDS ( BookingSupplementId SupplementId Price CurrencyCode )
WITH it_booksuppl_cba
MAPPED DATA(it_mapped).
mapped-z04_dv_travel_m = it_mapped-z04_dv_travel_m.
ENDMETHOD.
后面都弄好了,但是要想给前端用,还需要在Behavior Projection里面公开。
g),use action copyTravel - Z04_PV_Travel_M
use action copyTravel;

后台都搞好了,画面端也要给加上,显示出来,客户才可以使用。
h),画面表示 - Metadata
做法是很简单,在Travel_M Metadata文件中的 任意 lineItem 里面添加下面这行代码
{ type:#FOR_ACTION, dataAction: 'copyTravel', label: 'Copy Travel' }
注意这里是要在 List Report 页面上显示 Copy Travel,所以要在 lineItem上加
如果要在 Object Page上显示该按钮,就要在 identification 上加。

以上代码准备好了,现在在 debug 模式下看一看内表里都有什么数据吧。
4-2,debug - Copy Travel 按钮
a),确认一下拷贝元数据
Travel ID:2

Booking_M:2条

Booksuppl_M:Booking 的1条,没有Booking Supplement;Booking 的另一条 有1条 Book Suppl

回到 List Report,点一下Copy Travel 按钮试试,别忘了打断点啊
b),选中拷贝元,点Copy Travel 按钮

崩了😓,去掉断点,然后重新加,再试一次


就可以了哈~😓
1),keys
好像没啥问题, cid 也有,Travel ID也有

Row %CID %CID_REF TRAVELID
=============================================
1 id-1754805330140-145 00000002
2),Read Entity
跟上面a 确认拷贝元数据 里面得到的结果相同
Travel_M

Row TRAVELID AGENCYID CUSTOMERID BEGINDATE ENDDATE BOOKINGFEE TOTALPRICE CURRENCYCODE DESCRIPTION OVERALLSTATUS CREATEDBY CREATEDAT LASTCHANGEDBY LASTCHANGEDAT
============================================================================================================================================================================================================================
1 00000002 070007 000608 20250517 20250518 20.00 900.00 USD Business Trip for Christine, Pierre X Meier 20250430205413.0000000 CB9980000888 20250805092951.4655290
Booking_M

Row TRAVELID BOOKINGID BOOKINGDATE CUSTOMERID CARRIERID CONNECTIONID FLIGHTDATE FLIGHTPRICE CURRENCYCODE BOOKINGSTATUS LASTCHANGEDAT
========================================================================================================================================================
1 00000002 0001 20250515 000099 UA 1537 20250517 438.00 USD N 20250502210758.0000000
2 00000002 0002 20250515 000660 UA 1537 20250517 438.00 USD N 20250502210758.0000000
Booksuppl_M

Row TRAVELID BOOKINGID BOOKINGSUPPLEMENTID SUPPLEMENTID PRICE CURRENCYCODE LASTCHANGEDAT
========================================================================================================
1 00000002 0002 01 ML-0002 4.00 EUR 20250502210758.0000000
3),变数 - it_travel
- cid:使用拷贝元的cid
- TravelId:为空,因为这里还用到了early numbering,稍后会自动採番

Row %CID TRAVELID AGENCYID CUSTOMERID BEGINDATE ENDDATE BOOKINGFEE TOTALPRICE CURRENCYCODE DESCRIPTION OVERALLSTATUS CREATEDBY CREATEDAT LASTCHANGEDBY LASTCHANGEDAT %CONTROL
=================================================================================================================================================================================================================================================================================
1 id-1754805330140-145 00000000 070007 000608 20250810 20250909 20.00 900.00 USD Business Trip for Christine, Pierre O Meier 20250430205413.0000000 CB9980000888 20250805092951.4655290 Structure: flat, not charlike
4),变数 - it_booking

Row %CID_REF TRAVELID %TARGET
==============================================================
1 id-1754805330140-145 00000000 Standard Table[0x13(136)]
5),变数 - it_booking_cba
中间出了点儿错,再试一遍cid 好像有点儿不一样了
Row %CID_REF TRAVELID %TARGET
==============================================================
1 id-1754808184397-144 00000000 Standard Table[2x13(136)]

双击target,显示内表
Row %CID TRAVELID BOOKINGID BOOKINGDATE CUSTOMERID CARRIERID CONNECTIONID FLIGHTDATE FLIGHTPRICE CURRENCYCODE BOOKINGSTATUS LASTCHANGEDAT %CONTROL
=================================================================================================================================================================================================================
1 id-1754808184397-1440001 00000000 0001 20250515 000099 UA 1537 20250517 438.00 USD N 20250502210758.0000000 Structure: flat, not charlike
2 id-1754808184397-1440002 00000000 0002 20250515 000660 UA 1537 20250517 438.00 USD N 20250502210758.0000000 Structure: flat, not charlike

6),变数 - it_booksuppl_cba
Row %CID_REF TRAVELID BOOKINGID %TARGET
===========================================================================
1 id-1754808184397-1440001 00000000 0000 Standard Table[0x9(96)]
2 id-1754808184397-1440002 00000000 0000 Standard Table[1x9(96)]

分别双击 target 内表
Row %CID TRAVELID BOOKINGID BOOKINGSUPPLEMENTID SUPPLEMENTID PRICE CURRENCYCODE LASTCHANGEDAT %CONTROL
===================================================================================================================================================================
1 id-1754808184397-144000201 00000000 0000 01 ML-0002 4.00 EUR 20250502210758.0000000 Structure: flat, not charlike

出了个错误
通信エラー<?xml version="1.0" encoding="utf-8"?><errorxmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"><code>RAISE_SHORTDUMP</code><message>実行時エラー: 'RAISE_SHORTDUMP'。OData 要求処理が異常終了しました。Eclipse 向け ABAP 開発ツールのフィードリーダを使用するか、トランザクション /IWFND/ERROR_LOG、/IWBEP/ERROR_LOG、または ST22 を使用して、実行時エラーを分析してください。OData サーブすのアプリケーションコンポーネントで、SAP 提供のサービスで発生したエラーについてサポートチケットを作成してください。</message><timestamp>20250810065047</timestamp></error>

看了半天,说 key重复
也不知道是什么原因,DB里面的数据,现在有点儿乱,我把採番表最大採番号之后的那些数据给删了,然后再试就OK了。
就比如最大是4271,下一个是4272,那4272 已经存在了的话,你执行的可不就出重复key错误?
7),执行结果
执行完成之后,大概效果如下:




本章讲了RAP actions,主要是实现了 Copy 功能。步骤就是
- 后台拿到 keys,然后循环根据每个key,去拷贝关联表(Travel_M,Bookng_M,Booksuppl_M)
- 其中还用到 early numbering来进行採番
- 注意点就是 cid,cid_ref,因为是新规,尚未产生Key ID,所以必须用它们来做表关联
以上就是本篇的全部内容。
如果大家觉得还行,希望大家多点赞,收藏,转发,让更多热爱技术的人看到,感谢!
更多SAP顾问业务知识请点击下面目录链接或东京老树根的博客主页
409

被折叠的 条评论
为什么被折叠?



