Enterprise Resource Planning Blogs by SAP
Get insights and updates about cloud ERP and RISE with SAP, SAP S/4HANA and SAP S/4HANA Cloud, and more enterprise management capabilities with SAP blog posts.
cancel
Showing results for 
Search instead for 
Did you mean: 
Masaaki
Advisor
Advisor
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



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.








  • 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!
18 Comments