Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
peter_neumayer
Explorer
0 Kudos
In MDG Central Governance many  customers already use the Custom Object framework to apply the governance functionality also to reference data. Thus the question came up how to enable the MDG Data Quality functionality also for custom objects.

The SAP Master Data Quality configuration guide (link) describes the steps required to enable rule management and rule mining for your custom object and also how to configure tiles for the Evaluation Run application. These applications allow you to create rules, run and schedule data quality evaluations, and define data quality dimensions as KPIs for monitoring. You then can visualize the results of the evaluation run however you can view only aggregated results based on the defined scores and KPIs.

The purpose of this document is to describe the steps required to create an application to analyze and drill down into evaluation results for your custom object. As with the ALPs for the product and business partner domains we will use an analytical list page (ALP) template for our application. The template builds on the defined CDS views and the corresponding annotations. Thus, the main work is in the creation of the different CDS views required. Based on the existing database tables for the custom object, we must create the custom-object specific CDS views, and then we must link them to the analytical consumption CDS views for data quality analytics.

Recommendation: before you start, please read the following blog: https://blogs.sap.com/2016/09/20/fioriui5-cds-bopf-for-beginners/

 

Create CDS views for your custom object


In my example we will use the type carrier from the SFLIGHT data model. We are following the MDC configuration guide for custom object and thus the generated database table is ZZCARRIER_ACT and the object type defined is ZZCARR.



Figure 1: Screenshot of the custom object database table ZZCARRIERS_ACT

 



Figure 2: Screenshot of the object type ZZCARR in the MDC data model (TA MDCMODEL)

 

1.     Create a basic CDS view for ZZCARRIER_ACT


Use the ABAP Development Tools to create the following CDS views
@AbapCatalog.sqlViewName: 'ZDDL_C_ZZCARR'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'ZPN_C_ZZCARRIERS_ACT'

@OData.publish: true

@Search.searchable: true
@UI.headerInfo.typeName: 'CarrierID'
@UI.headerInfo.title.value: 'CarrierID'

@ObjectModel.transactionalProcessingDelegated: true
@ObjectModel.semanticKey: ['CARRID']
@ObjectModel.representativeKey: 'CARRID'
@ObjectModel.createEnabled: true
@ObjectModel.updateEnabled: true
@ObjectModel.deleteEnabled: true

