ABAP RAP

ABAP RESTful Application Programming Model

Übungsaufgaben

(C) Brandeis Consulting📁

Namenskonvention in den Übungen

Grundsätzlich gelten die Konventionen aus dem Buch Clean ABAP. Da im RAP Bereich viele Objekte aufeinander aufbauen, muss man einerseits einen durchgängigen Namen verwenden, im folgenden mit <Name> bezeichnet, andererseits die Objekte bzw. Objekttypen sauber und einheitlich unterscheiden. Das bringt uns wieder zur umstrittenen ungarischen Notation, die an dieser Stelle aber die beste Option ist. Die Notation orientiert sich an der SAP Dokumentation zu RAP.

Ein guter <Name>

Die Suffix _R,_M,_D,_U,_C, aus der SAP Doku sind in der echten Welt nicht notwendig, da hier die Szenarien (read-only, managed, draft, unmanaged, service consumption) auf den gleichen Tabellen implementiert wurden.

Euere Initialen <I> und die Übungsnummer

Damit wir uns nicht gegenseitig stören verwenden wir unsere 2- oder 3-stelligen Initialen in den Namen. In meinem Falle z.B. JB oder JBR.
Darüber hinaus werden wir für das gleiche Objekt im Laufe der Schulung mehrere Views anlegen. Damit wir hier keine Namenskonflikte bekommen, setzen wir hinter unsere Initialen auch immer die Übungsnummer. Der CDS View auf die Tabelle ZBC_USERS für die erste Übung, würde also bei mir beispielsweise ZI_JBR1_USERS heißen.

Initialen nur für Übungszwecke
Die Initialen und Nummern werden hier nur für die Übungen verwendet oder für lokale Tests, die nicht produktiv genutzt werden sollen. Im Projekt haben diese natürlich nichts zu suchen.

(C) Brandeis Consulting📁

Namenskonventionen - Objekttypen

Basierend auf den Vorschlägen der SAP, ergänzt um die Initialen und den Kundennamensraum Z.

Objekttyp Konvention Beschreibung
DB-Tabelle ZA_<I>_<Name> Aktive Daten
DB-Tabelle ZD_<I>_<Name> Draft Daten
CDS View ZI_<I>_<Name> Interface View
CDS View ZR_<I>_<Name> Basis View
CDS View ZC_<I>_<Name> Projection View für den Consumption layer
MetaData Extensions Wie der zugehörige CDS View, Falls mehr als eine benötigt wird, kann man diese durchnummerieren
Behaviour Definition Wie der zugehörige CDS View
Service Defintion Z_<I>_<Name>
Service Binding ZUI_<I>_<Name> Für UI Services
Service Binding ZAPI_<I>_<Name> Für API Services
ABAP Klasse ZBP_<I>_<Name> Klasse für den Behaviour Pool
Lokale Klasse LHC_ Lokale Handler Klasse
Lokale Klasse LSC_ Lokale Saver Klasse
(C) Brandeis Consulting📁

Übungsszenario

Aufgabenverwaltung

Administrative Tätigkeiten

  • Benutzer der Aufgabenverwaltung
  • Statusverwaltung
    • Statusübergänge / Workflows

Nutzung der Aufgabenverwaltung

  • Aufgabenliste
  • Projektverwaltung
  • Kommentare zu einzelnen Aufgaben
(C) Brandeis Consulting📁

Übung 1: Lesender Zugriff auf eine DB-Tabelle (1/1)

In dieser Übung sollen die Daten der CDS-View im Browser angezeigt werden. Es sind die folgenden Elemente relevant:

  • Datenbanktabelle ZBC_USERS - diese ist bereits vorhanden und muss nicht angelegt werden
  • CDS View ZI_<I>_USERS
  • Service Definition Z_<I>_<Name>
  • Service Binding ZUI_<I>_<Name>

1. Anlegen eine CDS View Entity auf Basis der Tabelle

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Users Interface View'
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
define view entity zi_jb1_users
  as select from zbc_users
{
  key user_id       as UserId,
      firstname     as Firstname,
      lastname      as Lastname,
      email         as Email,
      gender        as Gender,
      date_of_birth as DateOfBirth
}
(C) Brandeis Consulting📁

Übung 1: Lesender Zugriff auf eine DB-Tabelle (2/)

2. Anlegen eines Services

Zum Anlegen eines neuen Services für Euren CDS View macht Ihr in der Navigation auf der linken Seite einen Rechtsklick. Dann aus dem Kontextmenü auswählen: New Service Definition

(C) Brandeis Consulting📁

Übung 1: Lesender Zugriff auf eine DB-Tabelle (3/)

Das Ergebnis ist eine Service Definition:

@EndUserText.label: 'Users Service'
define service Z_jb1_users {
    expose ZI_JB1_USERS; 
    
}

Der Aliasname as Users kann nach dem Viewnamen hinzugefügt werden. Das ist nicht unbedingt notwendig, ist aber schöner als der technische Namen.

@EndUserText.label: 'Users Service'
define service Z_jb1_users {
  expose zi_jb1_users as Users;
}
(C) Brandeis Consulting📁

Übung 1: Lesender Zugriff auf eine DB-Tabelle (4/)

3. Anlegen des Service Bindings

Zum Anlegen des Service Bindings machen wir widerum ein Rechtsklick auf den Service und wählen dann aus dem Kontextmenü New Service Binding

Im folgenden Popup müssen wir den Binding Type auswählen. Wir verwenden hier OData V2 - UI

(C) Brandeis Consulting📁

Übung 1: Lesender Zugriff auf eine DB-Tabelle (5/)

Nach dem Aktivieren des Serivice Bindings können wir den Service Endpoint lokal veröffentlichen. Das kann ein paar Sekunden dauern.
Danach können wir auf der rechten Seite unser Objekt Users auswählen und auf Preview... klicken.

