Technical Articles
Predefined Virtual Data Model as example for understanding difference of CDS View type
Updated:
01/12/2018: Added CDS View including BOPF object and CDS View including Virtual Elements.
12/12/2018: Master View for value help in CDS View with UI annotation is added, and the CDS View with UI annotation in which value help master view is set cannot be set as Analytic Query.
Purpose of this blog
The first purpose of the blog is to provide an overview of CDS View and explain the differences between the CDS View types. There are many types of CDS Views, and characteristics and the definition are quite different. Some are positioned as data layer (transaction/master) and others are positioned as application objects and consumed by applications. I personally think Consumption View (CDS View used by application) cannot be called “View” (combination of tables/views) but should be rather “Query”.
There are Master CDS Views, Transaction CDS Views, CDS Views published as OData Services, CDS Views with UI annotation and Analytic Query etc. In addition, CDS Views for data extraction and CDS API Views are available.
The second purpose of this blog is to provide example source code for each of the CDS View types from the Predefined Virtual Data Model (VDM).
In addition, this blog also includes CDS View including table function, CDS View inclluding BOPF Object and CDS View including Virtual Elements for understanding the detail of the functions.
To understand the source code, I think the best way is to refer to the example source code, and it is also the case for ABAP CDS View. Other than Standard DEMO Examples of ABAP CDS views, you can see Predefined VDMs as examples to understand CDS Views. Unlike examples from DEMO CDS Views, you see CDS Views based on the real business entities from Predefined VDMs. Simple examples for study may help, but sometimes the requirement in real world is more complex, so the samples from a real case would be more helpful.
Please see the blog if you want to know the VDM used in a Fiori app.
My motivation to write this blog
CDS View is one of the key of S/4HANA architecture principles. Pushdown (execute within the HANA DB Engine) is included automatically when CDS Views are used, and CDS Views are the foundation of Fiori applications. However, CDS Views may not be so easy to understand. One reason is many of the published documents focus only on particular types of CDS View, e.g. Many documents about Fiori Development, focus only on CDS View with UI annotation, and Analytic Query, details about Interface Views are not included. I feel a holistic view of CDS View is what is expected. I also belive general SQL knowledge is not enough, where knowledge applying to application specific settings are also required, including SAP specific characteristics, e.g. association.
I’m writing this blog to help address the current situation, of missing a holistic view of CDS View, with all required insights available in one place.
Blog Contents
-
CDS View types overview
-
How to open CDS View in ADT (ABAP Development Tools)
-
Detail of CDS View types
Due to the inclusion of source code within the section “Detail of Predefined VDM Types”, this blog will appear somewhat lengthy. However this example code is only a copy from an S/4HANA system, for ease of reading, you may prefer to skip this code and refer the source code within your own S/4HANA system.
CDS View types overview
CDS View types:
-
- Interface View: This view is the foundation for Consumption Views (Tech naming pattern: I_*). Master View and Transaction View are both of the type “interface”.
- Transaction CDS View and Master CDS View are not official terms, but my own terms. I differentiate them as the purpose, structure and settings are different. I dear to differentiate Transaction CDS View and Master CDS View because I believe it helps understanding better. Profit Center is obviously master data, and it is clear that Purchase Order is transaction data from semantic point of view, and technically characteristics of master view and transaction view are quite different in practice. In some cases, whether it is Transaction or Master is unclear, e.g. Billing document is obviously transactional entity but I_BillingDocumentItem might not be called so, as it does not have measures. But it is only exceptional case, and what is transaction and what is master is obvious in most cases.
- Consumption View: The view consumed by applications (Tech naming pattern: C_*). Various Consumptrion View types are available depending on the application consuming the view, these include, CDS View published as OData Service, CDS View with UI Annotation, Analytic Query and CDS API View.
- They are not official categories other than CDS API View, but I differentiate them as the application to use them and settings are different.
- Interface View: This view is the foundation for Consumption Views (Tech naming pattern: I_*). Master View and Transaction View are both of the type “interface”.
- Other important Predefined CDS View: This blog includes other important views worth highlighting.
- CDS View as ODP Datasource can be used as a source to send the data to BW or other external system. Most of them are Interface View.
- Table Function is also important to realize complex requirements.
- For read/write application, CDS View including BOPF object is used.
- For including ABAP logic in CDS View, Virtual Elements can be used (only for OData Service).
- Private View is a view used as a part for Interface View and Consumption View, not supposed to be reused. There are 2 types in Interface View, Basic View and Composite View. Basic View is the view for an entity in S/4HANA and Consumption View consists of Basic View. This blog does not focus on those views as Private View and difference between Basic View and Composite View is not so important.
- See the SAP Note 2540696 and 2657021 for the content rules.
CDS View types and examples in this blog
1. Interface View
1.1. Master CDS View:I_Currency/I_ProfitCenter
f
1.2. Transaction CDS View: I_BillingDocumentItemCube
2. Consumption View:
2.1. CDS View published as OData Service: C_APFlexibleAging
2.2. CDS View with UI Annotation: C_SuplrEvalRspEvaluateST
2.3. Analytic Query: C_Trialbalanceq0001
2.4. CDS API View: A_BankDetail
3. Other important Predefined CDS Views
3.1. CDS View as ODP Datasource: I_BillingDocumentItem
3.2. CDS View including table function: P_SalesDocumentByMultObjSts
3.3. CDS View including BOPF object: I_SalesPlanTP
3.4. CDS View including Virtual Elements: C_Product
3.5. CDS View calling ABAP Class-Method: I_FinancialStatementCube
From the term “View”, you would imagine it is the combination of tables/views. However, in reality a Consumption View is rather a query or frontend object.
For those who may have some BW skills, we can compare some CDS Views to BW objects as follows:
Master CDS View is like an InfoObject (Charactersitic)
Transaction CDS View is like an InfoProvider (Transaction Basic View is like Infocube or ADSO
Transaction Composite View is like a Multiprovider or Composite Provider)
Consumption View is like a BW Query.
How to open CDS View in ADT
You can open the definition of the Predefined VDMs used in this blog with ADT as follows:
In ADT, Click “Open ABAP Development Object”.
Search with the DDL Source name (“I_PROFITCENTER” in the following case) and select the listed CDS View.
The definition of the CDS View is displayed.
Detail of CDS View types
1. Interface View
1.1 Master CDS View
- In general, the source of a Master CDS View is an attribute table of the master data, text and hierarchy views, where the Master CDS View for each the attributes are associated.
- In general, Analytic Data category is set as DIMENSION using the annotation “@Analytics.dataCategory”, with the representative key set using the annotation “@ObjectModel.representativeKey”.
A. Simple example: Currency code (I_Currency)
- CDS View for Currency code (I_Currency) uses table TCURC and TCURX as a source which includes attribute data.
- For Text data, I_CurrencyText is associated to the attribute and set as foreign key association using the annotation “@ObjectModel.text.association”.
- Data Category is set as #DIMENSION.
I_Currency
@EndUserText.label: 'Currency'
@ObjectModel.representativeKey: 'Currency'
@Analytics: {dataCategory: #DIMENSION, dataExtraction.enabled: true}
@VDM.viewType: #BASIC
@AbapCatalog.sqlViewName: 'IFICURRENCY'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Search.searchable: true
define view I_Currency
as select from tcurc
left outer join tcurx on tcurc.waers = tcurx.currkey
association [0..*] to I_CurrencyText as _Text
on $projection.Currency = _Text.Currency
{
@Semantics.currencyCode: true
@ObjectModel.text.association: '_Text'
key tcurc.waers as Currency,
_Text,
cast(COALESCE(tcurx.currdec, 2) as currdec_cds) as Decimals,
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.8
cast(tcurc.isocd as isocd_cds) as CurrencyISOCode,
cast(tcurc.altwr as altwr_cds) as AlternativeCurrencyKey,
tcurc.xprimary as IsPrimaryCurrencyForISOCrcy
};
I_CurrencyText
- Source is the text table of currency: TCURT
- It has language key to which I_Language is associated.
- Text field CurrencyName is set as text field using annotation “@Semantics.text”.
- Object data category is set to #TEXT
@EndUserText.label: 'Currency Text'
@ObjectModel.dataCategory: #TEXT
@ObjectModel.representativeKey: 'Currency'
@Analytics.dataExtraction.enabled: true
@VDM.viewType: #BASIC
@AbapCatalog.sqlViewName: 'IFICURRENCYTEXT'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Search.searchable: true
define view I_CurrencyText
as select from tcurt
association[1..1] to I_Currency as _Currency
on $projection.Currency = _Currency.Currency
association[0..1] to I_Language as _Language
on $projection.Language = _Language.Language
{
@Semantics.language: true
@ObjectModel.foreignKey.association: '_Language'
key spras as Language,
_Language,
@Semantics.currencyCode: true
@ObjectModel.foreignKey.association: '_Currency'
key waers as Currency,
_Currency,
@Semantics.text: true
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.8
cast(ltext as ltext_cds preserving type) as CurrencyName,
@Semantics.text: true
ktext as CurrencyShortName
};
B. Complex example: Profit Center (I_ProfitCenter)
I_ProfitCenter
- CDS View for Profit Center (I_ProfitCenter) uses table CEPC as a source which includes attribute data
- For Text data, I_ProfitCenterText is associated to the attribute and set as foreign key association.
- For hierarchy data, I_ProfitCenterHierarchyNode is associated and set as hierarchy association using annotation “@ObjectModel.Hierarchy.association”.
- For attributes, master view for them are associated to provide text and attributes of the attributes.
@EndUserText.label: 'Profit Center'
@VDM.viewType: #BASIC
@AbapCatalog.sqlViewName: 'IFIPROFITCENTER'
@AbapCatalog.preserveKey:true
@AccessControl.authorizationCheck: #CHECK
@AccessControl.privilegedAssociations: [ '_ProfitCenterHierarchyNode'
//--[ GENERATED:012:29JlHNUf7jY4ipE4XHfNBG
,'_ControllingAreaText','_SegmentText'
// ]--GENERATED
]
@Metadata.allowExtensions: true
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.representativeKey: 'ProfitCenter'
@ClientHandling.algorithm: #SESSION_VARIABLE
@ObjectModel.supportedCapabilities: [ #ANALYTICAL_DIMENSION, #CDS_MODELING_ASSOCIATION_TARGET, #SQL_DATA_SOURCE, #CDS_MODELING_DATA_SOURCE ]
@ObjectModel.modelingPattern: [ #ANALYTICAL_DIMENSION ]
@ObjectModel.usageType: {
dataClass: #MASTER,
serviceQuality: #A,
sizeCategory: #M
}
@Analytics:{
dataCategory: #DIMENSION,
dataExtraction: {
enabled: true,
delta.changeDataCapture: {
automatic: true
}
}
}
@Search.searchable: true
@Consumption.filter.businessDate.at: true
define view I_ProfitCenter
as select distinct from cepc
//--[ GENERATED:012:29JlHNUf7jY4ipE4XHfNBG
association [1..1] to I_ControllingArea as _ControllingAreaText on $projection.ControllingArea = _ControllingAreaText.ControllingArea
association [0..*] to I_SegmentText as _SegmentText on $projection.Segment = _SegmentText.Segment
// ]--GENERATED
association [1..1] to I_ControllingArea as _ControllingArea on $projection.ControllingArea = _ControllingArea.ControllingArea
association [0..*] to I_ProfitCenterText as _Text on $projection.ControllingArea = _Text.ControllingArea
and $projection.ProfitCenter = _Text.ProfitCenter
and $projection.ValidityEndDate = _Text.ValidityEndDate
association [0..*] to I_ProfitCenterHierarchyNode as _ProfitCenterHierarchyNode on $projection.ControllingArea = _ProfitCenterHierarchyNode.ControllingArea
and $projection.ProfitCenter = _ProfitCenterHierarchyNode.ProfitCenter
association [0..1] to I_Country as _Country on $projection.Country = _Country.Country
association [0..1] to I_CompanyCode as _Company on $projection.CompanyCode = _Company.CompanyCode
association [0..1] to I_Segment as _Segment on $projection.Segment = _Segment.Segment
association [0..1] to I_Region as _Region on $projection.Country = _Region.Country
and $projection.Region = _Region.Region
association [0..*] to I_PrftCtrCompanyCodeAssignment as _PrftCtrCompanyCodeAssignment on $projection.ControllingArea = _PrftCtrCompanyCodeAssignment.ControllingArea
and $projection.ProfitCenter = _PrftCtrCompanyCodeAssignment.ProfitCenter
//association[0..*] to I_TaxJurisdiction as _TaxJurisdiction on $projection.TaxJurisdiction = _TaxJurisdiction.TaxJurisdiction
association [0..1] to I_Language as _Language on $projection.Language = _Language.Language
association [1..1] to E_ProfitCenter as _Extension on $projection.ControllingArea = _Extension.ControllingArea
and $projection.ProfitCenter = _Extension.ProfitCenter
and $projection.ValidityEndDate = _Extension.ValidityEndDate
{
//--[ GENERATED:012:29JlHNUf7jY4ipE4XHfNBG
@Consumption.valueHelpDefinition: [
{ entity: { name: 'I_ControllingArea',
element: 'ControllingArea' }
}]
@ObjectModel.text.association: '_ControllingAreaText'
// ]--GENERATED
@ObjectModel.foreignKey.association: '_ControllingArea'
key kokrs as ControllingArea,
@ObjectModel.text.association: '_Text'
@ObjectModel.hierarchy.association: '_ProfitCenterHierarchyNode'
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
key prctr as ProfitCenter,
@Semantics.businessDate.to: true
key datbi as ValidityEndDate,
verak as ProfitCtrResponsiblePersonName,
bukrs as CompanyCode,
verak_user as ProfitCtrResponsibleUser,
@Semantics.businessDate.from: true
datab as ValidityStartDate,
abtei as Department,
khinr as ProfitCenterStandardHierarchy,
//--[ GENERATED:012:29JlHNUf7jY4ipE4XHfNBG
@Consumption.valueHelpDefinition: [
{ entity: { name: 'I_SegmentStdVH',
element: 'Segment' }
}]
@ObjectModel.text.association: '_SegmentText'
// ]--GENERATED
@ObjectModel.foreignKey.association: '_Segment' //Inserted by VDM CDS Suite Plugin
segment as Segment,
lock_ind as ProfitCenterIsBlocked,
pca_template as FormulaPlanningTemplate,
anred as FormOfAddress,
name1 as AddressName,
name2 as AdditionalName,
name3 as ProfitCenterAddrName3,
name4 as ProfitCenterAddrName4,
stras as StreetAddressName,
pfach as POBox,
ort01 as CityName,
pstlz as PostalCode,
pstl2 as POBoxPostalCode,
ort02 as District,
@ObjectModel.foreignKey.association: '_Country' //Inserted by VDM CDS Suite Plugin
land1 as Country,
@ObjectModel.foreignKey.association: '_Region'
regio as Region,
txjcd as TaxJurisdiction,
@Semantics.language:true
@ObjectModel.foreignKey.association: '_Language' //Inserted by VDM CDS Suite Plugin
spras as Language,
telf1 as PhoneNumber1,
telf2 as PhoneNumber2,
telbx as TeleboxNumber,
telx1 as TelexNumber,
telfx as FaxNumber,
teltx as TeletexNumber,
datlt as DataCommunicationPhoneNumber,
drnam as ProfitCenterPrinterName,
usnam as ProfitCenterCreatedByUser,
ersda as ProfitCenterCreationDate,
_Text,
_Country,
_ControllingArea,
_ProfitCenterHierarchyNode,
_Language,
_Company,
_Segment,
_Region,
_PrftCtrCompanyCodeAssignment,
//--[ GENERATED:012:29JlHNUf7jY4ipE4XHfNBG
@Consumption.hidden: true
_ControllingAreaText,
@Consumption.hidden: true
_SegmentText
// ]--GENERATED
//_TaxJurisdiction
}
C. Value help
Other than the Master View in which analytical category is set to DIMENSION, there is Master View for Value help used in CDS View with UI annotation. As is used in the Value help for the transaction CDS View C_SUPLREVALRSPEVALUATEST, which is the source of Fiori app “Evaluate Suppliers “F1650”, Searchc annotation is used to activate fuzzy search in the value help of the selection field in the List Report.
Example: I_PurchasingCategoryValueHelp
@AbapCatalog.sqlViewName: 'IPUCVH'
@EndUserText.label: 'Purchasing Category'
@AbapCatalog.preserveKey:true
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@ClientHandling.algorithm: #SESSION_VARIABLE
@VDM.viewType: #COMPOSITE
@VDM.lifecycle.contract.type: #PUBLIC_LOCAL_API
@Search.searchable: true
@ObjectModel.representativeKey: 'PurchasingCategory'
@ObjectModel.usageType.serviceQuality: #C
@ObjectModel.usageType.sizeCategory: #M
@ObjectModel.usageType.dataClass: #MIXED
@ObjectModel.dataCategory: #VALUE_HELP
@ObjectModel.supportedCapabilities: [#VALUE_HELP_PROVIDER]
@Metadata.ignorePropagatedAnnotations: true
define view I_PurchasingCategoryValueHelp
as select distinct from I_PurchasingCategory
{
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.7
@ObjectModel.text.element: 'PurgCatName'
key cast(I_PurchasingCategory.PurchasingCategory as /srmsmc/generic_id preserving type) as PurchasingCategory,
@Search.defaultSearchElement: true
@Semantics.text: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.7
cast(I_PurchasingCategory.PurgCatName as /srmsmc/medium_name preserving type) as PurgCatName,
@Search.defaultSearchElement: true
@Semantics.text: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.7
cast(I_PurchasingCategory._PurchasingCategoryDesc[1: Language = $session.system_language].PurgCatDescription as /srmsmc/description_char60 preserving type) as PurgCatDescription
}
1.2. Transaction CDS View
Transaction CDS View is the foundation for the CDS View for applications. In the view, Master CDS Views are associated to attributes and the fields for measure are set as measure using annotation “@DefaultAggregation”. To be the foundation for the analytic query, it must be set as DIMENSION or CUBE in the annotation of Analytic Data category.
In Predefined VDM, in most Transaction Views, several Transaction View are created to create the one for the foundation of Consumption View. In the following case, I_BIllingDocumentItemCube is the source of Analytic Query C_SalesVolumeAnalysisQuery. I_BIllingDocumentItemCube is not created directly from VBRP but several Transaction CDS Views are created between the source transaction table and the Transaction View for Consumption View. But it is not mandatory to create several “nested” Transaction CDS Views.
Sample: I_BillingDocumentItemCube
- Analytic Data Category is set to CUBE (or DIMENSION for some other Transaction CDS View) In this case, representative Key doesn’t have to be set. Then Analytic Query can be created on top of this view.
- Master Data CDS Views are joined using association, and it is set as foreign key association with @ObjectModel.foreignKey.association, e.g. I_Product is associated and renamed to _Product, which is exposed (_Product is added in the selection field) and set to foreign key association to the field Product.
- Fields for measure are set to be measure by using @DefaultAggregation and unit is associated.
@ClientHandling.algorithm: #SESSION_VARIABLE
@EndUserText.label: 'Sales Volume - Cube'
@VDM.viewType: #COMPOSITE
@AccessControl: {
authorizationCheck: #CHECK,
personalData.blocking: #('TRANSACTIONAL_DATA'),
privilegedAssociations: ['_CreatedByUser']
}
@AbapCatalog: {
sqlViewName: 'ISDBILLGDOCITMC',
compiler.compareFilter: true,
preserveKey: true
}
@ObjectModel: {
usageType: {
dataClass: #MIXED,
serviceQuality: #D,
sizeCategory: #XL
}
}
@Analytics.dataCategory: #CUBE
@ObjectModel.supportedCapabilities:
[ #ANALYTICAL_PROVIDER, #SQL_DATA_SOURCE, #CDS_MODELING_DATA_SOURCE ]
@ObjectModel.modelingPattern: #ANALYTICAL_CUBE
@Metadata: {
allowExtensions: true,
ignorePropagatedAnnotations:true
}
define view I_BillingDocumentItemCube
with parameters
P_ExchangeRateType : kurst,
P_DisplayCurrency : vdm_v_display_currency
as select from I_BillingDocItemAnalytics as Item
left outer to one join I_PersonWorkAgreement_1 as SalesEmployeeWorkAgreement on Item.SalesEmployee = SalesEmployeeWorkAgreement.PersonWorkAgreement
left outer to one join I_PersonWorkAgreement_1 as RespEmployeeWorkAgreement on Item.ResponsibleEmployee = RespEmployeeWorkAgreement.PersonWorkAgreement
left outer to one join I_CalendarDate as CalendarDate on Item.CreationDate = CalendarDate.CalendarDate
left outer to one join I_CalendarDate as CalendarDateBillingDoc on Item.BillingDocumentDate = CalendarDateBillingDoc.CalendarDate
left outer to one join I_EngagementProjectItem as CustomerProjectItem on Item.SalesDocument = CustomerProjectItem.EngagementProjectItem
and CustomerProjectItem.EngagementProjectItemType = '0SOH'
// Association
association [0..1] to I_EngagementProject as _CustomerProject on $projection.CustomerProject = _CustomerProject.EngagementProject
association [0..1] to I_Customer as _Customer on $projection.Customer = _Customer.Customer
association [0..1] to I_Customer as _ShipToParty on $projection.ShipToParty = _ShipToParty.Customer
association [0..1] to I_Customer as _BillToParty on $projection.BillToParty = _BillToParty.Customer
association [0..1] to I_Employee as _SalesEmployee on $projection.SalesEmployee = _SalesEmployee.PersonnelNumber
association [0..1] to I_Employee as _ResponsibleEmployee on $projection.ResponsibleEmployee = _ResponsibleEmployee.PersonnelNumber
association [0..1] to I_PersonWorkAgreement_1 as _SalesEmployee_2 on $projection.SalesEmployee = _SalesEmployee_2.PersonWorkAgreement
association [0..1] to I_PersonWorkAgreement_1 as _ResponsibleEmployee_2 on $projection.ResponsibleEmployee = _ResponsibleEmployee_2.PersonWorkAgreement
association [0..1] to I_Product as _Product on $projection.Product = _Product.Product
association [0..1] to I_ProductGroup_2 as _ProductGroup on $projection.ProductGroup = _ProductGroup.ProductGroup
// Extension Association
association[0..1] to E_BillingDocumentItem as _Extension on $projection.BillingDocument = _Extension.BillingDocument
and $projection.BillingDocumentItem = _Extension.BillingDocumentItem
{
// Key
@ObjectModel.foreignKey.association: '_BillingDocument'
@Consumption.valueHelpDefinition: [ { entity: { name: 'I_BillingDocumentBasicStdVH', element: 'BillingDocument' } } ]
key BillingDocument,
key BillingDocumentItem,
// Key Association
_BillingDocument,
// Category
@ObjectModel.foreignKey.association: '_SDDocumentCategory'
SDDocumentCategory,
@ObjectModel.foreignKey.association: '_BillingDocumentCategory'
BillingDocumentCategory,
@ObjectModel.foreignKey.association: '_BillingDocumentType'
BillingDocumentType,
@ObjectModel.foreignKey.association: '_SalesDocumentItemCategory'
SalesDocumentItemCategory,
@ObjectModel.foreignKey.association: '_SalesDocumentItemType'
SalesDocumentItemType,
ReturnItemProcessingType,
// Category Association
_SDDocumentCategory,
_BillingDocumentCategory,
_BillingDocumentType,
.....
In the transaction view used as the source of OData based Consumption views, keys have to be set.
2. Consumption View
2.1. CDS View published as OData Service
- The CDS View published as OData Service can be used in Smart Business Apps including KPI tile, Analytical List Report (created with Fiori Report Design Modeler app) and Analysis Path Framework (APF).
- The field to which exposed master view should be associated or to which text field is associated using @ObjectModel.text.element (e.g. C_BankRiskAnalytics) to group Key and text, which can be used in Smart Business apps. But it is not mandatory.
- For measure field, aggregation type have to be defind using @DefaultAggregation.
- Other settings like “parameters” or “where conditions” can be included. In many of the SAP standard CDS Views for KPI Tiles, the parameter P_DateFunction is included with which the date or period can be filtered dynamically, e.g. C_OverduePO. See the blog for further details.
- It is possible to create “calculated measures” with the AnalyticsDetail annotation (e.g. @AnalyticsDetails.query.formula: ‘NETWR – WAVWR ‘) for this view type. To do this , the view needs be set to Analytic Query (see below).
- It is not mandatory to create from Interface view, but it is enough to be published as OData Service. It is technically no problem to include the settings normally in the Interface view, e.g. association, default aggregation, associating unit to measure in this view. Example of this type is C_PurOrdValueWithPlnd. In this case, keys have to be set in the Interface View.
Example: C_APFlexibleAging (Used in Fiori App “Aging Analysis” (F1733))
@EndUserText.label: 'Aging Analysis Smart Business App'
@VDM.viewType: #CONSUMPTION
@Analytics.query: true
@OData.publish: true
@AbapCatalog.sqlViewName: 'CAPFLXBLAGING'
@AccessControl.authorizationCheck: #PRIVILEGED_ONLY
@ClientHandling.algorithm: #SESSION_VARIABLE
@ObjectModel.supportedCapabilities: [ #ANALYTICAL_QUERY ]
@ObjectModel.usageType.serviceQuality: #D
@ObjectModel.usageType.sizeCategory: #XXL
@ObjectModel.usageType.dataClass: #MIXED
@Metadata.ignorePropagatedAnnotations: true
define view C_APFlexibleAging
// Corresponds to calculation view 'sap.hba.r.sfin700.AccountsPayableFlexibleAgingQuery'
with parameters
@EndUserText.label: 'Open on Key Date'
@Consumption.defaultValue: 'TODAY'
@Consumption.valueHelpDefinition: [{
entity: {
name: 'C_GregorianCalSglDateFuncVH',
element: 'DateFunction'
}
}]
P_DateFunction : datefunctionid,
@Consumption.derivation: {
lookupEntity: 'I_SglGregorianCalDateFunction',
resultElement: 'DateFunctionStartDate',
binding: [
{ targetParameter : 'P_DateFunction' ,
type : #PARAMETER, value : 'P_DateFunction' }
]
}
@Consumption.hidden: true
P_KeyDate : sydate,
@Consumption.hidden: true
@Environment.systemField: #SYSTEM_LANGUAGE
P_Language : sylangu,
@Consumption.defaultValue: '30'
P_AgingGridMeasureInDays : farp_aging_grid_measure,
@Consumption.defaultValue: '4'
P_NumberOfAgingGridColumns : farp_number_of_grid_columns,
@Consumption.defaultValue: 'EUR'
P_DisplayCurrency : vdm_v_display_currency,
@Consumption.defaultValue: 'M'
@Consumption.valueHelpDefinition: [{ entity: { name: 'I_ExchangeRateType', element: 'ExchangeRateType' } }]
P_ExchangeRateType : kurst
as select from
I_APFlexibleAging(
P_KeyDate : :P_KeyDate,
P_AgingGridMeasureInDays : :P_AgingGridMeasureInDays,
P_NumberOfAgingGridColumns : :P_NumberOfAgingGridColumns,
P_DisplayCurrency : :P_DisplayCurrency,
P_ExchangeRateType: :P_ExchangeRateType)
{
CompanyCode,
TransactionCurrency,
GLAccount,
Supplier,
AccountingDocumentCategory,
AgingGridText,
SpecialGLCode,
ExchangeRateType,
_CompanyCode.CompanyCodeName as CompanyCodeName ,
_GLAccount._Text[1:Language = $parameters.P_Language].GLAccountLongName as GLAccountLongName,
_Supplier.SupplierName as SupplierName,
_AccountingDocumentCategory._Text[1:Language = $parameters.P_Language].AccountingDocumentCategoryName as AccountingDocumentCategoryName,
_SpecialGLCode._Text[1:Language = :P_Language].SpecialGLCodeLongName as SpecialGLCodeName,
ChartOfAccounts,
_ChartOfAccounts._Text[1:Language = $parameters.P_Language].ChartOfAccountsName as ChartOfAccountsName,
SupplierAccountGroup,
_SupplierAccountGroup._SupplierAccountGroupText[1:Language = $parameters.P_Language].AccountGroupName as SupplierAccountGroupName,
@Semantics.currencyCode:true
DisplayCurrency,
@DefaultAggregation: #SUM
@Semantics.amount.currencyCode: 'DisplayCurrency'
AmountInDisplayCurrency_E,
@DefaultAggregation: #SUM
@Semantics.amount.currencyCode: 'DisplayCurrency'
TotalNotOvrdAmtInDspCrcy,
@DefaultAggregation: #SUM
@Semantics.amount.currencyCode: 'DisplayCurrency'
TotalOverdueAmtInDspCrcy_E
//@DefaultAggregation: #SUM
//NumberOfOpenItems
};
2.2. CDS View with UI Annotation
- CDS View with UI Annotation can be used in Fiori Elements apps including List Report, Analytical List Page(ALP)/Object Page, Overview Page, Worklist/Object Page.
- UI Annotations are included to define the UI setting of the Fiori Elements apps, e.g. OData Publish, @UI.lineItem is used for displaying the field in the initial screen, @UI.selectionField is used to set the field to be included in the selection field area in the initial screen.
- It is not mandatory to include UI Annotation, but Settings for UI is possible using a Local Annotation with Annotation Modeler in Fiori Elements template in Web IDE. Please watch the movie for further details. This means, “CDS View published as OData Service” can also be the source of Fiori Elements apps.
- It is possible to create “calculated measures” with AnalyticsDetail annotation (e.g. @AnalyticsDetails.query.formula: ‘NETWR – WAVWR ‘) for this view type. To enable this, the view needs to be set to Analytic Query (see below), but in this case, associated Master View cannot be used as the value help in List Report. Example in which associated master view is used for value help in List Report is C_SuplrEvalRspEvaluateST, which is the source of Fiori app “Evaluate Suppliers” (F1650).
- Search Annotation can be included to make freestyle search available.
- Other settings like “parameters” or “where conditions” can be included. (But Parameter cannot be used in List Report by default)
- It is not mandatory to create this view type from an Interface view, it is sufficient to be published as OData Service (the same as CDS View published as OData Service).
Example:C_CnsldtnJrnlEntr (Used in Fiori App “Display Group Journal Entries” (F2573)
@AbapCatalog.sqlViewName: 'CCNSLDTNJRNLENTR'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Journal Entry Consumption View'
@VDM.viewType: #CONSUMPTION
@ObjectModel.compositionRoot:true
@ClientHandling.algorithm: #SESSION_VARIABLE
@ObjectModel.usageType: {
sizeCategory: #XXL,
serviceQuality: #D,
dataClass: #MIXED
}
@ObjectModel.semanticKey: ['ConsolidationLedger','ConsolidationDimension','FiscalYear','ConsolidationDocumentNumber','ConsolidationPostingItem','ConsolidationVersion']
@Metadata.allowExtensions: true
@Search.searchable: true
@Consumption.semanticObject: 'ConsolidationJournalList'
@UI.headerInfo: {
typeName: 'Group Journal Entry',
typeNamePlural: 'Group Journal Entries',
title: { value: 'ConsolidationDocumentNumber'},
description: { value: 'FiscalYear' }
}
@AccessControl.personalData.blocking: #NOT_REQUIRED
@UI.presentationVariant.requestAtLeast: [ 'ConsolidationDimension', 'CompanyCode','ConsolidationDocumentNumber','ConsolidationPostingItem','ConsolidationDocumentType','PostingLevel',
'FiscalYear','FiscalPeriod','ReferenceFiscalYear','ConsolidationVersion', 'ConsolidationLedger','CurrencyConversionsDiffType', 'CnsldtnGroupJrnlEntryBundle' ]
@AccessControl.privilegedAssociations: ['_BusinessAreaText','_PartnerBusinessAreaText','_ChartOfAccountsText','_CostCenterText','_PartnerCostCenterText',
'_ControllingAreaText','_CustomerText','_GLAcctInChartOfAccountsText','_LedgerText','_DocumentTypeText',
'_PartnerProfitCenterText','_ProfitCenterText','_PartnerSegmentText','_SegmentText','_SupplierText',
'_CnsldtnUnitText','_InvesteeUnitText','_PartnerUnitText','_CompanyCodeText','_InvestmentActivityTypeText']
define view C_CnsldtnJrnlEntr
as select from I_CnsldtnJrnlEntr as CnsldtnJrnlEntr
association [0..1] to I_CnsldtnBusinessAreaT as _BusinessAreaText on $projection.BusinessArea = _BusinessAreaText.BusinessArea
and _BusinessAreaText.Language = $session.system_language
association [0..1] to I_CnsldtnBusinessAreaT as _PartnerBusinessAreaText on $projection.PartnerBusinessArea = _PartnerBusinessAreaText.BusinessArea
and _PartnerBusinessAreaText.Language = $session.system_language
......
{
@Consumption.valueHelpDefinition: [
{ entity: { name: 'C_CnsldtnLedgerVH',
element: 'ConsolidationLedger' }
}]
@ObjectModel.text.association: '_LedgerText'
@UI.facet: [
{
label: 'General Data',
id: 'General',
type: #COLLECTION,
position: 20
},
{
label: 'Line Items',
id: 'Item',
type: #LINEITEM_REFERENCE,
position: 40,
targetElement: '_Item'
},
{
label: 'Global Parameters',
id: 'GeneralColumn1',
parentId: 'General',
type: #FIELDGROUP_REFERENCE,
position: 10,
targetQualifier: 'GeneralColumn1'
},
{
label: 'Posting Information',
id: 'GeneralColumn2',
parentId: 'General',
type: #FIELDGROUP_REFERENCE,
position: 20,
targetQualifier: 'GeneralColumn2'
},
{
label: 'Organizational Unit',
id: 'GeneralColumn3',
parentId: 'General',
type: #FIELDGROUP_REFERENCE,
position: 30,
targetQualifier: 'GeneralColumn3'
}
]
@UI: {
fieldGroup: [{
qualifier: 'GeneralColumn1',
groupLabel: 'Global Parameters',
position: 10
}],
lineItem: [{
position: 200,
importance: #HIGH
}]
}
key ConsolidationLedger,
.....
2.3 Analytic Query
- Analytic Query is a type of CDS View, which is exposed to the SAP HANA Analytical Engine so it can be used for multidimensional analytics, e.g. slicing, dicing and drilldown. It can be consumed just the same way as BW Queries, e.g. for testing with the BW transaction code RSRT, and for consumption via SAP Analysis for Office. It can be the source of Fiori Multidimensional Reporting app and SAP Analytics Cloud Online scenarios. It can also be used with BusinessObjects solution.
- It is set as an Analytic Query with annotation @Analytics.query: true,
- It needs to use the Interface View in which Analytic Data Category is set to #CUBE or #DIMENSION.
- Association join cannot be set in an Analytic Query view.
- Attributes of associated master view are ignored in Analytic Query views (so the attributes of the associated master view need to be set in the Interface view).
- With AnalyticsDetails Annotations, default multidimensional layout of the query can be specified. The query must be set as Analytic Query (@Analytics.query: true). Restricted and Calculated Measure can be created.
- When creating Calculated Measure with AnalyticsDetail Annotation, the Calculated Measure created in the same view can be used in the formula of another Calculated Measure. (e.g. @AnalyticsDetails.query.formula: ‘NETWR – WAVWR ‘ 1 as CM1, @AnalyticsDetails.query.formula: ‘ $projection.CM1/NETWR ’ 1 as CM12).
- To define a specific behavior that relates to the consumption of CDS content through domain-specific frameworks, Consumption Annotations is used, e.g. to set an attribute as a prompt value, @Consumption.filter is used.
- See Wiki page for more details relating to annotations in Analytic Query view.
- Other settings like “parameters” or “where conditions”: can also be included.
Example: C_Trialbalanceq0001 (Used in Fiori App “Trial Balance” (F0996A))
@AbapCatalog: { sqlViewName: 'CFITRIALBALQ0001',
preserveKey: true,
compiler.compareFilter: true,
buffering.status: #NOT_ALLOWED }
@AccessControl.authorizationCheck: #PRIVILEGED_ONLY
@Analytics.query: true
@Analytics.settings.maxProcessingEffort: #HIGH
@ClientHandling.algorithm: #SESSION_VARIABLE
@EndUserText.label: 'Trial Balance'
@Metadata: { ignorePropagatedAnnotations: true,
allowExtensions: true }
@ObjectModel: { supportedCapabilities: [ #ANALYTICAL_QUERY ],
modelingPattern: #ANALYTICAL_QUERY,
usageType: { sizeCategory: #XXL,
serviceQuality: #D,
dataClass: #MIXED } }
@OData.publish: true
@VDM.viewType: #CONSUMPTION
define view C_Trialbalanceq0001
with parameters
@Consumption.hidden: true
@Environment.systemField: #SYSTEM_LANGUAGE
P_Language : sylangu,
@Consumption.hidden: true
@Environment.systemField: #SYSTEM_DATE
P_KeyDate : sydate,
//@Consumption.derivation: { lookupEntity: 'I_CalendarDate',
// resultElement: 'FirstDayofMonthDate', binding: [
// { targetElement : 'CalendarDate' , type : #PARAMETER, value : 'P_KeyDate' } ]
// }
@Consumption.derivation: { lookupEntity: 'I_CalendarDate',
resultElement: 'FirstDayofMonthDate',
binding: [
{ targetElement : 'CalendarDate' , type : #PARAMETER, value : 'P_ToPostingDate' } ]
}
P_FromPostingDate : fis_budat_from,
//@Consumption.derivation: { lookupEntity: 'I_CalendarDate',
// resultElement: 'CalendarDate', binding: [
// { targetElement : 'CalendarDate' , type : #PARAMETER, value : 'P_KeyDate' } ]
// }
@Consumption.derivation: { lookupEntity: 'I_MySessionContext',
resultElement: 'UserLocalDate', binding: [
{ targetElement : 'UserID' , type : #SYSTEM_FIELD, value : '#USER' } ]
}
P_ToPostingDate : fis_budat_to
as select from I_GLAcctBalanceCube
( P_FromPostingDate: $parameters.P_FromPostingDate, P_ToPostingDate: $parameters.P_ToPostingDate ) as I_GLAcctBalanceCube
{
// Filter/ Fixed Rows
@Consumption.filter: {selectionType: #SINGLE, multipleSelections: false, mandatory: true}
@Consumption.derivation: { lookupEntity: 'I_LedgerStdVH',
resultElement: 'Ledger', binding: [
{ targetElement : 'IsLeadingLedger' , type : #CONSTANT, value : 'X' } ]
}
@AnalyticsDetails.query.variableSequence : 10
//@AnalyticsDetails.query.axis: #FREE
Ledger,
....
2.4. CDS API View
- CDS API View is supposed to be used as API.
Example: A_BankDetail
@AbapCatalog.sqlViewName: 'ABKDET'
@EndUserText.label: 'Bank Details'
@VDM:{
viewType: #BASIC,
lifecycle.contract.type:#PUBLIC_REMOTE_API
}
@AccessControl.authorizationCheck: #CHECK
@ObjectModel: {
usageType: {
serviceQuality: #B,
sizeCategory: #L,
dataClass: #MASTER
}
}
@Metadata.ignorePropagatedAnnotations: true
@ClientHandling.algorithm: #SESSION_VARIABLE
@AbapCatalog.compiler.compareFilter: true
define view A_BankDetail as select from bnka {
key banks as BankCountry,
key bankl as BankInternalID,
banka as BankName,
swift as SWIFTCode,
bgrup as BankGroup,
bnklz as BankNumber,
provz as Region,
stras as StreetName,
ort01 as CityName,
brnch as Branch
// xpgro as IsPostBankAccount,
// loevm as IsMarkedForDeletion,
// pskto as PostOfficeBankAccount,
// chkme as CheckDigitCalculationMethod,
// vers as BankDataFileFormat,
// erdat as CreationDate ,
// ernam as CreatedByUser,
// adrnr as AddressID
}where loevm = '' or loevm = ' ';
3. Other important Predefined CDS Views
3.1. CDS View as ODP Data Source
- The CDS View can be used as a data source to send the data to BW system. It is a kind of ODP Data Source. The CDS View can be set to ODP Data Source with the annotation @Analytics.dataExtraction.enabled: true.
- In the BW system connected to S/4HANA system (including embedded BW in S/4HANA), the CDS View can be replicated as ODP Data Source. See the blog in detail. This data source can also be used by DataServices, SDI etc. in the same way as a BW Data Source.
- Most of the CDS Views as ODP Data Source are Interface View. In those Interface View, the annotation @Analytics.dataExtraction.enabled: true is included.
Example: I_JournalEntry
@VDM.lifecycle.contract.type: #PUBLIC_LOCAL_API
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog: {
sqlViewName: 'IFIJOURNALENT',
preserveKey:true
}
@EndUserText.label: 'Journal Entry'
@VDM.viewType: #BASIC
@AccessControl.authorizationCheck: #CHECK
@ObjectModel.representativeKey: 'AccountingDocument'
@ObjectModel.usageType.sizeCategory: #XXL
@ObjectModel.usageType.dataClass: #TRANSACTIONAL
@ObjectModel.usageType.serviceQuality: #A
@Analytics: {
dataCategory: #DIMENSION,
dataExtraction: {
enabled: true,
delta.changeDataCapture: {
mapping:
[
{
table: 'BKPF',
role: #MAIN,
viewElement: ['CompanyCode', 'FiscalYear', 'AccountingDocument'],
tableElement: ['bukrs', 'gjahr', 'belnr']
}
]
}
}
}
@ClientHandling.algorithm: #SESSION_VARIABLE
@AbapCatalog.buffering.status: #NOT_ALLOWED
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions:true
//--[ GENERATED:012:29JlHNUf7jY4ip7HtmZN9m
@AccessControl.privilegedAssociations: ['_AccountingDocumentTypeText','_BusinessTransactionTypeText','_FinancialManagementAreaText','_LedgerText']
// ]--GENERATED
@ObjectModel.modelingPattern: #ANALYTICAL_DIMENSION
@ObjectModel.supportedCapabilities: [#ANALYTICAL_DIMENSION,
#CDS_MODELING_ASSOCIATION_TARGET,
#CDS_MODELING_DATA_SOURCE,
#EXTRACTION_DATA_SOURCE,
#SQL_DATA_SOURCE]
define view I_JournalEntry as select from P_BKPF_COM
/* *************************************************************
* association zur ID
* *************************************************************
*/
//--[ GENERATED:012:29JlHNUf7jY4ip7HtmZN9m
association [0..*] to I_AccountingDocumentTypeText as _AccountingDocumentTypeText on $projection.AccountingDocumentType = _AccountingDocumentTypeText.AccountingDocumentType
association [0..*] to I_BusTransactionTypeText as _BusinessTransactionTypeText on $projection.BusinessTransactionType = _BusinessTransactionTypeText.BusinessTransactionType
association [0..*] to I_FinancialManagementAreaText as _FinancialManagementAreaText on $projection.FinancialManagementArea = _FinancialManagementAreaText.FinancialManagementArea
association [0..*] to I_LedgerText as _LedgerText on $projection.Ledger = _LedgerText.Ledger
....
3.2. CDS View including Table Function
(This example is to explain the technical characteristics, which is slightly different from the main topic of this blog. But as it is so interesting and think it will help provide further context the Predefined VDM).
Table function can be the source of a CDS View, and Native SQL can be included in it. By using flexibility of Native SQL, a more sophisticated process might be possible. In Table Function, ABAP Managed Database Procedure (AMDP) is included in which Native SQL can be included.
Example: P_SalesDocumentByMultObjSts
@ClientHandling.type: #CLIENT_DEPENDENT
@ClientHandling.algorithm: #SESSION_VARIABLE
@AccessControl.authorizationCheck: #NOT_REQUIRED
@VDM.viewType: #BASIC
@VDM.private: true
define table function P_SalesDocumentByMultObjSts
with parameters
im_clnt : abap.clnt @Environment.systemField: #CLIENT
returns
{
key Client : mandt;
key SalesDocument : vbeln_va;
key SalesDocumentItem : posnr;
// System Status long name concatenated, max. 100 for one Item
SystemStatusName : concat_sys_status_name;
// System Status short name concatenated, max. 100 for one Item
SystemStatusShortName : concat_sys_status_short_name;
// User Status long name concatenated, max. 100 for one Item
UserStatusName : concat_user_status_name;
// User Status short name concatenated, max. 100 for one Item
UserStatusShortName : concat_user_status_short_name;
}
implemented by method
CL_SD_SLSDOCBYOBJSTS=>CONCAT_STATUS_FOR_SLSDOC;
In the Native SQL of the AMDP, “string_agg” is used, which is not available in normal ABAP CDS Views.
CLASS CL_SD_SLSDOCBYOBJSTS DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES IF_AMDP_MARKER_HDB .
CLASS-METHODS CONCAT_STATUS_FOR_SLSDOC
FOR TABLE FUNCTION P_SALESDOCUMENTBYMULTOBJSTS .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS CL_SD_SLSDOCBYOBJSTS IMPLEMENTATION.
METHOD CONCAT_STATUS_FOR_SLSDOC BY DATABASE FUNCTION FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY
USING PSLSDOCBYOBJSTS.
RETURN
SELECT "P1"."MANDT" AS Client,
"P1"."SALESDOCUMENT" AS SalesDocument,
"P1"."SALESDOCUMENTITEM" AS SalesDocumentItem,
string_agg ( concat("P1"."SYSTEMSTATUSNAME", concat(' (', concat("P1"."SYSTEMSTATUSSHORTNAME", '), ') ) ) ) AS SystemStatusName,
string_agg ( concat("P1"."SYSTEMSTATUSSHORTNAME", ', ') ) AS SystemStatusShortName,
string_agg ( concat("P1"."USERSTATUSNAME", concat(' (', concat("P1"."USERSTATUSSHORTNAME", '), ') ) ) ) AS UserStatusName,
string_agg ( concat("P1"."USERSTATUSSHORTNAME", ', ') ) AS UserStatusShortName
FROM "PSLSDOCBYOBJSTS" "P1"
GROUP BY "P1"."MANDT", "P1"."SALESDOCUMENT", "P1"."SALESDOCUMENTITEM" ;
ENDMETHOD.
ENDCLASS.
Table function can be used in other CDS View.
P_SalesDocumentByMultObjSts is used in C_SalesDocumentByObjectStatus.
....
@UI.headerInfo: {
typeName: 'Sales Document',
typeNamePlural: 'Sales Documents',
title: {
type: #STANDARD,
value: 'SalesDocumentItem'
}
}
define view C_SalesDocumentByObjectStatus
as select from P_SalesDocumentByMultObjSts( im_clnt : $session.client )
inner join I_SalesDocument as SalesDocument on P_SalesDocumentByMultObjSts.SalesDocument = SalesDocument.SalesDocument
...
in the AMDP of the Table function, it is possible to call BADI. See the table function P_FML_SDM_PHASE_BADI_AMDP_TF and cl_fml_sdm_badi_amdp_tf=>fetch_sdm_phase in it.
FML_SDM_PHASE_BADI=>READ_SDM_PHASE is called in the AMTP.
3.3. CDS View including BOPF object
CDS View including BOPF Business Object works as the source of the apps to insert/update/delete data as well as to read.
There are many sources about BOPF like the blog.
On the other hand, BOPF is not a part of new framework called ABAP RESTful Programming Model (RAP).
Example: I_SalesPlanTP
(It is the source of C_SalesPlanTP used in OData SD_SALESPLAN, the OData of Fiori app “Manage Sales Plans”)
@EndUserText.label: 'Sales Plan Header Transaction Object'
@VDM.viewType: #TRANSACTIONAL
@VDM.lifecycle.contract.type:#SAP_INTERNAL_API
@AccessControl.authorizationCheck: #NOT_REQUIRED
@AccessControl.personalData.blocking: #NOT_REQUIRED
@AbapCatalog.sqlViewName: 'ISDSLSPLANTP'
@ClientHandling.algorithm: #SESSION_VARIABLE
@ObjectModel.usageType.dataClass: #TRANSACTIONAL
@ObjectModel.usageType.serviceQuality: #C
@ObjectModel.usageType.sizeCategory: #XL
@ObjectModel.transactionalProcessingEnabled: true
@ObjectModel.draftEnabled: true
@ObjectModel.compositionRoot: true
@ObjectModel.writeDraftPersistence: 'SALESPLAN_D'
@ObjectModel.writeActivePersistence: 'SALESPLAN'
@ObjectModel.entityChangeStateId: 'LastChangeDateTime'
@ObjectModel.createEnabled:true
@ObjectModel.updateEnabled: 'EXTERNAL_CALCULATION'
@ObjectModel.deleteEnabled: 'EXTERNAL_CALCULATION'
@ObjectModel.semanticKey: [ 'SalesPlan' ]
define view I_SalesPlanTP
as select distinct from I_SalesPlan as SP
left outer join P_SalesPlanMyTeams as Teams on SP.AssignedTeamID = Teams.RespyMgmtTeamID
association [0..*] to I_SalesPlanItemTP as _SalesPlanItem on $projection.SalesPlanUUID = _SalesPlanItem.SalesPlanUUID
association [0..*] to I_SalesPlanDimnSelectionTP as _SalesPlanDimnSelection on $projection.SalesPlanUUID = _SalesPlanDimnSelection.SalesPlanUUID
{
key SP.SalesPlanUUID,
@ObjectModel: {
mandatory: 'EXTERNAL_CALCULATION',
readOnly: 'EXTERNAL_CALCULATION'
}
SalesPlan,
@ObjectModel.mandatory: true
SalesPlanVersion,
@ObjectModel.readOnly: 'EXTERNAL_CALCULATION'
SalesPlanDescription,
SalesPlanVersionDescription,
@ObjectModel.readOnly: true
SalesPlanStatus,
@Semantics.currencyCode: true
@ObjectModel.mandatory: 'EXTERNAL_CALCULATION'
SalesPlanCurrency,
@ObjectModel.readOnly: 'EXTERNAL_CALCULATION'
SalesPlanPeriodType,
@ObjectModel: {
mandatory: 'EXTERNAL_CALCULATION', //Date type with mandatory true will cause exception
readOnly: 'EXTERNAL_CALCULATION'
}
SalesPlanStartDate as SalesPlanFrom,
@ObjectModel: {
mandatory: 'EXTERNAL_CALCULATION', //Date type with mandatory true will cause exception
readOnly: 'EXTERNAL_CALCULATION'
}
SalesPlanEndDate as SalesPlanTo,
@ObjectModel.readOnly: 'EXTERNAL_CALCULATION'
SalesPlanPurpose,
//Type: Quantity/Amount Planning
SalesPlanType,
//Admin
CreatedByUser,
CreationDateTime,
LastChangedByUser,
LastChangeDateTime,
ReferenceSalesPlanUUID,
@ObjectModel.readOnly: 'EXTERNAL_CALCULATION'
AssignedTeamID,
@ObjectModel.readOnly: 'EXTERNAL_CALCULATION'
AssignedTeamName,
SalesPlanItemUpdatedDesc,
//Association
@ObjectModel.association.type: #TO_COMPOSITION_CHILD
_SalesPlanItem,
@ObjectModel.association.type: #TO_COMPOSITION_CHILD
_SalesPlanDimnSelection,
_SalesPlanCurrency,
_SalesPlanCurrencyText,
_CreatedByUser,
_LastChangedByUser,
_SalesPlanPeriodTypeText,
_SalesPlanPeriodType,
_SalesPlanStatusText,
_SalesPlanStatus,
_SalesPlanPurposeText,
_SalesPlanPurpose,
_SalesPlanType,
_SalesPlanTypeText
}
where
(
//CreatedByUser is Session User, Access Level 1
CreatedByUser = $session.user
)
or
(
// Assigned Team in session user's team list
SP.AssignedTeamID = Teams.RespyMgmtTeamID
)
Object Model annotation is used for the detail setting for the CDS View with BOPF Business Object.
BOPF Business Object can be opend with ADT.
It can be checked with the Transaction BOB in SAPGUI.
3.4. CDS View including Virtual Elements
ABAP Logic can be included in CDS View using Virtual Element. It is defined at the level of CDS consumption views as additional elements within the SELECT list using specific @DataModel annotations. However, the calculation or further processing of their values is carried out by means of ABAP classes that implement the specific code exit interfaces provided for this purpose. This function is available as of NW 7.52.
It works only in the generated OData service, not in, e.g. Analytic Query.
Example: C_Product
(it is used in the OData MD_C_PRODUCT_MAINTAIN_SRV, the source of Fiori app “Manage Product Master”)
...
@VDM.viewType: #CONSUMPTION
@ObjectModel: {
compositionRoot: true,
transactionalProcessingDelegated:true,
semanticKey: ['Product'],
createEnabled:true,
updateEnabled:#('EXTERNAL_CALCULATION'),
deleteEnabled:#('EXTERNAL_CALCULATION'),
draftEnabled: true,
usageType.serviceQuality: #C,
usageType.sizeCategory : #L,
usageType.dataClass: #MIXED,
text.control: #ASSOCIATED_TEXT_UI_HIDDEN
}
@Search.searchable: true
@AccessControl: {
authorizationCheck: #CHECK,
privilegedAssociations: ['_CreatedByUserContactCard', '_LastChangedByUserContactCard']
}
@Metadata.allowExtensions: true
define view C_Product
as select from I_ProductWD as Product
association [0..*] to C_Productdescription as _Description on $projection.Product = _Description.Product
association [0..*] to C_Productplant
...
@Semantics.imageUrl: true
@ObjectModel.readOnly: true
@ObjectModel.virtualElement
@ObjectModel.virtualElementCalculatedBy: 'ABAP:CL_MD_PRODUCT_ROOT_CALC_EXIT1'
cast( '' as productimageurl ) as ProductImageURL,
...
Class: CL_MD_PRODUCT_ROOT_CALC_EXIT1
CLASS cl_md_product_root_calc_exit1 DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_sadl_exit .
INTERFACES if_sadl_exit_calc_element_read .
PROTECTED SECTION.
PRIVATE SECTION.
TYPES: BEGIN OF ty_calc,
productimageurl TYPE c_product-productimageurl,
product TYPE c_product-product,
productforedit TYPE c_product-productforedit,
draftuuid TYPE /bobf/conf_key,
isactiveentity TYPE boolean,
numberofproductvariants TYPE c_product-numberofproductvariants,
numberofprocessedprodvariants TYPE c_product-numberofprocessedprodvariants,
hasactiveentity TYPE boolean,
isvariantconfigurationenabled TYPE c_product-isvariantconfigurationenabled,
END OF ty_calc.
CONSTANTS:
BEGIN OF cs_calc_element,
productimageurl TYPE sadl_entity_element VALUE 'PRODUCTIMAGEURL' ##NO_TEXT,
createdbyusername TYPE sadl_entity_element VALUE 'CREATEDBYUSERNAME' ##NO_TEXT,
lastchangedbyusername TYPE sadl_entity_element VALUE 'LASTCHANGEDBYUSERNAME' ##NO_TEXT,
numoftotalvariants TYPE sadl_entity_element VALUE 'NUMBEROFPRODUCTVARIANTS' ##NO_TEXT,
numofcompletedvariants TYPE sadl_entity_element VALUE 'NUMBEROFPROCESSEDPRODVARIANTS' ##NO_TEXT,
END OF cs_calc_element .
CONSTANTS:
BEGIN OF cs_calc_config_variant_element,
isvariantconfigurationenabled TYPE sadl_entity_element VALUE 'ISVARIANTCONFIGURATIONENABLED' ##NO_TEXT,
END OF cs_calc_config_variant_element .
CONSTANTS:
BEGIN OF cs_cal_element_variant,
variantenabled TYPE sadl_entity_element VALUE 'VARIANTENABLED',
END OF cs_cal_element_variant .
....
CLASS CL_MD_PRODUCT_ROOT_CALC_EXIT1 IMPLEMENTATION.
METHOD get_image_url.
DATA: lv_object_key TYPE objky,
lv_mimetype TYPE w3conttype,
lv_thumbnail_url TYPE saeuri.
FIELD-SYMBOLS: TYPE any,
TYPE simple,
type simple,
TYPE simple,
TYPE simple.
MOVE-CORRESPONDING it_original_data TO ct_calc.
LOOP AT ct_calc ASSIGNING .
ASSIGN ('-PRODUCTIMAGEURL') TO FIELD-SYMBOL().
CHECK sy-subrc = 0.
ASSIGN ('-DRAFTUUID') TO .
CHECK sy-subrc = 0.
ASSIGN ('-PRODUCT') TO .
CHECK sy-subrc = 0.
.....
3.5. CDS View calling ABAP Class-Method
Data in CDS View can be posted with ABAP Class-Method
I_FinancialStatementCube (Source of C_FINSTMNTCOMPARISON)
@AbapCatalog.sqlViewName: 'IFIFINSTMTCUBE'
@EndUserText.label: 'Virtual Financial Statement Cube'
@Analytics: { dataCategory: #CUBE, dataExtraction.enabled: true }
@VDM.viewType: #COMPOSITE
@AccessControl.authorizationCheck: #PRIVILEGED_ONLY
@AccessControl.personalData.blocking:#BLOCKED_DATA_EXCLUDED
@ObjectModel.representativeKey: 'LedgerGLLineItem'
@ObjectModel.usageType.sizeCategory: #XXL
@ObjectModel.usageType.dataClass: #MIXED
@ObjectModel.usageType.serviceQuality: #D
@ClientHandling.algorithm: #SESSION_VARIABLE
@AbapCatalog.buffering.status: #NOT_ALLOWED
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions:true
@Analytics.readClassName: 'CL_FIS_FINS_STMNT_CDS'
define view I_FinancialStatementCube
with parameters
P_FinancialStatementVersion : hryid,
P_FromFiscalPeriod : fis_period_from,
P_ToFiscalPeriod : fis_period_to,
P_FiscalYear : fis_gjahr,
P_FromComparisonFiscalPeriod : fis_period_from_cmp,
P_ToComparisonFiscalPeriod : fis_period_to_cmp,
P_ComparisonFiscalYear : fis_gjahr_cmp,
P_Ledger : fins_ledger,
P_ComparisonLedger : fis_ledger_cmp,
P_CurrencyRole : fis_curtp,
P_PlanningCategory : fcom_category
as select from I_GLAccountLineItem as I_GLAccountLineItem
association [1..1] to E_JournalEntryItem as _Extension on $projection.SourceLedger = _Extension.SourceLedger
and $projection.CompanyCode = _Extension.CompanyCode
and $projection.FiscalYear = _Extension.FiscalYear
and $projection.AccountingDocument = _Extension.AccountingDocument
and $projection.LedgerGLLineItem = _Extension.LedgerGLLineItem
association [0..1] to I_FinancialStatementLeafItem as _FinancialStatementLeafItem on $projection.FinancialStatementLeafItem = _FinancialStatementLeafItem.FinancialStatementLeafItem
association [1..1] to I_Currency as _Currency on $projection.Currency = _Currency.Currency
association [0..1] to I_ChartOfAccounts as _CorpGroupChartOfAccounts on $projection.corporategroupchartofaccounts = _CorpGroupChartOfAccounts.ChartOfAccounts
association [0..1] to I_GLAccountInChartOfAccounts as _CorporateGroupAccount on $projection.corporategroupchartofaccounts = _CorporateGroupAccount.ChartOfAccounts
and $projection.CorporateGroupAccount = _CorporateGroupAccount.GLAccount
association [0..1] to I_AlternativeGLAccountIsUsed as _AlternativeGLAccountIsUsed on $projection.AlternativeGLAccountIsUsed = _AlternativeGLAccountIsUsed.AlternativeGLAccountIsUsed
association [0..1] to I_Indicator as _ZeroBalanceAccountIsDisplayed on $projection.ZeroBalanceAccountIsDisplayed = _ZeroBalanceAccountIsDisplayed.IndicatorValue
{
@ObjectModel.foreignKey.association: '_Ledger'
key Ledger,
_Ledger,
@ObjectModel.foreignKey.association: '_SourceLedger'
.....
@DefaultAggregation: #SUM
@Semantics: { amount : {currencyCode: 'Currency'} }
cast( 1 as fis_repo_amount ) as PeriodBalanceAmount,
..
Personal opinions:
- “CDS View published as OData Service” or “CDS View with UI annotation” does not need to be created from Interface View technically, but I think it is better to do so as much as possible because of the standardization of the modeling. Anyway, application specific view needs to be created, and Interface View cannot be consumed by application directly in general.
- This means, master view associations and foreign key associations should be set in Interface View as much as possible. For Fiori Elements apps, Master View would be needed for value help in its Consumption View, technically, the exposed master view associated in the source Interface View (e.g. _Plant) could be used in the Consumption View created on top of it. But if some specific settings are needed in the value help, e.g. fuzzy search, projection (limit the attribute), another master view for value help would be needed.
- I think Interface View should be created to be supposed to be used for many purposes and should avoid having application specific settings as much as possible like BW Infoprovider and InfoObject are supposed to be reused for many reports.
- So, In my opinion, Calculated measure (and Restricted measure) should be created in Consumption View, not in Interface View, as much as possible. It is because by using annotation “@AnalyticsDetails.query.formula”, the calculated measure created in the same analytic query can be used in the formula. In the standard predefined VDM used in KPI tile or Fiori Elements apps, it is not the case. But I think it is beneficial as we can avoid creating several source CDS Views to add calculated measures. But calculated measures/restricted measures supposed to be reused by many apps, it should be created in Interface View. In addition, if master views for value help have to be used in the consumption view for Fiori Elements, it is not the case and it cannot be set as analytic query.
- In addition, I believe parameters should be set in Consumption View, not in Interface View, as much as possible as in general parameters are application specific. But if the parameter values are not pushed down to the source tables/views, they need to be created in the lower views to use it in the WHERE condition explicitly. But as SELECT statement is optimized internally in HANA, WHERE conditions for normal columns (dynamically generated by the navigations in an application) should be pushed down to lower tables/views in general, so it is not necessary to use parameter instead of normal column filtering.
- WHERE condition should not be included in Interface View as much as possible as Interface View should be supposed to be used also for the queries which need to all data in the source.
But they are only my current personal opinions, and I think there are some other ones who have different opinions but are also good. I would like you to share your opinions.
Training
It is recommended to participate in the following training courses.
S4D430 Building Views in Core Data Services ABAP (CDS ABAP)
S4H41E S/4HANA embedded analytics and Modeling Basics with Core Data Services (CDS Views)
or
S4H410 S/4HANA embedded analytics and Modeling Basics with Core Data Services (CDS Views)
Hope it help understanding overall of the CDS View!
I'm glad this is long as the examples help. I'm using CDS views. However, I believe there are times to use them and a time to just code for your tables. What are your suggestions? When would you create a CDS view? Assuming one can't be found that will work for you. (or extended)
Hi,
Sorry, what is your point?
Masa
I'm wondering when would you create a CDS view instead of just doing it in ABAP? Sometimes, it is an easy choice if the CDS view is already available.
Hello Michelle,
If Predefined VDM meets your customer’s requirement go with them. it it could meet by adding some fields, they can do that.
But it is always the case that customer want to use their own CDS View to meet their own requirement, then Custom CDS View needs to be created. in my experience, most customers want to create their own CDS Views.
For creating them, Key User Tools could be used, but the functionality is limited. Then the remaining way is to use ADT.
In addition, even if standard Fiori app is used, IT team want to know the technologies used in the app. Then it is essential to understand the difference of the CDS View type.
Thanks, Masa
Hi Masa,
Thanks for the Blog on Types CDS views with lot of information.
where we can use the CDS API View,Can you give example?
Thanks,
Syam
Hello Syambabu,
Below is the answer from DEV team.
They are solely used to define OData services for external (cross system) consumption on them, and are kept stable across releases.
They must not be used otherwise.
Thanks, Masa
Thank you
Hi Masa,
This is glad information, wanna to know the query part in RSRT. How to we create the selection parameter prompt like Variable? with default select last 3 month data? Still need to using CMOD coding?
Thanks,
Jack
Hello Jack,
For analytical query, Consumption filter annotation “@Consumption.filter: {selectionType: #SINGLE, multipleSelections: false, mandatory: true}”, or Parameter should be used. See 2.3. Analytic Query in detail.
btw, for Fiori Element app, ui annotation selection field needs to be used.
Regarding the filtering time attributte, I would be happy if my blog could help.
https://blogs.sap.com/2018/09/27/date-function-for-dynamic-date-filtering-in-fiori-apps/
Thanks, Masa
Congratulations for your post! Great!
Hi Masa,
Nice Blog!! Thanks for sharing the information.
I would like to know where can I get information about CDS annotations apart from its documentation. I always get confused when to use which annotation and why. I have gone through all the SAP Help Documentation but that is not enough. Is there any source where I can get examples of the annotations?
Thanks.
Hello Jyoti,
I think SAP help is the best to know detail of each annotation.
https://help.sap.com/viewer/cc0c305d2fab47bd808adcad3ca7ee9d/7.5.9/en-US/630ce9b386b84e80bfade96779fbaeec.html
the following page could help understanding Analytic Query and Analytic Detail annotation.
https://wiki.scn.sap.com/wiki/display/BI/CDS+views%3A+HowTo+use+in+BW+contexts
Thanks, Masa
Hi Masa, I got the couple of questions in this blog.
Very nice and helpful bolg.
Thank you so much for sharing.
Helps me a lot as I am coming from BW.
Hello Masa,
Thanks for sharing wonderful blog.
Can you please check below mention blog and share your suggestions.
How to look-up a Profit Center in filtered Hierarchy in Analytics Query CDS
https://answers.sap.com/questions/13326804/how-to-loop-up-a-profit-center-in-filtered-hierarc.html
Thanks
Safal
Hello SAFAL,
The only idea coming to my mind is to use BW variable exit I_STEP =2. BW Query can be created on top of CDS View and Variable exit can be created here.
https://blogs.sap.com/2018/08/08/bw-query-on-cds-view-odata-from-bw-and-value-of-bw-query-in-s4hana/
https://blogs.sap.com/2013/12/13/all-about-istep-variable/
https://blogs.sap.com/2014/11/11/everything-you-always-wanted-to-know-about-the-processing-of-customer-exit-variables-but/
But i have not tested, so, sorry if it would not work.
Thanks, Masa
Thanks Masa for sharing your view.
助かりました、ありがとうございました