define view ZPN_C_ZZCARRIERS_ACT
as select from ZPN_I_ZZCARRIERS_ACT
{

// Key CarrierID
@Search.defaultSearchElement: true
@ObjectModel.readOnly: true
@ObjectModel.text.element: [ 'CarrName' ]
@UI.identification: [{position:10, importance: #HIGH }]
@UI.lineItem: [{position: 10, importance: #HIGH }]
@UI.textArrangement: #TEXT_FIRST
@EndUserText: { label: 'Airline ID', quickInfo: 'Airline ID' }
key ZPN_I_ZZCARRIERS_ACT.CarrID,

// Name
@Semantics.text: true
@UI.identification: [{position:70, importance: #HIGH }]
@UI.lineItem: [{position: 70, importance: #HIGH }]
@EndUserText: { label: 'Airline Name', quickInfo: 'Airline Name' }
ZPN_I_ZZCARRIERS_ACT.CarrName,

// Currency
@UI.identification: [{position:80, importance: #HIGH }]
@UI.lineItem: [{position: 80, importance: #HIGH }]
@EndUserText: { label: 'Airline Currency', quickInfo: 'Airline Currency' }
ZPN_I_ZZCARRIERS_ACT.CurrCode

}

Listing 1: Basic CDS view ZPN_I_ZZCARRIERS_ACT

 

2.     Create a corresponding consumption view


@AbapCatalog.sqlViewName: 'ZDDL_C_ZZCARR'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'ZPN_C_ZZCARRIERS_ACT'

@OData.publish: true

@Search.searchable: true
@UI.headerInfo.typeName: 'CarrierID'
@UI.headerInfo.title.value: 'CarrierID'

@ObjectModel.transactionalProcessingDelegated: true
@ObjectModel.semanticKey: ['CARRID']
@ObjectModel.representativeKey: 'CARRID'
@ObjectModel.createEnabled: true
@ObjectModel.updateEnabled: true
@ObjectModel.deleteEnabled: true

define view ZPN_C_ZZCARRIERS_ACT
as select from ZPN_I_ZZCARRIERS_ACT
{

// Key CarrierID
@Search.defaultSearchElement: true
@ObjectModel.readOnly: true
@ObjectModel.text.element: [ 'CarrName' ]
@UI.identification: [{position:10, importance: #HIGH }]
@UI.lineItem: [{position: 10, importance: #HIGH }]
@UI.textArrangement: #TEXT_FIRST
@EndUserText: { label: 'Airline ID', quickInfo: 'Airline ID' }
key ZPN_I_ZZCARRIERS_ACT.CarrID,

// Name
@Semantics.text: true
@UI.identification: [{position:70, importance: #HIGH }]
@UI.lineItem: [{position: 70, importance: #HIGH }]
@EndUserText: { label: 'Airline Name', quickInfo: 'Airline Name' }
ZPN_I_ZZCARRIERS_ACT.CarrName,

// Currency
@UI.identification: [{position:80, importance: #HIGH }]
@UI.lineItem: [{position: 80, importance: #HIGH }]
@EndUserText: { label: 'Airline Currency', quickInfo: 'Airline Currency' }
ZPN_I_ZZCARRIERS_ACT.CurrCode

}

Listing 2: consumption CDS view ZPN_C_ZZCARRIERS_ACT

 

For this cds view the annotation 'oData.publish : true' is set and the corresponding service needs to be activated via transaction /IFWND/MAINT_SERVICE .

 

3.     Create data quality CDS views


Now we switch to the data quality related structures. As part of the configuration activities the table ZZCARR_DQ_RES was generated, which stores the results of the evaluation. Therefore, we create the following basic CDS view ZPN_I_MDGLTYCustomObject.
@AbapCatalog.sqlViewName: 'ZPNICUSTOBJ'
@AbapCatalog.compiler.compareFilter: true
@ClientHandling.algorithm: #SESSION_VARIABLE
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ObjectModel.usageType: { sizeCategory: #L, dataClass: #MIXED, serviceQuality: #C }
@ObjectModel.representativeKey: 'MasterDataChangeProcess'
@VDM.viewType: #BASIC
@AbapCatalog.preserveKey:true
@Metadata.allowExtensions:true
@EndUserText.label: 'ALP for Custom Object ZZCarrier_ACT'
define view ZPN_I_MDGLTYCustomObject
as select from zzcarr_dq_res
inner join mdc_d_prcroot as _MasterDataChangeProcess on zzcarr_dq_res.process_id = _MasterDataChangeProcess.id

association [0..1] to I_MDQltyBusRuleEvalResult as _MDQltyBusRuleEvalResult on _MDQltyBusRuleEvalResult.MDQltyBusRuleEvalResultCode = $projection.MDQltyBusRuleEvalResultCode
association [0..*] to I_MDQltyBusRuleEvalResultText as _MDQltyBusRuleEvalResultText on _MDQltyBusRuleEvalResultText.MDQltyBusRuleEvalResultCode = $projection.MDQltyBusRuleEvalResultCode
{
// keys
key carrid as Carrier,
key ruleuuid as MDQualityBusinessRuleUUID,
@ObjectModel.foreignKey.association: '_MDQltyBusRuleEvalResult'
@ObjectModel.text.association: '_MDQltyBusRuleEvalResultText'
key resulttype as MDQltyBusRuleEvalResultCode,
key zzcarr_dq_res.process_id as MasterDataChangeProcess,

// attributes
result_count as MDQltyNmbrOfBusRuleEvalResults,
cast (dats_tims_to_tstmp( _MasterDataChangeProcess.finish_date,
_MasterDataChangeProcess.finish_time,
abap_system_timezone( $session.client,'NULL' ),
$session.client,
'NULL' ) as timestampl ) as MDChgProcessFinishDateTime,
basetable as MDQltyBusinessRuleBaseTable,


// associations
@Consumption.filter.hidden: true
_MDQltyBusRuleEvalResult,
@Consumption.filter.hidden: true
_MDQltyBusRuleEvalResultText
}

Listing 3: Basic CDS view ZPN_I_MDGLTYCustomObject

 

And the corresponding consumption CDS view with the definition for the keys:
@AbapCatalog.sqlViewName: 'ZPNCCUSTOBJ'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ClientHandling.algorithm: #SESSION_VARIABLE
@ObjectModel.usageType: { sizeCategory: #XL, dataClass: #MIXED, serviceQuality: #D }
@ObjectModel.representativeKey: 'MasterDataChangeProcess'
@VDM.viewType: #CONSUMPTION
@EndUserText.label: 'ALP for Custom Object ZZCarrier_ACT'

@ObjectModel.text.control: #('ASSOCIATED_TEXT_UI_HIDDEN')
@OData.publish: true
@Metadata: {
allowExtensions: true,
ignorePropagatedAnnotations: true
}

define view ZPN_C_MDGLTYCustomObject
as select from ZPN_I_MDGLTYCustomObject
association [0..1] to C_MDQualityBusinessRuleTP as _MDQualityBusinessRuleTP on $projection.MDQualityBusinessRuleUUID = _MDQualityBusinessRuleTP.MDQualityBusinessRuleUUID
association [0..1] to C_MDQualityEvaluations as _MDQualityEvaluations on $projection.MasterDataChangeProcess = _MDQualityEvaluations.MasterDataChangeProcess
association [0..1] to ZPN_C_ZZCARRIERS_ACT as _Carrier on $projection.Carrier = _Carrier.CarrID
association [0..1] to C_MDQltyBusinessRuleVH as _MDQltyBusinessRuleVH on $projection.MDQualityBusinessRule = _MDQltyBusinessRuleVH.MDQualityBusinessRule
and $projection.MDQltyBusinessObjectTypeCode = _MDQltyBusinessRuleVH.MDQltyBusinessObjectTypeCode
association [0..1] to C_MDQltyBusinessRuleBaseTblVH as _MDQltyBusinessRuleBaseTblVH on $projection.MDQltyBusinessRuleBaseTable = _MDQltyBusinessRuleBaseTblVH.MDQltyBusinessRuleBaseTable
and $projection.MDQltyBusinessObjectTypeCode = _MDQltyBusinessRuleBaseTblVH.MDQltyBusinessObjectTypeCode
association [0..*] to I_MDQltyBusinessRuleBaseTableT as _MDQltyBusinessRuleBaseTableT on $projection.MDQltyBusinessRuleBaseTable = _MDQltyBusinessRuleBaseTableT.MDQltyBusinessRuleBaseTable

Listing 4: Consumption CDS view ZPN_C_MDGLTYCustomObject (part1)

 

With the following definition for the keys
{
// keys
@Consumption.derivation: {
lookupEntity: 'C_MDQualityEvaluations',
resultElement: 'MasterDataChangeProcess',
binding:
[
{ targetElement: 'MDChgProcessIsLatest', type: #CONSTANT, value: 'X' },
{ targetElement: 'MDQltyBusinessObjectTypeCode', type: #CONSTANT, value: 'ZZCARR' }
]
}
@Consumption.filter: {selectionType: #SINGLE, multipleSelections: false }
@Consumption.valueHelpDefinition: [
{
entity: {
name: 'C_MDQualityEvaluations',
element: 'MasterDataChangeProcess'
},
additionalBinding: [{
localElement: 'MDQltyBusinessObjectTypeCode',
element: 'MDQltyBusinessObjectTypeCode',
usage: #FILTER
}]
}
]
@ObjectModel.foreignKey.association: '_MDQualityEvaluations'
key MasterDataChangeProcess,

@ObjectModel.foreignKey.association: '_Carrier'
@Consumption.valueHelpDefinition: [
{ association: '_Carrier' }
]
@ObjectModel.text.association: '_Carrier'
key Carrier,

key MDQualityBusinessRuleUUID,

@Consumption.valueHelpDefinition: [
{ association: '_MDQltyBusRuleEvalResult' },
{
qualifier: 'FilterEvalsByRes' ,
presentationVariantQualifier: 'FilterEvalsByRes' ,
entity: { name: 'ZPN_C_MDGLTYCustomObject', element: 'MDQltyBusRuleEvalResultCode' } ,
additionalBinding: [
{ usage: #FILTER_AND_RESULT, element: 'MasterDataChangeProcess', localElement: 'MasterDataChangeProcess' }
]
}
]
@Consumption.filter.defaultValue: 'F'
@ObjectModel.foreignKey.association: '_MDQltyBusRuleEvalResult'
@ObjectModel.text.association: '_MDQltyBusRuleEvalResultText'
key MDQltyBusRuleEvalResultCode,

Listing 5: Consumption CDS view ZPN_C_MDGLTYCustomObject (part2)

 

And the following data definitions:
     // data
@Consumption.filter: { hidden: true, multipleSelections: false, selectionType: #SINGLE, defaultValue: 'ZZCARR'}
_MDQualityBusinessRuleTP.MDQltyBusinessObjectTypeCode as MDQltyBusinessObjectTypeCode,

@DefaultAggregation: #SUM
MDQltyNmbrOfBusRuleEvalResults,

@Consumption.valueHelpDefinition: [
{ association: '_MDQltyBusinessRuleVH' }
]
@ObjectModel.foreignKey.association: '_MDQltyBusinessRuleVH'
@ObjectModel.text.element: [ 'MDQualityBusinessRuleName' ]
_MDQualityBusinessRuleTP.MDQualityBusinessRule as MDQualityBusinessRule,

_MDQualityBusinessRuleTP.MDQualityBusinessRuleName as MDQualityBusinessRuleName,

@Consumption.valueHelpDefinition: [
{ association: '_MDQltyBusinessRuleBaseTblVH' }
]
@ObjectModel.foreignKey.association: '_MDQltyBusinessRuleBaseTblVH'
@ObjectModel.text.association: '_MDQltyBusinessRuleBaseTableT'
MDQltyBusinessRuleBaseTable,

@Consumption.valueHelpDefinition: [
{
qualifier: 'FilterEvalsByCurrency' ,
presentationVariantQualifier: 'FilterEvalsByCurrency' ,
entity: {
name: 'ZPN_C_MDGLTYCustomObject',
element: 'CurrencyCode'
}
}
]
_Carrier.CurrCode as CurrCode,

// associations
@Consumption.filter.hidden
_Carrier,
@Consumption.filter.hidden
_MDQltyBusRuleEvalResult,
@Consumption.filter.hidden
_MDQltyBusRuleEvalResultText,
@Consumption.filter.hidden
_MDQltyBusinessRuleVH,
@Consumption.filter.hidden
_MDQltyBusinessRuleBaseTblVH,
@Consumption.filter.hidden
_MDQltyBusinessRuleBaseTableT,
@Consumption.filter.hidden
_MDQualityEvaluations
}

Listing 6: Consumption CDS view ZPN_C_MDGLTYCustomObject (part 3)

 

4.     Create Annotation file


Next, we create an Annotation file, which describes the UI specific aspects.
@Metadata.layer: #CUSTOMER

@UI.headerInfo:{
typeName: 'Evaluation Item',
typeNamePlural: 'Evaluation Items',
description.value: '_MDQualityEvaluations.MDChgProcessDescription',
title.value: 'MasterDataChangeProcess'
}

@UI.chart: [
{
qualifier: 'TotalNumEvalsByResult',
title: 'Results by Outcome',
chartType: #DONUT,
dimensions: [ 'MDQltyBusRuleEvalResultCode' ] ,
measures: [ 'MDQltyNmbrOfBusRuleEvalResults' ] ,
dimensionAttributes: [
{ dimension: 'MDQltyBusRuleEvalResultCode', role: #CATEGORY }
],
measureAttributes: [
{ measure: 'MDQltyNmbrOfBusRuleEvalResults', role: #AXIS_1, asDataPoint: true }
]
},
{
qualifier: 'FilterEvalsByCurrency',
title: 'Results By Carrier Currency',
chartType: #COLUMN,
dimensions: [ 'CurrCode' ] ,
measures: [ 'MDQltyNmbrOfBusRuleEvalResults' ] ,
dimensionAttributes: [
{ dimension: 'CurrCode',role: #CATEGORY }
],
measureAttributes: [
{ measure: 'MDQltyNmbrOfBusRuleEvalResults', role: #AXIS_1, asDataPoint: true }
]
}
]

@UI.presentationVariant: [
{
text: 'Visual Filter: Results by Outcome',
qualifier: 'FilterEvalsByRes',
sortOrder: [{ by: 'MDQltyNmbrOfBusRuleEvalResults', direction: #DESC }],
visualizations: [
{ type: #AS_CHART, qualifier: 'TotalNumEvalsByResult' }
]
},
{
text: 'Visual Filter: Results by Currency',
qualifier: 'FilterEvalsByCurrency',
sortOrder: [{ by: 'MDQltyNmbrOfBusRuleEvalResults', direction: #DESC }],
visualizations: [
{ type: #AS_CHART, qualifier: 'FilterEvalsByCurrency' }
]
},
{
qualifier: 'DefaultVariantALP' ,
groupBy: [ 'Carrier' ],
sortOrder: [{ by: 'Carrier', direction: #ASC }],
initialExpansionLevel: 1,
visualizations: [
{ type: #AS_LINEITEM, qualifier: 'DefaultVariantALP' }
],
requestAtLeast: ['MasterDataChangeProcess']
},
{
qualifier: 'TotalNumEvalsByResultBase',
sortOrder: [{ by: 'MDQltyNmbrOfBusRuleEvalResults', direction: #DESC }],
visualizations: [
{ type: #AS_CHART, qualifier: 'TotalNumEvalsByResultBase' }
]
}
]
annotate view ZPN_C_MDGLTYCustomObject with
{
@UI.selectionField: [{ position: 40 }]
@EndUserText: { label: 'Evaluation', quickInfo: 'Evaluation' }
MasterDataChangeProcess;

@Consumption.semanticObject: 'Carrier'
@UI.identification: [{ position: 10 }]
@UI.lineItem: [
{ qualifier: 'DefaultVariantALP', position: 10, label: 'Airline' }
]
@UI.selectionField:[{ position: 10 }]
@UI.textArrangement: #TEXT_FIRST
@EndUserText: { label: 'Airline', quickInfo: 'Airline' }
Carrier;

@UI.hidden: true
MDQualityBusinessRuleUUID;

@UI.selectionField:[{ position: 60 }]
@UI.textArrangement: #TEXT_ONLY
@EndUserText: { label: 'Outcome', quickInfo: 'Outcome' }
MDQltyBusRuleEvalResultCode;

@UI.identification: [{ position: 20 }]
@UI.lineItem: [
{ qualifier: 'DefaultVariantALP', position: 20, label: 'Airline Currency' }
]
@UI.selectionField: [{ position: 20 }]
@EndUserText: { label: 'Airline Currency', quickInfo: 'Airline Currency' }
CurrCode;

@UI.identification: [{ position: 30 }]
@UI.lineItem: [
{ qualifier: 'DefaultVariantALP', position: 30, label: 'Rule' }
]
@UI.selectionField: [{ position: 30 }]
@UI.textArrangement: #TEXT_LAST
@EndUserText: { label: 'Rule', quickInfo: 'Rule' }
MDQualityBusinessRule;

@UI.hidden: true
MDQualityBusinessRuleName;

@UI.identification: [{ position: 40 }]
@UI.lineItem: [
{ qualifier: 'DefaultVariantALP', position: 40, label: 'Base Table' }
]
@UI.selectionField: [{ position: 50 }]
@UI.textArrangement: #TEXT_FIRST
@EndUserText: { label: 'Base Table', quickInfo: 'Base Table' }
MDQltyBusinessRuleBaseTable;

@UI.dataPoint: { valueFormat.numberOfFractionalDigits: 0, title: 'Results' }
@UI.lineItem: [
{ qualifier: 'DefaultVariantALP', position: 50, label: 'Results' }
]
@EndUserText: { label: 'Results', quickInfo: 'Results' }
MDQltyNmbrOfBusRuleEvalResults;
}

Listing 7: Annotation file for view ZPN_C_MDGLTYCustomObject

 

5.     Activate the oData Service


For this CDS view the annotation “oData.publish: true” is set and the gateway oData service is generated with the activation of the CDS view. As a last step, the service then needs to be activated using transaction /IFWND/MAINT_SERVICE.

 

Summary


We have now created the following 5 files:



Figure 3: List of created files

 

We have now created 2 basic CDS views and 2 consumption views where the 2 consumption views provide the oData services access by the ALP , which we create in the next  paragraph. The created Metadata Extension file is applied to adapt the layout of the ALP.

 

Create the ALP application in your Web IDE


 

  1. Start a web IDE that is connected to the system where you developed the ALP in order to create a new project. For example: WEB IDE https://webidecp-fiori.dispatcher.int.sap.hana.ondemand.com/.Note: We assume here that it is an external application not built using SAP internal processes, thus add the following parameter &sap-ide-external=true to the URL of your SAP  Web IDE (if not done automatically).

  2. From the menu choose Create Project from Template.

  3. Clear filters and select New Analytical List Page and enter a project name and a title.Note: We assume here that it is an external application not built using SAP internal processes, thus add the following parameter sap-ide-external=true to the URL of your SAP  Web IDE (if not done automatically).Figure 4: first entry screen for creating the ALP from template

  4. Enter the service details: Search for the newly create consumption view ZPN_C_MDGLTYCUSTOMOBJECT_CDS

    Figure 5: Select the oData service  used by the ALP

    and, on the next screen, select both proposed annotations.

  5. Next select data collection ZPN_C_MDGLTYCustomObject.

  6. Important: To get the ALP working you have to do a minor correction in the generated code: Remove the following code snippet from the manifest file of your web app:
    "metadataUrlParams": {
    "sap-value-list": "none"
    }​


  7. Run as a Web Application (to use the already existing data in the database), logon to the backend system, and select GO, so that data is selected.


Then the resulting ALP should look like this:



Figure 6: Final ALP displaying evaluation resuts for the defined custom object ZZCARRIERS

 

Summary:


Our example is based on the assumption that you can use the generated persistence from the MDC model , because our focus is on the explanation how to create an ALP for analysing and drilling down into the rule evaluation result.

In the first section we created the required CDS views and in the section section we showed step by step how to create a FIORI application based on the ALP template.

In our example we restricted to  the main ALP capabilities. Usually more navigation possibilities are required by customers. For implementing those, please see as a starting point the given links below.

 

Further Reading for ALPs