(C) Brandeis Consulting📁

Übung 1: Lesender Zugriff auf eine DB-Tabelle (6/)

Im Browser öffnet sich dann ein neuer Tab und es erscheint die Vorschau auf unseren View. Allerdings sehen wir zunächst keine Daten.
Dazu braucht es noch zwei Schritte:

  • Einstellen der sichtbaren Spalten über die Settings. In dem Einstelldialog kann auch die Reihenfolge der Spalten festgelegt werden:
  • Suche der Daten über den Button Go

(C) Brandeis Consulting📁

Übung 1: Lesender Zugriff auf eine DB-Tabelle (6/)

Ergebnis

(C) Brandeis Consulting📁

Übung 1: Lesender Zugriff auf eine DB-Tabelle (6/) - Erweiterung um UI-Annotationen

Annotationen für die Liste

Füge für die Felder jeweils eine Annotation

      @UI.lineItem:      [ { position: 10 } ] 

ein. Damit werden die Spalten an der angegebenen Position eingeblendet.

Annotationen für die Details

Füge für die Felder auch noch eine Annotation für die Details-Ansicht hinzu:

      @UI.identification:[ { position: 10, 
                             label:    'User ID' } ] 
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Users Interface View'
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
define view entity zi_jb1_users
  as select from zbc_users
{
      @UI.facet: [  { id: 'User',
                      purpose:  #STANDARD,
                      type:     #IDENTIFICATION_REFERENCE,
                      label:    'User',
                      position: 10 }  ]

      @UI.lineItem:      [ { position: 10 } ] 
      @UI.identification:[ { position: 10, 
                             label:    'User ID' } ] 
  key user_id       as UserId,
   
      @UI: { lineItem:       [ { position: 20  } ] }
      firstname     as Firstname,
      
      @UI: { lineItem:       [ { position: 30, label: 'Nachname'  } ] }
      lastname      as Lastname,
      
      @UI: { lineItem:       [ { position: 40  } ] }
      email         as Email,
      
      @UI: { lineItem:       [ { position: 50 } ] }
      gender        as Gender,
      
      @UI: { lineItem:       [ { position: 60 } ] }
      date_of_birth as DateOfBirth
}
(C) Brandeis Consulting📁

Übung 1a. Erweiterung um Suchfelder

Voraussetzungen

Ausgangsbasis sind die Objekte aus Übung 1:

  • CDS View
  • Service Definition
  • Service Binding

Falls es damit Probleme gab, bitte den View ZI_JB1_USERS als Vorlage kopieren und Service Definition und -Binding neu anlegen.

Aufgabenstellung

Erweitere Deinen View um Suchfelder für Vor- und Nachname.

Musterlösung

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Users Interface View'
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
define view entity zi_jb1_users
  as select from zbc_users
{
      @UI.facet: [  { id: 'User',
                      purpose:  #STANDARD,
                      type:     #IDENTIFICATION_REFERENCE,
                      label:    'User',
                      position: 10 }  ]

      @UI.lineItem:      [ { position: 10 } ] 
      @UI.identification:[ { position: 10, 
                             label:    'User ID' } ] 
  key user_id       as UserId,
   
      @UI: { lineItem:       [ { position: 20  } ] ,
             selectionField: [{ position: 10 }]}
      firstname     as Firstname,
      
      @UI: { lineItem:       [ { position: 30, label: 'Nachname'  } ] ,
             selectionField: [{ position: 20 }]}
      lastname      as Lastname,
      
...
}
(C) Brandeis Consulting📁

Übung 1b. Erweiterung um Google-Suchfeld

Voraussetzungen

Wir bauen auf den bisherigen Views auf. Hilfsweise vorgehen wie bei Übung 1a.

Aufgabenstellung

Erstelle für den View ein Google Suchfeld, das die Felder

  • Firstname
  • Lastname und
  • UserId
    berücksichtigt.

