上一章讲了RAP action 指 Copy Travel。
SAP学习笔记 - 开发56 - RAP开发 Managed App RAP action: Processor/ Approver,Copy Travel 功能实现-CSDN博客
本章继续将RAP action的相关内容。
目录
[2,New Projection View](#2,New Projection View)
[2-3,设定Project View之间的父子关系](#2-3,设定Project View之间的父子关系)
[2-4,设定Metadata - Z04_PV_Travel_M_Approver](#2-4,设定Metadata - Z04_PV_Travel_M_Approver)
[2-5,设定Metadata - Z04_PV_Booking_M_Approver](#2-5,设定Metadata - Z04_PV_Booking_M_Approver)
[3,New Behavior Definition - Z04_PV_Travel_M_Approver](#3,New Behavior Definition - Z04_PV_Travel_M_Approver)
[4,New Service Definition](#4,New Service Definition)
[5,New Service Binding](#5,New Service Binding)
[7,Accept Travel 功能实现](#7,Accept Travel 功能实现)
[8,Reject Travel 功能实现](#8,Reject Travel 功能实现)
[9,测试 Accept Travel 和 Reject Travel 功能](#9,测试 Accept Travel 和 Reject Travel 功能)
[9-1,Accept Travel](#9-1,Accept Travel)
[9-2,Reject Travel](#9-2,Reject Travel)
[9-3,Accept Travel - Object Page](#9-3,Accept Travel - Object Page)
[9-4,Reject Travel - Object Page](#9-4,Reject Travel - Object Page)
下面是详细内容。
1,需求说明
本章要做 Accept Travel,Reject Travel 两个更新操作。
设定的背景是该用户不能看到Create,Delete功能,只能做Accept,Reject,以及部分编集。
另外,该用户不需要看到 Booking Supplement,只需要看到 Travel,Booking两个表的数据。
为了实现这个功能,咱们需要新做一个 Projection View,并在其基础上,做 Behavior,Service。

下面来实操,看系统上怎么实现。
2,New Projection View
2-1,Z04_PV_Travel_M_Approver
右键 Data View,点 New Data Definition

输入Name,Description,点Next

选中ProjectionView模板,点Finish

这样就做好了,注意Currency Code,以及provider contract 设定

同样,把Booking 的Projection View也做了
2-2,Z04_PV_Booking_M_Approver

2-3,设定Project View之间的父子关系

2-4,设定Metadata - Z04_PV_Travel_M_Approver
推荐还是要使用单独的Metadata文件,这里就偷懒给放到 Projection View里面
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Data Definition for Z04_PV_Travel_M_Approver - Projection V'
@Metadata.ignorePropagatedAnnotations: true
@UI.headerInfo: {
typeName: 'Travel',
typeNamePlural: 'Travels',
title: {
type: #STANDARD,
label: 'Travel',
value: 'TravelId'
}
}
define root view entity Z04_PV_Travel_M_Approver
provider contract transactional_query
as projection on Z04_DV_Travel_M
{
@UI.facet: [
{
id: 'TravelDetail',
purpose: #STANDARD,
parentId: '',
position: 10,
label: 'Travel Detail',
type: #IDENTIFICATION_REFERENCE
},
{
id: 'Booking',
purpose: #STANDARD,
parentId: '',
position: 20,
label: 'Bookings',
targetElement: '_Booking',
type: #LINEITEM_REFERENCE
}
]
@UI:{ lineItem:[{ position: 10 }],
identification: [{ position: 10 }]
}
@Search.defaultSearchElement: true
key TravelId,
@UI:{ lineItem:[{ position: 20, importance: #HIGH }],
selectionField: [{ position: 20 }],
identification: [{ position: 20 }]
}
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Agency',
element: 'AgencyID'
},
label: 'Agency'
}]
@ObjectModel.text.element: [ 'AgencyName' ]
AgencyId,
_Agency.Name as AgencyName,
@UI:{ lineItem:[{ position: 30 }],
selectionField: [{ position: 30 }],
identification: [{ position: 30 }]
}
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{
entity: {
name: '/DMO/I_Customer',
element: 'CustomerID'
},
label: 'Customer'
}]
@ObjectModel.text.element: [ 'CustomerName' ]
CustomerId,
_Customer.LastName as CustomerName,
@UI:{ lineItem:[{ position: 40 }],
identification: [{ position: 40 }]
}
BeginDate,
@UI:{ lineItem:[{ position: 50 }],
identification: [{ position: 50 }]
}
EndDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
@UI:{ lineItem:[{ position: 55 }],
identification: [{ position: 55 }]
}
BookingFee,
@Semantics.amount.currencyCode: 'CurrencyCode'
@UI:{ lineItem:[{ position: 60 }],
identification: [{ position: 60 }]
}
TotalPrice,
@Consumption.valueHelpDefinition: [{
entity: {
name: 'I_Currency',
element: 'Currency'
},
label: 'Currency'
}]
CurrencyCode,
@UI:{ lineItem:[{ position: 60 }],
identification: [{ position: 65 }]
}
Description,
@UI:{ lineItem:[{ position: 70 },
{ type:#FOR_ACTION, dataAction: 'acceptTravel', label: 'Accept Travel' },
{ type:#FOR_ACTION, dataAction: 'rejectTravel', label: 'Reject Travel' }],
selectionField: [{ position: 70 }],
identification: [{ position: 70 },
{ type:#FOR_ACTION, dataAction: 'acceptTravel', label: 'Accept Travel' },
{ type:#FOR_ACTION, dataAction: 'rejectTravel', label: 'Reject Travel' }],
textArrangement: #TEXT_ONLY
}
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{
entity: {
name: '/DMO/I_Overall_Status_VH',
element: 'OverallStatus'
},
label: 'Overall Status'
}]
@ObjectModel.text.element: [ 'OverallStatusText' ]
OverallStatus,
@UI.hidden: true
_Status._Text.Text as OverallStatusText : localized,
@UI.hidden: true
CreatedBy,
@UI.hidden: true
CreatedAt,
@UI.hidden: true
LastChangedBy,
@UI.hidden: true
LastChangedAt,
/* Associations */
_Agency,
_Booking : redirected to composition child Z04_PV_Booking_M_Approver,
_Currency,
_Customer,
_Status
}

2-5,设定Metadata - Z04_PV_Booking_M_Approver
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Data Definition for Z04_PV_Booking_M_Approver - Projection V'
@Metadata.ignorePropagatedAnnotations: true
@UI.headerInfo: {
typeName: 'Booking',
typeNamePlural: 'Bookings',
title: {
type: #STANDARD,
label: 'Booking',
value: 'BookingId'
}
}
@Search.searchable: true
define view entity Z04_PV_Booking_M_Approver
as projection on Z04_DV_Booking_M
{
@UI.facet: [
{
id: 'BookingDetail',
purpose: #STANDARD,
parentId: '',
position: 10,
label: 'Booking Detail',
type: #IDENTIFICATION_REFERENCE
}]
@Search.defaultSearchElement: true
key TravelId,
@UI:{ lineItem:[{ position: 20 }],
identification: [{ position: 20 }]
}
@Search.defaultSearchElement: true
key BookingId,
@UI:{ lineItem:[{ position: 30 }],
identification: [{ position: 30 }]
}
BookingDate,
@UI:{ lineItem:[{ position: 40 }],
identification: [{ position: 40 }]
}
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Customer',
element: 'CustomerID'
} }]
@ObjectModel.text.element: [ 'CustomerName' ]
CustomerId,
_Customer.LastName as CustomerName,
@UI:{ lineItem:[{ position: 50 }],
identification: [{ position: 50 }]
}
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Carrier',
element: 'AirlineID'
} }]
@ObjectModel.text.element: [ 'CarrierName' ]
CarrierId,
_Carrier.Name as CarrierName,
@UI:{ lineItem:[{ position: 60 }],
identification: [{ position: 60 }]
}
ConnectionId,
@UI:{ lineItem:[{ position: 70 }],
identification: [{ position: 70 }]
}
FlightDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
@UI:{ lineItem:[{ position: 80 }],
identification: [{ position: 80 }]
}
FlightPrice,
CurrencyCode,
@UI:{ lineItem:[{ position: 90 }],
identification: [{ position: 90 }],
textArrangement: #TEXT_ONLY
}
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Booking_Status_VH',
element: 'BookingStatus'
} }]
@ObjectModel.text.element: [ 'BookingStatusText' ]
BookingStatus,
@UI.hidden: true
_Booking_Status._Text.Text as BookingStatusText : localized,
@UI.hidden: true
LastChangedAt,
/* Associations */
_BookingSupplement,
_Booking_Status,
_Carrier,
_Connection,
_Customer,
_Travel : redirected to parent Z04_PV_Travel_M_Approver
}

3,New Behavior Definition - Z04_PV_Travel_M_Approver
右键 Projection View,然后点 New Behavior Definition

输入Name,Description,点Next

然后点Finish,就建好了

但是注意我们这里不需要Create,Delete,所以都去掉
Booking方面,也不需要Update,Delete,也去掉,就变成下面这个样子的
projection;
strict ( 2 );
define behavior for Z04_PV_Travel_M_Approver //alias <alias_name>
use etag
{
// use create;
use update;
// use delete;
use action acceptTravel;
use action rejectTravel;
// use action copyTravel;
// use association _Booking { create; }
}
//
//define behavior for Z04_PV_Booking_M_Approver //alias <alias_name>
//{
// use update;
// use delete;
//
// use association _Travel;
//}

4,New Service Definition
右键 Projection View,点 New Service Definition

输入Name,Description,点Next

再点Next,然后点Finish,这样就完成了
将 Booking 也暴露,然后点 Alt+F3,激活

5,New Service Binding
右键 Service Definition,然后点 New Service Binding

输入Name,Description,Binding Type,然后点Next

再点Finish 就建好了
按 Ctrl+F3激活,然后点Publish,发布

6,测试一下Layout
Layout基本上没啥问题。
List Report

Object Page

按钮位置好像不对,这个也可以通过Position设定

刷新一下,就OK了

万事具备,就剩下实现具体功能了。
7,Accept Travel 功能实现
METHOD acceptTravel.
MODIFY ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M
UPDATE FIELDS ( OverallStatus )
WITH VALUE #( FOR ls_keys IN keys ( %tky = ls_keys-%tky
OverallStatus = 'A' ) ).
READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_result).
result = VALUE #( FOR ls_result IN lt_result ( %tky = ls_result-%tky
%param = ls_result ) ).
ENDMETHOD.

下面来看一下代码解释。
这段代码是一个RAP (Restful ABAP Programming) action的实现,用于将旅行记录的状态更新为"已接受"(A)。以下是代码的详细解释:
a),修改实体数据
abap
MODIFY ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M
UPDATE FIELDS ( OverallStatus )
WITH VALUE #( FOR ls_keys IN keys ( %tky = ls_keys-%tky
OverallStatus = 'A' ) ).
这部分代码执行以下操作:
-
MODIFY ENTITIES
- 修改实体数据 -
OF Z04_DV_Travel_M
- 操作的是名为Z04_DV_Travel_M
的CDS视图实体 -
IN LOCAL MODE
- 在本地模式下运行(不触发到数据库的直接修改) -
更新
Z04_DV_Travel_M
实体的OverallStatus
字段 -
使用
VALUE #(...)
构造器为每个传入的key设置新值:-
%tky
- 技术key(包含业务对象的key字段) -
OverallStatus = 'A'
- 将状态设置为'A'(表示"已接受")
-
b),读取更新后的数据
abap
READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE
ENTITY Z04_DV_Travel_M
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_result).
这部分代码读取刚刚更新后的数据:
-
从同一个实体
Z04_DV_Travel_M
中读取数据 -
读取所有字段(
ALL FIELDS
) -
使用传入的
keys
作为筛选条件 -
将结果存储在内部表
lt_result
中
c),构建返回结果
abap
result = VALUE #( FOR ls_result IN lt_result ( %tky = ls_result-%tky
%param = ls_result ) ).
这部分构建方法的返回结果:
-
使用
VALUE #(...)
构造器为每个结果记录创建条目 -
每个条目包含:
-
%tky
- 技术key -
%param
- 完整的实体数据(作为参数返回)
-
d),总结
这个acceptTravel
action的主要功能是:
-
接收一组旅行记录的key
-
将这些记录的
OverallStatus
字段更新为'A'(表示接受) -
读取并返回更新后的完整记录数据
这是RAP中处理业务对象状态变更的标准模式,遵循ABAP RESTful Application Programming Model的规范。
Reject Travel 和 Accept Travel 功能基本相同,唯一不同的就是设定的值不同。
8,Reject Travel 功能实现
-
OverallStatus = 'X'
METHOD rejectTravel. MODIFY ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE ENTITY Z04_DV_Travel_M UPDATE FIELDS ( OverallStatus ) WITH VALUE #( FOR ls_keys IN keys ( %tky = ls_keys-%tky OverallStatus = 'X' ) ). READ ENTITIES OF Z04_DV_Travel_M IN LOCAL MODE ENTITY Z04_DV_Travel_M ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result). result = VALUE #( FOR ls_result IN lt_result ( %tky = ls_result-%tky %param = ls_result ) ). ENDMETHOD.
OverallStatus 的值有 3个,O:Open/ A:Accepted/ X:Rejected

9,测试 Accept Travel 和 Reject Travel 功能
9-1,Accept Travel
选中一条,然后点 Accept Travel 按钮

状态已经变为了 A:Accepted(受入)

9-2,Reject Travel
选中一条,然后点 Reject Travel 按钮

状态已经变为了 X:Rejected(拒否)

看一下数据库,Accept Travel/ Reject Travel 都已经写到数据库里面去了。

9-3,Accept Travel - Object Page
点 Accept Travel

修改成功

9-4,Reject Travel - Object Page
点 Reject Travel

修改成功

数据也更新了

Accept Travel,Reject Travel 本身功能就是更新一个字段,所以代码也比较少。
但是因为功能所限,所以要单独开一个App,重新做一遍 Projection View,Service,Binding。
以上就是本篇的全部内容。
如果大家觉得还行,希望大家多点赞,收藏,转发,感谢!
更多SAP顾问业务知识请点击下面目录链接或东京老树根的博客主页