In this blog lets look at the Managed Scenario (Behavior Implementation )  in the ABAP on BTP platform .

 

As mentioned in the title , Managed scenario helps to eliminate most of the code (used for CRUD operations in the Implementation class).  Since the managed approach takes all the responsibilities  of the CRUD operations , we just need to take account of certain authorizations (If we are going to implement the authorization objects and related objects ).

 

Lets consider the following scenario :

We have a student portal application with one header table and two other child tables .

Student Header Table :

@EndUserText.label : 'Header  for Student'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table ystudent_hdr {
  key client   : abap.clnt not null;
  key sid      : sysuuid_x16 not null;
  admission_no : abap.char(10);
  fname        : abap.char(25);
  mname        : abap.char(25);
  lname        : abap.char(25);
  contact_no1  : abap.char(10);
  contact_no2  : abap.char(10);
  dob          : abap.dats;
  gender       : ydgender;
  semail       : abap.char(50);
  pemail       : abap.char(50);
  paddress     : abap.string(256);
  caddress     : abap.string(256);
  blood_group  : ydbg;
  height       : abap.dec(4,4);
  weight       : abap.dec(4,4);

}

Student Academic details:

@EndUserText.label : 'Current Academic Details'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table yst_cracademic {
  key client   : abap.clnt not null;
  @AbapCatalog.foreignKey.screenCheck : false
  key sid      : sysuuid_x16 not null
    with foreign key ystudent_hdr
      where client = yst_cracademic.client
        and sid = yst_cracademic.sid;
  key course   : ydcourse not null;
  key semester : ydsemester not null;
  status       : ydsemstat;
  percentage   : abap.dec(3,2);
  feedback     : abap.string(256);

}

Student attendance details :

@EndUserText.label : 'Semester Attendance'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table yst_attendance {
  key client   : abap.clnt not null;
  @AbapCatalog.foreignKey.screenCheck : false
  key sid      : sysuuid_x16 not null
    with foreign key ystudent_hdr
      where client = yst_attendance.client
        and sid = yst_attendance.sid;
  key course   : ydcourse not null;
  key semester : ydsemester not null;
  status       : ydsemstat;
  percentage   : abap.dec(3,2);
  feedback     : abap.string(256);

}

Following CDS entities representing the Parent child Hierarchy :

Header :

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Student Header'
@Metadata.allowExtensions: true
define root view entity YSTUDENT_HDR_I
  as select from ystudent_hdr
  composition [0..*] of YST_CRACADEMIC_I as _academic
  composition [0..*] of YST_ATTENDANCE_I as _attendance
  association to YI_GENDER               as _gender on $projection.gender = _gender.Value
  association to YI_BG                   as _bg     on $projection.blood_group = _bg.Value
{
  key sid,
      admission_no,
      fname,
      mname,
      lname,
      contact_no1,
      contact_no2,
      dob,
      gender,
      _gender.Description as genderdesc,
      semail,
      pemail,
      paddress,
      caddress,
      blood_group,
      _bg.Description     as bg_desc,
      height,
      weight,
      _academic,
      _attendance,
      _gender,
      _bg
      //    _association_name // Make association public
}

Metadata Extension :