...
@Search.searchable: true   
define view entity zi_jb1_users
  as select from zbc_users
{
...

      @UI.lineItem:      [ { position: 10 } ] 
      @UI.identification:[ { position: 10, 
                             label:    'User ID' } ] 
      @Search: { defaultSearchElement: true,
                 fuzzinessThreshold: 0.95,
                 ranking: #HIGH }
  key user_id       as UserId,
   
      @Search: { defaultSearchElement: true,
                 fuzzinessThreshold: 0.7,
                 ranking: #LOW }
      @UI: { lineItem:       [ { position: 20  } ] ,
             selectionField: [{ position: 10 }]}
      firstname     as Firstname,
      
      @Search: { defaultSearchElement: true,
                 fuzzinessThreshold: 0.7,
                 ranking: #LOW }
      @UI: { lineItem:       [ { position: 30, label: 'Nachname'  } ] ,
             selectionField: [{ position: 20 }]}
      lastname      as Lastname,
.

(C) Brandeis Consulting📁

Übung 1c.

Voraussetzungen

Wir bauen auf den bisherigen Views auf. Hilfsweise vorgehen wie bei Übung 1a.

Aufgabenstellung

Das Feld GENDER soll auch zu einem Suchfeld werden und es soll eine Werthilfe zur Verfügung gestellt werden. Verwende hierfür die Domänenfestwerte aus der Domäne ZBC_GENDER. Dazu können Sie einen CDS View auf die Tabelle DD07T erstellen. Beachten Sie die Sprachabhängigkeit. Diese wird durch die Annotation
@Semantics.language: true
erreicht.

Nach dem der neue View angelegt wurde, muss dieser auch in der Service Definition mit angegeben werden.

Musterlösung

Werthilfeview

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Gender Value Help'
define view entity zi_jb1c_gender
  as select from dd07t
{
  key valpos     as value_position,
      @Semantics.language: true
  key ddlanguage as language,
      domvalue_l as value,

      @Semantics.text: true    
      ddtext     as text
}
where domname    = 'ZBC_GENDER'
  and ddlanguage = $session.system_language

Einbindung der Werthilfe in der Feldliste:

      @UI: { lineItem:       [ { position: 50 } ] ,
             selectionField: [{ position: 40  } ] }
      @Consumption.valueHelpDefinition: [{ entity: { name: 'zi_jb1c_gender',
                                                     element: 'value' } } ] 
      
      gender        as Gender,
(C) Brandeis Consulting📁

Übung 1d.

Aufgabenstellung

Lagere alle Annotationen mit Hilfe des Quick-Fixes in eine Metadata Extension (MDE) Datei mit dem gleichen Namen wie dein View aus. Dazu gibt es einen Assistenten unter dem Menüpunkt Source Code => Extract Metadata Extension. Aktiviere alles und teste Deine Anwendung.

Beachte:

  • Die Annotation @Metadata.layer: #CUSTOMER sollte in der MDE gesetzt sein.
  • Die Annotation @Metadata.allowExtensions: true muss in dem CDS View gesetzt sein. (=> Quickfix)
  • Nach dem Extrahieren sollte die CDS Datei sauber formatiert werden.

(C) Brandeis Consulting📁

Übung 1d. - Musterlösung

Assistent zur Extraktion der MDE

CDS View

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Users Interface View'
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
} 
@Metadata.allowExtensions: true
define view entity zi_jb1_users
  as select from zbc_users
    
{
  key user_id       as UserId,
      firstname     as Firstname,
      lastname      as Lastname,
      email         as Email,
      gender        as Gender,
      date_of_birth as DateOfBirth
}
(C) Brandeis Consulting📁

Übung 1d.

Musterlösung

Metadata Extension

@Metadata.layer: #CUSTOMER
@Search.searchable: true   
annotate view zi_jb1_users with
{
  @UI.facet: [  { id: 'User',
                  purpose:  #STANDARD,
                  type:     #IDENTIFICATION_REFERENCE,
                  label:    'User',
                  position: 10 }  ]
  @UI.lineItem:      [ { position: 10 } ]
  @UI.identification:[ { position: 10,
                         label:    'User ID' } ]
  @Search: { defaultSearchElement: true,
             fuzzinessThreshold: 0.95,
             ranking: #HIGH }
  UserId;

  @Search: { defaultSearchElement: true,
             fuzzinessThreshold: 0.7,
             ranking: #LOW }
  @UI: { lineItem:       [ { position: 20  } ] ,
         selectionField: [{ position: 10 }]}
  Firstname;

MDE, Fortsetzung

  @Search: { defaultSearchElement: true,
             fuzzinessThreshold: 0.7,
             ranking: #LOW }
  @UI: { lineItem:       [ { position: 30, 
                             label: 'Nachname'  } ] ,
         selectionField: [{ position: 20 }]}
  Lastname;
  
  @UI: { lineItem:       [ { position: 40  } ] }
  
  Email;
  @UI: { lineItem:       [ { position: 50 } ] ,
         selectionField: [{ position: 40  } ] }
  @Consumption.valueHelpDefinition: 
            [{ entity: { name: 'zi_jb1c_gender',
            element: 'value' } } ]

  Gender;
  @UI: { lineItem:       [ { position: 60 } ] }
  DateOfBirth;
}
(C) Brandeis Consulting📁

Übung 2: Übersicht

(C) Brandeis Consulting📁

Übung 2: Unterobjekte hinzufügen - Vorarbeiten (1/2)

Voraussetzung

Wir bauen auf den bisherigen Views auf. Bitte lege eine Kopie des CDS View ZI_<I>1_USERS auf ZI_<I>2_USERS an und verfahre genauso mit den MetaData Extensions. Falls Du nicht mitgekommen bist oder noch mal neu aufsetzen willst, kannst Du auch die folgenden Vorlagen nutzen:

  • CDS View ZI_EX1_USERS
  • MetaData Extension ZI_EX1_USERS

Am einfachsten erstellst Du eine Kopie von einem Objekt mit einem Rechtsklick in der Navigation, dann wählst Du "Duplicate" aus dem Kontextmenü:

Der Verweis der MDE auf den CDS-View muss nach dem Kopieren angepasst werden. Lege für den neuen View eine Service Definition und ein Service Binding an wie in Übung 1 an. Teste den neuen View.

Empfehlung
Schließe alle Tabreiter, die Du nicht mehr brauchst. Sonst kannst Du schnell mit den unterschiedlichen Versionen durcheinander kommen.

(C) Brandeis Consulting📁

Übung 2: Unterobjekte hinzufügen - Aufgabenstellung

Aufgabenstellung

Lege einen View ZI_<I>2_TASKS für die Aufgaben mit allen Spalten an.

Erstelle darin eine Assoziationen zu den Benutzern (View ZI_<I>2_USERS ) als Bearbeiter (Feld ASSIGNEE )

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Tasks'
define root view entity zi_jb2_tasks
  as select from zbc_tasks
  
  association [1] to zi_jb2_users as _Assignee
  on $projection.Assignee =  _Assignee.UserId
  
{
  key task_key    as TaskKey,
      summary     as Summary,
      status      as Status,
      project     as Project,
      description as Description,
      assignee    as Assignee,
      type        as Type,
      author      as Author,
      changed_at  as ChangedAt,
      created_at  as CreatedAt,
      due_date    as DueDate,
      solution    as Solution,
      priority    as Priority,
      product     as Product,
      
      _Assignee
}
(C) Brandeis Consulting📁

Übung 2: Unterobjekte hinzufügen - USER View erweitern

Erweitern des User-CDS-Views

Die "Gegenrichtung" der Assoziationen aus den Aufgaben müssen jetzt bei uns in den Benutzern modelliert werden.

Musterlösung

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Users Interface View'
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
} 
@Metadata.allowExtensions: true
define view entity ZI_JB2_USERS
  as select from zbc_users
  
  association [0..*] to zi_jb2_tasks as _TasksToDo
  on $projection.UserId = _TasksToDo.Assignee
        
{
  key user_id       as UserId,
      firstname     as Firstname,
      lastname      as Lastname,
      email         as Email,
      gender        as Gender,
      date_of_birth as DateOfBirth,
      
      //Associations
      _TasksToDo
}
(C) Brandeis Consulting📁

Übung 2: Unterobjekte hinzufügen - Exponieren des Tasks View

@EndUserText.label: 'Users'
define service Z_jb2_users {
  expose ZI_JB2_USERS as users;
  expose zi_jb2_tasks as tasks;
}
(C) Brandeis Consulting📁

Übung 2: Unterobjekte hinzufügen - Unterobjekte auf der Oberfläche plazieren

In der Metadata Extension für den User-View wird die @UI.facet Annotation ergänzt:

                { id: 'TasksToDo',
                  purpose:  #STANDARD,
                  type:     #LINEITEM_REFERENCE,
                  label:    'Tasks to do',
                  targetElement: '_TasksToDo',
                  position: 10 }  ]
(C) Brandeis Consulting📁

Übung 3: Anlegen eines einfachen Pflegeviews

Szenario

Es sollen die Projekte in der Aufgabenverwaltung gepflegt werden.

Das Feld PROJECT_KEY ist ein 10-Stelliger, alphanumerischer Schlüssel, der das Projekt bezeichnet, z.B. BW, RAP oder DB1_ etc.

Der NAME des Projektes soll frei vergeben werden können. Auf Mehrsprachigkeit wird hier bewusst verzichtet.

Das Feld PROJECT_MANAGER soll den Projektleiter enthalten. Er hat später automatisch besondere Berechtigungen. Dieses Feld bezieht sich auf die SAP Benutzerverwaltung bzw. unsere Ergänzungen in der Tabelle ZBC_USERS.

Die Felder werden automatisch gefüllt, wenn wir die passenden Annotationen verwenden:

  • CHANGED_BY
  • CHANGED_AT
  • CREATED_BY
  • CREATED_AT
@EndUserText.label : 'Projects'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zbc_projects {

  key client      : abap.clnt not null;
  key project_key : zbc_project_key not null;
  name            : zbc_project_name;
  project_manager : abp_creation_user;
  changed_by      : abp_lastchange_user;
  changed_at      : abp_locinst_lastchange_tstmpl;
  created_by      : zbc_author;
  created_at      : abp_creation_tstmpl;

}
(C) Brandeis Consulting📁

Übung 3 - 1. Schritt: Anlage des CDS Views

Das Objekt Root View Entity anlegen

(C) Brandeis Consulting📁

Übung 3 - 2. Schritt: Implementierung des CDS-Views

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projects'
define root view entity zi_projects 
as 
select from zbc_projects {
         @UI.facet: [ {  id: 'Tasks',
                       purpose: #HEADER ,
                      type:     #IDENTIFICATION_REFERENCE,
                      label: 'Status Value',
                      position: 10 } ,            
                      
                      {    id: 'Details',
                           purpose: #STANDARD ,
                           type:     #FIELDGROUP_REFERENCE,
                           label: 'Details',
                           position: 20 ,
                           targetQualifier: 'Details'}  ] 
                           
    @UI: { identification: [ { position: 10 } ],
           selectionField: [ { position: 10 } ] ,
           lineItem:       [ { position: 10 } ] }
    @UI.fieldGroup: [ { qualifier: 'Details' ,
                        position: 10 }]                           
    key project_key as ProjectKey,
    @UI: { identification: [ { position: 20 } ],
           lineItem:       [ { position: 20 } ] }
    
    @UI.fieldGroup: [ { qualifier: 'Details' ,
                        position: 20 }]          
    name as Name,
    @UI: {  lineItem:       [ { position: 10 } ] }
    @UI.fieldGroup: [ { qualifier: 'Details' ,
                        position: 30 }]          
    project_manager as ProjectManager,    
    
    @UI: { identification: [ { position: 110 } ] }
    @Semantics.user.lastChangedBy: true
    changed_by as ChangedBy,
    
    @UI: { identification: [ { position: 120 } ] }
    @Semantics.systemDateTime.lastChangedAt: true
    changed_at as ChangedAt,
    
    @UI: { identification: [ { position: 130 } ] }
    @Semantics.user.createdBy: true
    created_by as CreatedBy,
    
    @UI: { identification: [ { position: 150 } ] }
    @Semantics.systemDateTime.createdAt: true
    created_at as CreatedAt
}
(C) Brandeis Consulting📁

Übung 3 - 3. Schritt: Anlegen der Verhaltensdefinitiion

  • Rechtsklick auf den CDS-View: => New Behaviour Definition auswählen.
  • Zusätzlich zu dem generierten Code muss noch das Mapping zwischen DB-Feldern und Viewl-Feldern festgelegt werden.
  • Die Klasse für die Implementierung muss noch angelegt werden. Hierbei hilft ein Quick-Fix.
managed implementation in class zbp_i_projects unique;
strict ;

define behavior for zi_projects //alias <alias_name>
persistent table zbc_projects
lock master
authorization master ( instance )
//etag master <field_name>
{
  create;
  update;
  delete;
  mapping for zbc_projects
  {
    ProjectKey = project_key;
    Name = name;
    ProjectManager = project_manager;
    ChangedBy = changed_by;
    ChangedAt = changed_at;
    CreatedBy = created_by;
    CreatedAt = created_at;
  }
}
(C) Brandeis Consulting📁

Übung 3 - 4. Schritt: Anlegen der Serivce Definition und des Service Bindings

Anlegen der Service Definition

Rechtsklick auf den CDS-View: => New Service Definition auswählen.

@EndUserText.label: 'Projects'
define service Z_Projects {
  expose zi_projects;
}

Anlegen des Service Bindings

Rechtsklick auf die Service Definition: => New Service Binding auswählen.
Binding Type OData V2 - UI auswählen:

Danach Aktivieren und Veröffentlichen.

Teste die Anwendung jetzt im Browser.

(C) Brandeis Consulting📁

Übung 3 - Auslagern der Annotationen in eine MetaData Extension

Rufe über das Kontextmenü den Punkt Source Code => Extract Metadata Extension auf und extrahiere die Annotationen aus deinem View.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projects'
@Metadata.allowExtensions: true
define root view entity zi_jb3_projects 
as 
select from zbc_projects {
                                         
    key project_key as ProjectKey,    
                            
    name as Name,
    project_manager as ProjectManager,  
    
    @Semantics.user.lastChangedBy: true
    changed_by as ChangedBy,
    
    @Semantics.systemDateTime.lastChangedAt: true
    changed_at as ChangedAt,
    
    @Semantics.user.createdBy: true
    created_by as CreatedBy,    
    
    @Semantics.systemDateTime.createdAt: true
    created_at as CreatedAt
}
@Metadata.layer: #CUSTOMER 
annotate view zi_jb3_projects with
{
  @UI.facet: [ {  id: 'Tasks',
                purpose: #HEADER ,
               type:     #IDENTIFICATION_REFERENCE,
               label: 'Status Value',
               position: 10 } ,

               {    id: 'Details',
                    purpose: #STANDARD ,
                    type:     #FIELDGROUP_REFERENCE,
                    label: 'Details',
                    position: 20 ,
                    targetQualifier: 'Details'}  ]
  @UI: { identification: [ { position: 10 } ],
         selectionField: [ { position: 10 } ] ,
         lineItem:       [ { position: 10 } ] }
  @UI.fieldGroup: [ { qualifier: 'Details' ,
                      position: 10 }]
  ProjectKey;
  @UI: { identification: [ { position: 20 } ],
         lineItem:       [ { position: 20 } ] }
  @UI.fieldGroup: [ { qualifier: 'Details' ,
                      position: 20 }]
  Name;
  @UI: {  lineItem:       [ { position: 30 } ] }
  @UI.fieldGroup: [ { qualifier: 'Details' ,
                      position: 30 }]
  ProjectManager;
  @UI: { identification: [ { position: 110 } ] }
  ChangedBy;
  @UI: { identification: [ { position: 120 } ] }
  ChangedAt;
  @UI: { identification: [ { position: 130 } ] }
  CreatedBy;
  @UI: { identification: [ { position: 150 } ] }
  CreatedAt;
}
(C) Brandeis Consulting📁

Übung 4 - Erweiterung der bisherigen Read-Only Anwendung

Texte für die Projekte

Damit wir in unserer bisherigen Anwendung auch passende Texte für die Projektkürzel bekommen, müssen wir den neuen Projekte-View als Textview einbinden.

  • Ergänze in dem Projekte View (ZI_<I>3_PROJECTS) die Annotation @Semantics.text: true für das Feld Name
  • Erstelle die Assoziation von dem Tasks View (ZI_<I>2_TASKS) zu dem Projekte View
  • Füge zu dem Feld Project die Annotation @ObjectModel.text.association: <Assoziationsname>

Projects View

...
define root view entity zi_jb3_projects 
as 
select from zbc_projects {
                                         
    key project_key as ProjectKey,    
       
    @Semantics.text: true                           
    name as Name,

...
}

Tasks View

define root view entity zi_jb2_tasks
  as select from zbc_tasks
  
  association [1] to ZI_JB2_USERS as _Assignee
  on $projection.Assignee =  _Assignee.UserId
  
  association [0..*] to zi_jb2_status_text as _StatusText
  on $projection.Status = _StatusText.Status
  
  association [1] to zi_jb3_projects as _Project
  on $projection.Project = _Project.ProjectKey
{
  key task_key    as TaskKey,
      summary     as Summary,
      
      @ObjectModel.text.association: '_StatusText'
      status      as Status,
      
      @ObjectModel.text.association: '_Project'
      project     as Project,
      description as Description,
(C) Brandeis Consulting📁

Übung 6 - Aufgabenverwaltung

In dieser Übung werden wir die vollständige Aufgabenverwaltung implentieren. Dabei ist es wichtig, dass man nach jedem Meilenstein die Anwendung gründlich testes, so dass die Anwendung noch funktioniert. Denn ansonsten sucht man später Probleme, die aus den vorausgegangenen Schritten resultieren.

Meilenstein 1 - Aufgaben und Kommentare

Wir erstellen als erstes eine Anwendung auf Basis der beiden Tabellen ZBC_TASKS und ZBC_COMMENTS. Die COMMENTS sind Unterobjekt des Geschäftsobjektes TASKS.

Hier ist die Liste der relevanten Objekte, die für den Meilenstein angelegt werden. In Klammern ist jeweils die Kopiervorlage auf EHX angegeben.

  • CDS Root View Entity ZI_<I>6_TASKS für die Tasks. (ZI_JB6_TASKS)
  • CDS View Entity ZI_<I>6_COMMENTS für die Kommentare. (ZI_JB6_COMMENTS)
  • Behavior Definition mit dem gleichen Namen wie die CDS Root View Entity für die Tasks
  • Metadata Extension, ebenfalls mit dem gleichen Namen wie die CDS Root View Entity
  • Service Definition Z_<I>6_TASKS
  • Service Binding ZUI_<I>6_TASKS
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 1 - CDS Root View Entity

Name: ZI_<I>6_TASKS

  • Typ ROOT VIEW ENTITY - Weil das das eigentliche BO wird.
  • Wir legen die Oberflächeannotationen in einer MetaData Extension an. Darum ist die Zeile 5 wichtig.
  • Die 3. Zeile mit der Suche erzeugt zunächst einen Fehler, weil die Suchfelder in der MetaData Extension definiert sind. Darum kann man die Zeile auch zunächst auskommentieren.
  • Die Assoziation zu dem Kommentaren wird als COMPOSITION modelliert. D.h. die Kommentare sind Bestandteil des BO Tasks.
  • Die COMPOSITION ist ein Henne-Ei Problem. TASKS-View und COMMENTS-View referenzieren sich gegenseitig. Entweder kommentiert man die entsprechenden Zeilen aus und aktiviert zunächst jedes Objekt seperat. Oder man bringt beide in einen sauberen Zustand und aktiviert gleichzeitig.

Quellcode ZI_<I>6_TASKS

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Tasks'
@Search.searchable: true 
@Metadata.allowExtensions: true
define root view entity zi_jb6_tasks as select from zbc_tasks
composition [0..*] of zi_jb6_comments as _Comments 

association [1..1] to zcds_users as _Assignee
on $projection.Assignee = _Assignee.UserId 
{
  
    key task_key as TaskKey,
    summary as Summary,
    status as Status,
    project as Project,
    description as Description,
    assignee as Assignee,
    type as Type,
    author as Author,
    changed_at as ChangedAt,    
    created_at as CreatedAt,  
    due_date as DueDate,   
    solution as Solution,
    priority as Priority, 
    product as Product,
    _Comments, // Make association public
    _Assignee
}
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 1 - CDS View Entity

Name: ZI_<I>6_COMMENTS

  • Die Annotationen stehen hier direkt im CDS View, weil das Objekt nicht komplexer wird.
  • Die Assoziation zu den Tasks ist vom Typ TO PARENT, weil die Kommentare ein Unter-Objekt sind.
  • Zum ersten Aktivieren kann man die Assoziation auskommentieren. Oder die Aktivierung muss gleichzeitig mit dem Tasks-View erfolgen.

Quellcode

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Comments'
define view entity zi_jb6_comments 
as 
select from zbc_comments
association to parent zi_jb6_tasks as _Tasks
    on $projection.TaskKey = _Tasks.TaskKey {
      @UI.facet: [  { id: 'Comment',
                  purpose:  #STANDARD,
                  type:     #IDENTIFICATION_REFERENCE,
                  label:    'Comment',
                  position: 10 } ]
                  
  @UI.identification: [{ position: 10 }]
  @UI.lineItem: [{ position: 10 }]
    key task_key as TaskKey,
    
  @UI.identification: [{ position: 20 }]
  @UI.lineItem: [{ position: 20 }]
    key comment_nr as CommentNr,
    
  @UI.identification: [{ position: 30 }]
  @UI.lineItem: [{ position: 30 }]
    comment_text as CommentText,
    
  @UI.identification: [{ position: 40 }]
  @UI.lineItem: [{ position: 40 }]
    author as Author,
    
  @UI.identification: [{ position: 50 }]
  @UI.lineItem: [{ position: 50 }]
    changed_at as ChangedAt,
    
  @UI.identification: [{ position: 60 }]
  @UI.lineItem: [{ position: 60 }]
    created_at as CreatedAt,
    _Tasks // Make association public
}

(C) Brandeis Consulting📁

Übung 6 - Meilenstein 1 - Metadata Extension

Gleicher Name wie die CDS Root View Entity

@Metadata.layer: #CUSTOMER
annotate view zi_jb6_tasks with
{
  @UI.facet: [  { id: 'Tasks',
                  purpose:  #STANDARD,
                  type:     #IDENTIFICATION_REFERENCE,
                  label:    'Task',
                  position: 10 },
                  
                 { id: 'Comments',
                 type: #LINEITEM_REFERENCE,
                 purpose: #STANDARD,
                 targetElement: '_Comments',
                 label: 'Comments',
                 position:  20 }  ]
                 
  @UI.identification: [{ position: 10 }]
  
  @UI.lineItem: [{ position: 10,
                   importance: #HIGH } ]
                   
  @UI.selectionField: [{ position: 10 }]
  TaskKey;
  @Search:{ defaultSearchElement: true,
            fuzzinessThreshold: 0.7 }
  @UI.identification: [{ position: 20 }]
  @UI.lineItem: [{ position: 20,
                         importance: #HIGH }]
  Summary;
  @UI.identification: [{ position: 30 }]
  @UI.lineItem: [{ position: 30 }]
  Status;
@UI.identification: [{ position: 40 }]
@UI.lineItem: [{ position: 40 }]
Project;
@Search:{ defaultSearchElement: true,
          fuzzinessThreshold: 0.7 }
@UI.identification: [{ position: 50 }]
Description;
@UI.identification: [{ position: 60 }]
@UI.lineItem: [{ position: 50 }]
Assignee;
@UI.identification: [{ position: 80 }]
@UI.lineItem: [{ position: 60 }]
Type;
@UI.identification: [{ position: 90 }]
Author;
@UI.identification: [{ position: 100 }]
ChangedAt;
@UI.identification: [{ position: 110 }]
CreatedAt;
@UI.identification: [{ position: 120 }]
@UI.lineItem: [{ position: 70 }]
DueDate;
@UI.identification: [{ position: 130 }]
Solution;
@UI.identification: [{ position: 140 }]
@UI.lineItem: [{ position: 80 }]
Priority;
@UI.identification: [{ position: 150 }]
Product;
@Search.defaultSearchElement: true
_Assignee;
}
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 1 - Behavior Definition

  • Die Behavior Definition bezieht sich auf die Tasks als ROOT Objekt. Sie enthält aber auch das Verhalten der Kommentare.
  • Wenn man das Behavior erzeugt, muss das Mapping für TASKS und COMMENTS von Hand hinzugefügt werden.
  • Die Implementierungsklasse (1. Zeile) muss angelegt werden. Das erfolgt mit einem Quick-Fix.

Quellcode

managed implementation in class zbp_i_jb6_tasks unique;
strict;

define behavior for zi_jb6_tasks //alias <alias_name>
persistent table zbc_tasks
lock master
authorization master ( instance )
//etag master <field_name>
{
  create;
  update;
  //delete;
  //action action_delete_task;
  association _Comments { create; }
  mapping for zbc_tasks corresponding
  {
    TaskKey = task_key;
    ChangedAt = changed_at;
    CreatedAt = created_at;
    DueDate = due_date;
  }
}

define behavior for zi_jb6_comments //alias <alias_name>
persistent table zbc_comments
lock dependent by _Tasks
authorization dependent by _Tasks
//etag master <field_name>
{
  update;
  delete;
  field ( readonly ) TaskKey;
  association _Tasks;
  mapping for zbc_comments corresponding
  {
    TaskKey = task_key;
    CommentNr = comment_nr;
    CommentText = comment_text;
    ChangedAt = changed_at;
    CreatedAt = created_at;
  }
}
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 1 - Service Definition und Service Binding

Name der Service Definition: Z_<I>6_TASKS
Name des Service Bindings: ZUI_<I>6_TASKS

Achte beim Service darauf, dass alle Entities exponiert werden:

@EndUserText.label: 'Tasks'
define service Z_jb6_tasks {
  expose zi_jb6_tasks;
  expose zi_jb6_comments;
  expose zcds_users;
}
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 1 - Testen

Die Vorschau sollte uns eine Anwendung zeigen, mit der wir schon arbeiten können. Bitte teste die folgenden Testfälle durch, bevor Du mit dem nächsten Meilenstein weiter machst:

  • Auf der List-Page nach Tasks suchen können. Sind die beiden Suchfelder da?
  • Funktionieren die Suchfelder? Probiere beide mal aus.
  • Funktioniert die Navigation auf die Object-Page einer Aufgabe?
  • Auf der List-Page neue Aufgaben anlegen können. Hierbei müssen wir den TaskKey von Hand vergeben.
  • Auf der Object-Page sollten die Kommentare angezeigt werden. Falls für eine Aufgabe noch keine vorhanden sind, können wir diese aber anlegen.
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 2: Aktionen hinzufügen

In diesem Meilenstein wollen wir eine Aktion action_delete_task hinzufügen. Diese Aktion soll, anstatt die Aufgabe echt zu löschen, lediglich den Status auf "DELETED" setzen und den Bearbeiter auf den aktuellen Benutzer setzen.

Dazu müssen wir die folgenden Objekte bearbeiten:

  • Die Behavior Definition
  • Die Implementierungsklasse
  • Die MetaData Extensions für die Tasks
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 2 - Behavior Definition

In der Behavior Definition wird die Zeile
action action_delete_task;
hinzugefügt und die Zeile mit der originalen delete Aktion auskommentiert, falls diese noch aktiv ist.

managed implementation in class zbp_i_jb6_tasks unique;
strict;

define behavior for zi_jb6_tasks //alias <alias_name>
persistent table zbc_tasks
lock master
authorization master ( instance )
//etag master <field_name>
{
  create;
  update;
  //delete;
  action action_delete_task;
  association _Comments { create; }
  mapping for zbc_tasks corresponding
  {
    TaskKey = task_key;
    ChangedAt = changed_at;
    CreatedAt = created_at;
    DueDate = due_date;
  }
}

...
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 2 - Implementierungsklasse

Nach dem die Aktion in der Behavior Definition definiert wurde, muss diese auch in der Implementierungsklasse definiert werden. Dafür gibt es einen Quick-Fix in der Behavior, wenn die Warnung angezeigt wird, dass die Implentierung fehlt.

CLASS lhc_zi_jb6_tasks DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
      IMPORTING keys REQUEST requested_authorizations FOR zi_jb6_tasks RESULT result.
    METHODS action_delete_task FOR MODIFY
      IMPORTING keys FOR ACTION zi_jb6_tasks~action_delete_task.


ENDCLASS.

CLASS lhc_zi_jb6_tasks IMPLEMENTATION.

  METHOD get_instance_authorizations.
  ENDMETHOD.


  METHOD action_delete_task.

    modify entities of zi_jb6_tasks in local mode
      entity zi_jb6_tasks
     update FIELDS ( Status
                     assignee )
     with value #( for ls_key in keys
                 ( %tky = ls_key-%tky
                    status = 'DELETED'
                    assignee = sy-uname ) ).

    reported-zi_jb6_tasks =
        value #( ( %msg = new zbc_message( 
          |Der Status der Aufgabe wurde auf "DELETED" gesetzt. | ) ) ).


  ENDMETHOD.

ENDCLASS.
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 2 - MetaData Extensions für die Tasks

In der MetaData Extension wird die neue Aktion per Annotationen auf der Oberfläche an zwei Stellen sichtbar gemacht:

  • In der Liste in der Annotation @UI.lineItem
  • Auf der Object-Page mit der Annotation @UI.identification
@Metadata.layer: #CUSTOMER
annotate view zi_jb6_tasks with
{
  @UI.facet: [  { id: 'Tasks',
                  purpose:  #STANDARD,
                  type:     #IDENTIFICATION_REFERENCE,
                  label:    'Task',
                  position: 10 },
                  
                 { id: 'Comments',
                 type: #LINEITEM_REFERENCE,
                 purpose: #STANDARD,
                 targetElement: '_Comments',
                 label: 'Comments',
                 position:  20 }  ]
                 
  @UI.identification: [{ position: 10 },
                       { type: #FOR_ACTION,
                         dataAction: 'action_delete_task' , 
                         label: 'Set Task to  DELETED' }]
  
  @UI.lineItem: [{ position: 10,
                   importance: #HIGH } , 
                                      
                 { type: #FOR_ACTION, 
                   dataAction : 'action_delete_task' , 
                         label: 'Set Task to  DELETED' } 
                   ]
...
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 2 - Testfälle

Nach den Änderungen bitte die folgenden Situationen testen:

  • Ist der Button "Set Task to DELETED" in der List-Page und der Object-Page sichtbar?
  • An beiden Positoinen testen, ob die Wirkung des Buttons korrekt ist:
    • Wird der Bearbeiter auf den aktuellen Benutzer gesetzt?
    • Wird der Status auf "DELETED" gesetzt?
    • Erscheint eine Meldung?
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 3 - Validierung anlegen

Anforderung

Es soll sichergestellt werden, dass die Statuswerte auch tatsächlich existieren. D.h. wir machen einen Lookup auf die entsprechende Tabelle und prüfen das.

Implementierung

Wir müssen die folgenden Objekte anpassen:

  • Behavior Definition - Deklaration der Validierung
  • ABAP Implementation Class - Impelementierung der Prüfung
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 3 - Behavior Definition

  • Unsere neue Prüfung muss definiert werden...

Quellcode

managed implementation in class zbp_i_jb6_tasks unique;
strict;

define behavior for zi_jb6_tasks //alias <alias_name>
persistent table zbc_tasks
lock master
authorization master ( instance )
//etag master <field_name>
{
  create;
  update;
  //delete;
  action action_delete_task;
  association _Comments { create; }

//Milestone 3:
  validation validate_status on save { create;
                                        update;
                                        field Status ;}

  mapping for zbc_tasks corresponding
  {
    TaskKey = task_key;
    ChangedAt = changed_at;
    CreatedAt = created_at;
    DueDate = due_date;
  }
}
...
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 3 - ABAP Implementation Class

  • Die Customizinttabelle ZBC_STATUS_TEXT enthält alle gültige Werte.

 METHOD validate_status.

    SELECT DISTINCT status
      FROM zbc_statust
      INTO TABLE @DATA(status).

    READ ENTITIES OF zi_jb6_tasks IN LOCAL MODE
      ENTITY zi_jb6_tasks
        FIELDS ( status ) "the field to validate
        WITH CORRESPONDING #( keys )
      RESULT DATA(tasks).


    LOOP AT tasks INTO DATA(task) .
      IF NOT line_exists( status[ status = task-status ] ).

        APPEND VALUE #( %tky = task-%tky ) TO failed-zi_jb6_tasks.

        APPEND VALUE #( %tky = task-%tky
                        %msg = NEW zbc_rap_message(
                           msgv1 = |Status "{ task-status }" invalid|
                           textid = zbc_rap_message=>validation_failed
                           severity = if_abap_behv_message=>severity-error )
                         %element-status = if_abap_behv=>mk-on
                      ) TO reported-zi_jb6_tasks.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

(C) Brandeis Consulting📁

Übung 6 - Meilenstein 3 - Testfälle

Versuche eine Aufgabe so zu ändern, dass

  • Der Wert in der Tabelle ZBC_STATUST vorkommt - Es sollte keine Meldung beim Speichern erscheinen
  • Der Wert unbekannt ist. Dann sollte die passende Meldung erscheinen.
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 4 - Feature control

Anforderung

Die Aktion action_delete_task sollte nur für Aufgaben verfügbar sein, die noch nicht im Status DELETED sind.

Implementierung

Wir müssen die folgenden Objekte anpassen:

  • Behavior Definition - Deklaration der Validierung
  • ABAP Implementation Class - Impelementierung der Prüfung
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 4 - Behavior Definition

  • Wir ergänzen die Definition der Aktion action_delete_task wie folgt:
    action (features : instance) action_delete_task;
  • danach muss die entsprechende Methode per Quick-Fix angelegt werden:

managed implementation in class zbp_i_jb6_tasks unique;
strict;

define behavior for zi_jb6_tasks //alias <alias_name>
persistent table zbc_tasks
lock master
authorization master ( instance )
//etag master <field_name>
{
  create;
  update;
  //delete;
  
//Milestone 4:
  action (features : instance) action_delete_task;
  association _Comments { create; }

//Milestone 3:
  validation validate_status on save { create;
                                        update;
                                        field Status ;}

  mapping for zbc_tasks corresponding
  {
    TaskKey = task_key;
    ChangedAt = changed_at;
    CreatedAt = created_at;
    DueDate = due_date;
  }
}
(C) Brandeis Consulting📁

Übung 6 - Meilenstein 4 - ABAP Implementation Class

  • Lesen der Daten für die Entität aus dem Transaktionspuffer. Die Selektion der Daten mit dem CORRESPONDING Operator ist sehr elegant....
  • Erzeugen des Rückgabewertes RESULT. Beachte, dass die Struktur %action sich je nach Features des Behaviors verändert.
  METHOD get_instance_features.

    read entities of zi_jb6_tasks in local mode
        entity zi_jb6_tasks
        fields ( status ) with corresponding #( keys )
        result data(tasks).

    result = value #( for ls_task in tasks
                      ( %tky = ls_task-%tky
                        %action-action_delete_task =
                           switch #(  ls_task-Status when 'DELETED'
                                       THEN if_abap_behv=>fc-o-disabled
                                       ELSE if_abap_behv=>fc-o-enabled  ) ) ).

  ENDMETHOD.
(C) Brandeis Consulting📁