Skip to Content
Technical Articles
Author's profile photo Masaaki Arai

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

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!

Assigned Tags

      18 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Michelle Crapo
      Michelle Crapo

      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)

      Author's profile photo Masaaki Arai
      Masaaki Arai
      Blog Post Author

      Hi,

      Sorry, what is your point?

      Masa

      Author's profile photo Michelle Crapo
      Michelle Crapo

      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.

      Author's profile photo Masaaki Arai
      Masaaki Arai
      Blog Post Author

      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

       

      Author's profile photo Syambabu Allu
      Syambabu Allu

      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

      Author's profile photo Masaaki Arai
      Masaaki Arai
      Blog Post Author

      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

      Author's profile photo Syambabu Allu
      Syambabu Allu

      Thank you

      Author's profile photo Jack Chow
      Jack Chow

      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

       

       

      Author's profile photo Masaaki Arai
      Masaaki Arai
      Blog Post Author

      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

      Author's profile photo Enric Castella Gonzalez
      Enric Castella Gonzalez

      Congratulations for your post! Great!

      Author's profile photo Jyoti Bhagat
      Jyoti Bhagat

      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.

      Author's profile photo Masaaki Arai
      Masaaki Arai
      Blog Post Author

      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

       

      Author's profile photo Sreekanth Surampally
      Sreekanth Surampally

      Hi Masa, I got the couple of questions in this blog.

      1. @ObjectModel.representativeKey: <key>  it is going to be most important key of the key columns of the cds view, can I have 2 or more columns defined in this annotation? 
      2. you mentioned a point about some transactional cds views are defined as analytics.datacategory:#DIMENSION, like Billingitem cds, similarly I also notice salesorder and salesorderitem are also having #dimension data category, Why is it so? one of the reasons I found, when I am using a cds as a foreign key association target, then that cds must have #DIMENSION category, is that reason for these exceptions?
      Author's profile photo B. Struijk
      B. Struijk

      Very nice and helpful bolg.
      Thank you so much for sharing.
      Helps me a lot as I am coming from BW.

      Author's profile photo SAFAL AGRAWAL
      SAFAL AGRAWAL

      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

      Author's profile photo Masaaki Arai
      Masaaki Arai
      Blog Post Author

      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

      Author's profile photo SAFAL AGRAWAL
      SAFAL AGRAWAL

      Thanks Masa for sharing your view.

      Author's profile photo shu yin
      shu yin

      助かりました、ありがとうございました