@Metadata.layer: #CORE
@UI: {
    headerInfo: { typeName: 'Student Detail',
                  typeNamePlural: 'Student Details',
                  
                  title: { type: #STANDARD, label: 'Student Details', value: 'fname' },
                  description:{ type: #STANDARD , label: 'Student Details' , value : 'lname'}

    },
    presentationVariant: [{ sortOrder: [{by: 'fname', direction: #ASC}]  }]


}
@Search.searchable: true
annotate view YSTUDENT_HDR_I with
{
  @UI.facet: [{ id: 'Student',
                  purpose: #STANDARD,
                  type: #IDENTIFICATION_REFERENCE,
                  label: 'Student Details',
                  position: 10
     },
     { id: 'Academic',
  purpose: #STANDARD,
  type: #LINEITEM_REFERENCE,
  label: 'Academic Details',
  position: 20,
  targetElement: '_academic'},
     { id: 'Attendance',
  purpose: #STANDARD,
  type: #LINEITEM_REFERENCE,
  label: 'Attendance Details',
  position: 20,
  targetElement: '_attendance'}]

  @UI: { identification: [{ position: 10, label: 'Student ID' }] }
  @UI.hidden: true
  sid;
  @UI: { lineItem:       [{ position: 20, importance: #HIGH, label: 'Admission Number' }],
         identification: [{ position: 20, label: 'Admission Number' }] }
  @Search.defaultSearchElement: true
  admission_no;
  @UI: { lineItem:       [{ position: 30, importance: #HIGH, label: 'First Name' }],
     identification: [{ position: 30, label: 'First Name' }] }
  @Search.defaultSearchElement: true
  fname;
  @UI: { lineItem:       [{ position: 40, importance: #HIGH, label: 'Middle Name' }],
     identification: [{ position: 40, label: 'Middle Name' }] }
  mname;
  @UI: { lineItem:       [{ position: 50, importance: #HIGH, label: 'Last Name' }],
     identification: [{ position: 50, label: 'Last Name' }] }
  @Search.defaultSearchElement: true
  lname;
  @UI: { lineItem:       [{ position: 60, importance: #HIGH, label: 'Contact Number 1' }],
     identification: [{ position: 60, label: 'Contact Number 1' }] }
  contact_no1;
  @UI: { lineItem:       [{ position: 70, importance: #HIGH, label: 'Contact Number 2' }],
     identification: [{ position: 70, label: 'Contact Number 2' }] }
  contact_no2;
  @UI: { lineItem:       [{ position: 80, importance: #HIGH, label: 'Date of Birth' }],
     identification: [{ position: 80, label: 'Date of Birth' }] }
  dob;
  @UI: { lineItem:       [{ position: 90, importance: #HIGH, label: 'Gender' }],
     identification: [{ position: 90, label: 'Gender' }] }
  @Consumption.valueHelpDefinition: [{ entity:
  {name: 'YI_GENDER' , element: 'Value' },
  additionalBinding: [{ localElement: 'genderdesc', element: 'Description' }]
  }]
  gender;
  @UI: { lineItem:       [{ position: 100, importance: #HIGH, label: '' }],
    identification: [{ position: 100, label: '' }] }
  genderdesc;
  @UI: { lineItem:       [{ position: 110, importance: #HIGH, label: 'Student Email' }],
     identification: [{ position: 110, label: 'Student Email' }] }
  semail;
  @UI: { lineItem:       [{ position: 120, importance: #HIGH, label: 'Parent Email' }],
     identification: [{ position: 120, label: 'Parent Email' }] }
  pemail;
  @UI: { lineItem:       [{ position: 130, importance: #HIGH, label: 'Parent Address' }],
     identification: [{ position: 130, label: 'Parent Address' }] }
  paddress;
  @UI: { lineItem:       [{ position: 140, importance: #HIGH, label: 'Student Address ' }],
      identification: [{ position: 140, label: 'Student Address ' }] }
  caddress;
  @UI: { lineItem:       [{ position: 150, importance: #HIGH, label: 'Blood Group' }],
     identification: [{ position: 150, label: 'Blood Group' }] }
  @Consumption.valueHelpDefinition: [{ entity:
  {name: 'YI_BG' , element: 'Value' },
  additionalBinding: [{ localElement: 'bg_desc', element: 'Description' }]
  }]
  blood_group;
  @UI: { lineItem:       [{ position: 160, importance: #HIGH, label: '' }],
     identification: [{ position: 160, label: '' }] }
  bg_desc;
  @UI: { lineItem:       [{ position: 170, importance: #HIGH, label: 'Height in CM' }],
  identification: [{ position: 170, label: 'Height in CM' }] }
  height;
  @UI: { lineItem:       [{ position: 180, importance: #HIGH, label: 'Weight in KG' }],
     identification: [{ position: 180, label: 'Weight in KG' }] }
  weight;

}

Academic details :

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Academic Details'
@Metadata.allowExtensions: true
define view  entity  YST_CRACADEMIC_I
  as select from yst_cracademic
  association to parent YSTUDENT_HDR_I as _HDR      on $projection.sid = _HDR.sid
  association to YI_COURSE             as _course   on $projection.course = _course.Value
  association to YI_SEMESTER           as _semester on $projection.semester = _semester.Value
  association to YI_SEMSTAT           as _stat on $projection.status = _stat.Value
{
  key sid,
  key course,
  key semester,
      _course.Description   as course_desc,
      _semester.Description as semester_desc,
      status,
      _stat.Description as semester_status,
      percentage,
      feedback,
      _HDR // Make association public
}

Metadata extension:

@Metadata.layer: #CORE

@UI: {
    headerInfo: { typeName: 'Academic Detail',
                  typeNamePlural: 'Academic Details',
                
                  title: { type: #STANDARD, label: 'Academic Details', value: 'course' },
                  description:{ type: #STANDARD , label: 'Academic Details' , value : 'course_desc'}

    },
    presentationVariant: [{ sortOrder: [{by: 'semester', direction: #ASC}]  }]


}
@Search.searchable: true
annotate view YST_CRACADEMIC_I with
{
  @UI.facet: [{ id: 'Acadmic',
                  purpose: #STANDARD,
                  type: #IDENTIFICATION_REFERENCE,
                  label: 'Academic Details',
                  position: 10
     }]
  @UI: { identification: [{ position: 10, label: 'Student ID' }] }
  @UI.hidden: true
  sid;
  @UI: { lineItem:       [{ position: 20, importance: #HIGH, label: 'Course' }],
   identification: [{ position: 20, label: 'Course' }] }
  @Search.defaultSearchElement: true
  @Consumption.valueHelpDefinition: [{ entity:
  {name: 'YI_COURSE' , element: 'Value' },
  additionalBinding: [{ localElement: 'course_desc', element: 'Description' }]
  }]
  course;
  @UI: { lineItem:       [{ position: 30, importance: #HIGH, label: '' }],
      identification: [{ position: 30, label: '' }] }
  @Search.defaultSearchElement: true
  course_desc;
  @UI: { lineItem:       [{ position: 40, importance: #HIGH, label: 'Semester' }],
      identification: [{ position: 40, label: 'Semester' }] }
  @Search.defaultSearchElement: true
  @Consumption.valueHelpDefinition: [{ entity:
  {name: 'YI_SEMESTER' , element: 'Value' },
  additionalBinding: [{ localElement: 'semester_desc', element: 'Description' }]
  }]
  semester;
  @UI: { lineItem:       [{ position: 50, importance: #HIGH, label: '' }],
      identification: [{ position: 50, label: '' }] }
  @Search.defaultSearchElement: true
  semester_desc;
  @UI: { lineItem:       [{ position: 60, importance: #HIGH, label: 'Status' }],
  identification: [{ position: 60, label: 'Status' }] }
  @Search.defaultSearchElement: true
  @Consumption.valueHelpDefinition: [{ entity:
  {name: 'YI_SEMSTAT' , element: 'Value' },
  additionalBinding: [{ localElement: 'semester_status', element: 'Description' }]
  }]
  status;
  @UI: { lineItem:       [{ position: 70, importance: #HIGH, label: '' }],
   identification: [{ position: 70, label: '' }] }
  @Search.defaultSearchElement: true
  semester_status;
  @UI: { lineItem:       [{ position: 80, importance: #HIGH, label: 'Percentage Scored' }],
  identification: [{ position: 80, label: 'Percentage Scored' }] }
  @Search.defaultSearchElement: true
  percentage;
  @UI: { lineItem:       [{ position: 90, importance: #HIGH, label: 'Student Feed Back' }],
   identification: [{ position: 90, label: 'Student Feed Back' }] }
  @Search.defaultSearchElement: true
  feedback;

}

Attendance Details :

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Attendance'
@Metadata.allowExtensions: true
define view entity YST_ATTENDANCE_I
  as select from yst_attendance
  association to parent YSTUDENT_HDR_I as _hdr      on $projection.sid = _hdr.sid
  association to YI_COURSE             as _course   on $projection.course = _course.Value
  association to YI_SEMESTER           as _semester on $projection.semester = _semester.Value
  association to YI_SEMSTAT            as _stat     on $projection.status = _stat.Value
{
  key sid,
  key course,
  key semester,
      _course.Description   as course_desc,
      _semester.Description as semester_desc,
      status,
      _stat.Description     as semester_status,
      percentage,
      feedback,
      _hdr // Make association public
}

Metadata Extension :

@Metadata.layer: #CORE
@UI: {
    headerInfo: { typeName: 'Attendance Detail',
                  typeNamePlural: 'Attendance Details',
                 
                  title: { type: #STANDARD, label: 'Attendance Detail', value: 'course' },
                  description:{ type: #STANDARD , label: 'Attendance Details' , value : 'course_desc'}

    },
    presentationVariant: [{ sortOrder: [{by: 'semester', direction: #ASC}]  }]


}
@Search.searchable: true
annotate view YST_ATTENDANCE_I
    with 
{
      @UI.facet: [{ id: 'Attendance',
                  purpose: #STANDARD,
                  type: #IDENTIFICATION_REFERENCE,
                  label: 'Attendance Details',
                  position: 10
     }]
  @UI: { identification: [{ position: 10, label: 'Student ID' }] }
  @UI.hidden: true
  sid;
  @UI: { lineItem:       [{ position: 20, importance: #HIGH, label: 'Course' }],
   identification: [{ position: 20, label: 'Course' }] }
  @Search.defaultSearchElement: true
  @Consumption.valueHelpDefinition: [{ entity:
  {name: 'YI_COURSE' , element: 'Value' },
  additionalBinding: [{ localElement: 'course_desc', element: 'Description' }]
  }]
  course;
  @UI: { lineItem:       [{ position: 30, importance: #HIGH, label: '' }],
      identification: [{ position: 30, label: '' }] }
  @Search.defaultSearchElement: true
  course_desc;
  @UI: { lineItem:       [{ position: 40, importance: #HIGH, label: 'Semester' }],
      identification: [{ position: 40, label: 'Semester' }] }
  @Search.defaultSearchElement: true
  @Consumption.valueHelpDefinition: [{ entity:
  {name: 'YI_SEMESTER' , element: 'Value' },
  additionalBinding: [{ localElement: 'semester_desc', element: 'Description' }]
  }]
  semester;
  @UI: { lineItem:       [{ position: 50, importance: #HIGH, label: '' }],
      identification: [{ position: 50, label: '' }] }
  @Search.defaultSearchElement: true
  semester_desc;
  @UI: { lineItem:       [{ position: 60, importance: #HIGH, label: 'Status' }],
  identification: [{ position: 60, label: 'Status' }] }
  @Search.defaultSearchElement: true
  @Consumption.valueHelpDefinition: [{ entity:
  {name: 'YI_SEMSTAT' , element: 'Value' },
  additionalBinding: [{ localElement: 'semester_status', element: 'Description' }]
  }]
  status;
  @UI: { lineItem:       [{ position: 70, importance: #HIGH, label: '' }],
   identification: [{ position: 70, label: '' }] }
  @Search.defaultSearchElement: true
  semester_status;
  @UI: { lineItem:       [{ position: 80, importance: #HIGH, label: 'Percentage Scored' }],
  identification: [{ position: 80, label: 'Percentage Scored' }] }
  @Search.defaultSearchElement: true
  percentage;
  @UI: { lineItem:       [{ position: 90, importance: #HIGH, label: 'Student Feed Back' }],
   identification: [{ position: 90, label: 'Student Feed Back' }] }
  @Search.defaultSearchElement: true
  feedback;
    
}

 

Few other CDS which is used for Search Help :

Blood Group:

@AbapCatalog.sqlViewName: 'YIBG'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Gender'
define view YI_BG as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YBG' )
{
key domain_name,
  key value_position,
      @Semantics.language: true
  key language,
      value_low as Value,
      @Semantics.text: true
      text      as Description
    
}

Domain :

Course :

@AbapCatalog.sqlViewName: 'YICOURSE'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Course'
define view YI_COURSE
  as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YCOURSE' )
{
  key domain_name,
  key value_position,
      @Semantics.language: true
  key language,
      value_low as Value,
      @Semantics.text: true
      text      as Description

}

Domain :

Gender :

@AbapCatalog.sqlViewName: 'YGEN'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Gender'
define view YI_GENDER as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YGENDER' )
{
key domain_name,
  key value_position,
      @Semantics.language: true
  key language,
      value_low as Value,
      @Semantics.text: true
      text      as Description
    
}

Domain :

Semester:

@AbapCatalog.sqlViewName: 'YISEM'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Semester'
define view YI_SEMESTER
  as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YSEMESTER' )
{
  key domain_name,
  key value_position,
      @Semantics.language: true
  key language,
      value_low as Value,
      @Semantics.text: true
      text      as Description

}

Domain :

Status :

@AbapCatalog.sqlViewName: 'YISTAT'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Status'
define view YI_SEMSTAT
  as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YSEMSTAT' )
{
  key domain_name,
  key value_position,
      @Semantics.language: true
  key language,
      value_low as Value,
      @Semantics.text: true
      text      as Description

}

Domain :

 

Behavior definition :

managed implementation in class YST_BEHAVIOUR unique;
strict;
define behavior for YSTUDENT_HDR_I 
persistent table ystudent_hdr
lock master
authorization master ( instance )

{
  field ( numbering : managed, readonly ) sid;
  create;
  update;
  delete;
  field ( readonly ) genderdesc;
  field ( readonly ) bg_desc;
  association _attendance { create; }
  association _academic { create; }
}

define behavior for YST_ATTENDANCE_I 
persistent table yst_attendance
lock dependent by _hdr
authorization dependent by _hdr

{
  update;
  delete;
  field ( readonly ) sid;
  field ( readonly ) course_desc;
  field ( readonly ) semester_desc;
  field ( readonly ) semester_status;
  association _hdr;
}

define behavior for YST_CRACADEMIC_I 
persistent table yst_cracademic
lock dependent by _HDR
authorization dependent by _HDR

{
  update;
  delete;
  field ( readonly ) sid;
  field ( readonly ) course_desc;
  field ( readonly ) semester_desc;
  field ( readonly ) semester_status;
  association _HDR;
}

Behavior Implementation :

****Global Class ************
CLASS yst_behaviour DEFINITION
  PUBLIC
  abstract
  final
  for behavior of YSTUDENT_HDR_I .

  PUBLIC SECTION.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS yst_behaviour IMPLEMENTATION.
ENDCLASS.
****Local Types******

CLASS YST_behaviour2 DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
      IMPORTING keys REQUEST requested_authorizations FOR ystudent_hdr_i RESULT result.


ENDCLASS.

CLASS YST_behaviour2 IMPLEMENTATION.
  METHOD get_instance_authorizations.
********Your code goes here
  ENDMETHOD.
ENDCLASS.

 

If we look at the Behavior class over here we have barely written 28 lines of code , if we expose the CDS in a service Definition and create a service Binding , All the CRUD operations for both parent and child works fine . All the CRUD operation code is taken care by the system and we have a minimum effort .

Since SAP recommends using the View Entities in CDS we had to create the class , if we were using just the views the class is optional.

Lets Have a look At the Screen:

NB: The blog is written based on my Research and developments on BTP , If I have mentioned any miss information , Please correct me in the comments .

Sara Sampaio

Sara Sampaio

Author Since: March 10, 2022

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x