Skip to content

Lesson 20 — RAP & CDS Extensibility ​

Why are we learning this? ​

A factory uses an application to record inspections of equipment. Each ticket has a priority: H (high) or N (normal). The factory now wants a more readable label, such as Urgent, and wants the application to reject invalid priorities such as X.

The original application might be maintained by another team or by SAP. Modifying the original source for every customer-specific request creates maintenance and upgrade risks. Extensibility means the original application's owner deliberately permits certain enhancements, and the extending team adds them through separate repository objects.

In this lesson we create a small application ourselves, then pretend its original code belongs to another team. We add a new CDS field and a new RAP validation without declaring either enhancement in the original view or behavior body.

text
Inspection Ticket application
       |
       +-- CDS extension
       |     -> display "Urgent" or "Routine"
       |
       +-- RAP behavior extension
             -> reject invalid priority on save

The core idea: the original business object stays the same; permitted extensions change what it can show and enforce.

What you will build ​

This lesson has an independent mini-project. It does not require the Reservation, Support Issue, Online Order, or Checkpoint 4 objects.

Teacher-led work demonstrates an Equipment Inspection BO. Student practice repeats the same techniques in a Calibration Request BO with different rules.

By the end, students can distinguish:

  • a provider from an extender;
  • CDS data-model extension from RAP behavior extension;
  • a calculated CDS field from a stored database field;
  • an extension contract from a released consumption API;
  • on-stack SAP S/4HANA developer extensions from BTP side-by-side extensions.

Format: teacher-led implementation plus independent practice. No chapter-level time estimates.

Replace ### with your three-digit student suffix. Keep the literal field suffix Z20 exactly as shown.

Object map ​

Provider objectsExtension objects
Table ZINSP_###CDS extension ZX_INSP_V_###
Root CDS ZR_INSP_###BDEF extension ZX_INSP_B_###
Base BDEF + ZBP_R_INSP_###Extension behavior pool ZBP_X_INSP_###
EML test class ZCL_INSP_TEST_###

1. Create the package ​

In SAP BTP ABAP Trial using Eclipse ADT, create package ZPKG_L20_### under your permitted course superpackage (often ZLOCAL in BTP ABAP Trial). The provider and extension objects may be placed in separate subpackages to show ownership, but this is not necessary for the syntax.

2. Create the provider table ​

Create a source-based Database Table ZINSP_### and activate:

abap
@EndUserText.label : 'Equipment Inspection Tickets'
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zinsp_### {
  key client                : abap.clnt not null;
  key uuid                  : sysuuid_x16 not null;
      asset_code            : abap.char(20);
      priority              : abap.char(1);
      inspection_note       : abap.char(120);
      local_last_changed_at : abp_locinst_lastchange_tstmpl;
}

The table initially has no rows. #EXTENSIBLE_ANY enables supported table extensions, but we do not append a stored field in today's guided path. Our new label will be computed in CDS.

3. Create the root CDS ​

Create a Data Definition ZR_INSP_###:

abap
@EndUserText.label: 'Inspection Root'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@AbapCatalog.viewEnhancementCategory: [#PROJECTION_LIST]
@AbapCatalog.extensibility: {
  extensible: true,
  elementSuffix: 'Z20',
  allowNewDatasources: false,
  dataSources: ['Ticket'],
  quota: { maximumFields: 10, maximumBytes: 1000 }
}
define root view entity ZR_Insp_###
  as select from zinsp_### as Ticket
{
  key Ticket.uuid                  as Uuid,
      Ticket.asset_code            as AssetCode,
      Ticket.priority              as Priority,
      Ticket.inspection_note       as InspectionNote,

      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      Ticket.local_last_changed_at as LocalLastChangedAt
}

Activate and open Data Preview. You should see the base fields only.

Why are these annotations here? The original CDS owner is explicitly allowing extra fields in the select list. The extender may refer to the existing data-source alias Ticket, must end new element names in Z20, and may not introduce an additional data source in this exercise. These are limits chosen by the provider.

4. Create the managed BDEF ​

Create a Managed Behavior Definition for ZR_INSP_###:

abap
managed implementation in class zbp_r_insp_### unique;
strict ( 2 );

extensible
{
  with validations on save;
}

define behavior for ZR_Insp_### alias Inspection
persistent table zinsp_###
lock master
authorization master ( none )
etag master LocalLastChangedAt
extensible
{
  create;
  update;
  delete;

  field ( numbering : managed, readonly ) Uuid;
  field ( readonly ) LocalLastChangedAt;

  mapping for zinsp_### corresponding extensible
  {
    Uuid               = uuid;
    AssetCode          = asset_code;
    Priority           = priority;
    InspectionNote     = inspection_note;
    LocalLastChangedAt = local_last_changed_at;
  }
}

Use ADT Quick Fix (Ctrl+1) to generate ZBP_R_INSP_###. Standard managed create/update/delete needs no manually implemented provider handler.

Checkpoint: activate the provider first ​

Before moving on, the table, root CDS, base BDEF, and generated behavior pool must activate. The original BDEF permits validation extensions via extensible { with validations on save; }, but it contains no validation itself.


Part A — The CDS Extension ​

5. New request: show a friendly priority ​

The supervisor does not want to interpret code H and N in reports. They want:

text
H -> Urgent
N -> Routine

This is a display/read model requirement, not new persistent business data. We can derive it in CDS without adding a table column.

6. Create a separate Data Definition ​

Right-click ZR_INSP_### in ADT and choose New Data Definition using the Extend View Entity template. Name it ZX_INSP_V_###:

abap
extend view entity ZR_Insp_### with
{
  @EndUserText.label: 'Priority Label'
  case
    when Ticket.priority = 'H'
      then cast( 'Urgent' as abap.char(12) )
    else cast( 'Routine' as abap.char(12) )
  end as ZZPriorityLabelZ20
}

Activate the extension. Do not insert this CASE expression into the original ZR_INSP_### source.

If an error occurs, check the provider's elementSuffix, dataSources, and enhancement category. Those contracts explain which extensions are legal.

7. Verify the read-model change ​

Open Data Preview on ZR_INSP_###. You should see a new field:

text
ZZPriorityLabelZ20

It appears even though there is no zz_priority_label database column. A CDS calculation happens when data is read.

At this point Data Preview may still have zero rows. We'll create one after implementing the RAP validation.

Checkpoint: inspect ownership ​

  • ZR_INSP_### source contains its original select list.
  • ZX_INSP_V_### contains the added CASE expression.
  • Data Preview for ZR_INSP_### includes both base and extension fields.

That is a real CDS view extension.


Part B — The RAP Behavior Extension ​

8. New request: prevent invalid priority ​

The CDS CASE expression would display Routine even for invalid input X. It does not enforce a business rule.

The factory now requires:

text
Allowed: H, N
Rejected: X, blank, other values

A RAP validation is the correct mechanism to protect writes made through this BO.

9. Create a separate behavior extension ​

Right-click the base BDEF -> New Behavior Extension. Name it ZX_INSP_B_###. In this self-owned BO exercise, leave the optional BO Interface field empty.

abap
extension implementation in class zbp_x_insp_### unique;

extend behavior for Inspection
{
  validation checkPriority on save
  {
    create;
    field Priority;
  }
}

This statement extends the entity alias Inspection. It does not create another standalone BO.

The behavior pool ZBP_X_INSP_### belongs to the extension. It must not be added to the original provider behavior pool.

10. Generate the implementation ​

Use Ctrl+1 to generate the extension behavior pool, then generate the handler method checkPriority. Let ADT create the local class and declaration; the method signature is equivalent to:

abap
METHODS checkPriority FOR VALIDATE ON SAVE
  IMPORTING keys FOR Inspection~checkPriority.

Put the next code inside the generated method, not in a console class.

11. Implement the validation ​

abap
METHOD checkPriority.

  READ ENTITIES OF ZR_Insp_### IN LOCAL MODE
    ENTITY Inspection
    FIELDS ( Priority )
    WITH CORRESPONDING #( keys )
    RESULT DATA(inspections).

  LOOP AT inspections INTO DATA(inspection).

    IF inspection-Priority <> 'H'
       AND inspection-Priority <> 'N'.

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

      APPEND VALUE #(
        %tky = inspection-%tky
        %msg = new_message_with_text(
          severity = if_abap_behv_message=>severity-error
          text     = 'Priority must be H or N'
        )
        %element-Priority = if_abap_behv=>mk-on
      ) TO reported-inspection.

    ENDIF.

  ENDLOOP.

ENDMETHOD.

Why IN LOCAL MODE? READ ENTITIES already reads the current RAP transactional state, including buffered changes. Local mode controls which checks are bypassed; it is not what gives EML access to the buffer. In a behavior extension, it bypasses only the extension's own feature-control and authorization implementations. The original BO's controls still apply. External console callers use normal EML without IN LOCAL MODE.

Why FAILED and REPORTED? FAILED rejects the invalid entity; REPORTED explains the failure to the caller.

Activate the BDEF extension and its generated behavior pool. If the validation is rejected in the extension, confirm that the base BDEF explicitly allows with validations on save;.


Part C — End-to-End Verification ​

12. Create a valid instance using EML ​

Create a console class ZCL_INSP_TEST_###, implement IF_OO_ADT_CLASSRUN, and put this code in if_oo_adt_classrun~main:

abap
MODIFY ENTITIES OF ZR_Insp_###
  ENTITY Inspection
  CREATE FIELDS ( AssetCode Priority InspectionNote )
  WITH VALUE #(
    ( %cid = 'V1'
      AssetCode = 'PUMP-20'
      Priority = 'H'
      InspectionNote = 'Inspect seal' )
  )
  MAPPED DATA(mapped)
  FAILED DATA(failed)
  REPORTED DATA(reported).

IF failed IS INITIAL.
  COMMIT ENTITIES RESPONSE OF ZR_Insp_###
    FAILED DATA(save_failed)
    REPORTED DATA(save_reported).

  out->write( save_failed ).
  out->write( save_reported ).
ELSE.
  out->write( failed ).
  out->write( reported ).
  ROLLBACK ENTITIES.
ENDIF.

Run with F9. The commit should succeed for H. Open Data Preview on ZR_INSP_###:

text
AssetCode          PUMP-20
Priority           H
ZZPriorityLabelZ20 Urgent

Repeating the test creates more rows with new managed UUID keys. That is acceptable in the student's own table.

13. Try a bad value ​

Run the same EML creation with %cid = 'X1', an unambiguous different AssetCode, and Priority = 'X'.

Expected behavior:

  • MODIFY ENTITIES may initially accept the requested change;
  • the on-save validation executes during COMMIT ENTITIES;
  • commit returns a failed entry and error message;
  • no new active row with priority X is persisted.

For this negative test inspect:

abap
COMMIT ENTITIES RESPONSE OF ZR_Insp_###
  FAILED DATA(save_failed)
  REPORTED DATA(save_reported).

out->write( save_failed ).
out->write( save_reported ).

IF save_failed IS NOT INITIAL.
  ROLLBACK ENTITIES.
ENDIF.

After a failed commit, clear the rejected buffer with ROLLBACK ENTITIES before starting another test. For clarity, run positive and negative tests as separate class runs.

14. Prove the original application was not changed ​

Compare:

Original providerNew extension
ZR_INSP_### lacks the calculated field declarationZX_INSP_V_### adds it
Base BDEF lacks checkPriorityZX_INSP_B_### declares it
ZBP_R_INSP_### provides managed CRUDZBP_X_INSP_### implements the new validation

The original provider was enabled for extension when it was built. The enhancement logic itself is separate.


Part D — SAP S/4HANA Context ​

So far, we have built and extended an application that we own. Now imagine the factory already uses an application supplied by SAP and asks us to add similar features. Before writing code, we need to understand who controls that application and which changes it supports.

SAP S/4HANA is business software for processes such as purchasing, sales, finance, and maintenance. SAP BTP is a platform where organizations can build and run additional applications. Our ABAP Trial is on BTP. In this section, we connect the techniques we practiced to these real project settings.

15. From our own BO to an SAP-owned application ​

We played two roles in the lab ​

Recall the order in which we built the inspection application:

Our roleWhat we didWhat that allowed
Provider: owner of the original BOAdded the CDS extensibility annotations and BDEF extensible declarationsOther objects could add permitted fields and validations
Extender: developer of the enhancementCreated ZX_INSP_V_### and ZX_INSP_B_###Added a label and a validation through those permissions

An extension point is a place the provider has deliberately made available for enhancements. In our lab, the CDS select list and the BDEF's permission for on-save validations are examples. The provider also sets limits: our field names must end in Z20, and our CDS extension cannot introduce another data source.

When SAP owns the original BO, SAP makes those decisions. We first look for a supported extension point that matches the requirement. Being able to see an object's source in ADT does not by itself mean it is available for customer extension.

Why a release contract matters ​

Imagine another team uses our Ticket alias in its CDS extension. If we rename that alias later, their extension could stop activating. Separate extension files help organize ownership, but the provider must also preserve the parts that extenders depend on.

A release contract records a supported way to use a development object and the stability commitment for that use. Here, released means the owner has made the object available for a specified purpose. Activating our lab objects does not give them a formal release contract. SAP uses release contracts to identify supported APIs and extension points. SAP reference: ABAP release contracts

An API (application programming interface) is an interface that other code can use. In ABAP, this can be a class you call, a CDS entity you read, or a BO interface you access through EML. Consuming an API means using the data or operations it already offers. Extending an object means adding something through an extension point.

SAP gives these two purposes short contract names:

ContractPlain-language meaningExample of the intended use
C0 — ExtendThe object offers a stable extension point, within its declared limitsAdd a field through a permitted CDS extension
C1 — Use System-InternallyOther code in the same ABAP system can use the released interfaceRead a released CDS entity or call a released class

A C1 release alone does not allow you to add fields or behavior to that object. A C0 release still requires you to respect the specific extension permissions. These are different supported uses, rather than levels where one includes the other. SAP references: C0 and C1.

For an SAP-delivered object, inspect its API state in ADT and its documentation: check that it is released for the intended use in ABAP Cloud and that the extension point permits the enhancement you need.

Connection to our lab: we created the extension permissions ourselves. We have not formally released our objects with C0 or C1. The exercise teaches how separate extensions work; Lesson 21 develops the released-API topic further.

16. Where does an S/4HANA extension run? ​

Release contracts describe how an object may be used. A separate design decision is where the enhancement runs and how it is built.

On-stack means the enhancement runs inside the S/4HANA system. Both key-user and developer extensibility can work there. Side-by-side means an additional application runs separately, for example on BTP, and communicates with S/4HANA through supported remote interfaces. A remote API lets one system request data or operations from another. SAP reference: extensibility options.

Suppose the factory asks for more inspection functionality. Depending on what its S/4HANA application supports, these approaches could fit:

ApproachHow the enhancement is builtExample requirement
Key-user — on-stackA business expert uses provided apps such as Custom Fields and Custom LogicAdd an inspector name through a supported custom-field option
Developer — on-stackAn ABAP developer uses ADT and released APIs or extension points inside S/4HANAAdd a validation through a BO extension point that permits it
Side-by-side — on BTPA developer builds a separate application connected to S/4HANA through released remote APIs or eventsBuild an inspection portal that retrieves equipment data from S/4HANA

Our CDS and BDEF extensions practice techniques used in developer extensibility. They extend our own BO in the Trial system. Merely running on BTP does not make this lab an S/4HANA side-by-side integration: we have not connected it to S/4HANA. That requires an accessible remote API and a configured connection, which we revisit in Lesson 23's final Stock Check project, connection permitting.

Also keep the API distinction clear: C1 describes use within an ABAP system. It does not by itself make an object available as a remote API for a BTP application.

17. What changes if the new field must be stored? ​

Our ZZPriorityLabelZ20 is calculated from Priority. Whenever CDS reads H, it can produce Urgent again. There is no extra value to save.

Now suppose the factory asks inspectors to enter a new InspectorName, such as “Alex Silva.” That name cannot be derived from the priority. It must survive saving and reopening the ticket, so it needs database storage.

In a developer extension, follow the value through the application:

  1. Store it. Use a permitted table extension. An append structure adds fields through a separate object; an extension include is a structure the provider has designated for such additions.
  2. Expose it in CDS. Add the stored field to the data model through a CDS extension.
  3. Let RAP save it. Check the behavior mapping between the CDS field and storage, and any field rules such as whether the field is editable.
  4. Show it to users. If the BO has an OData UI, check the projection that exposes fields and the metadata that controls their display.
  5. Support draft, if used. A draft-enabled BO must also retain the inspector name while the user edits an unsaved draft.

Finally, test that the inspector name survives saving and reopening, and that the expected access controls still apply. These steps explain why a stored custom field needs more work than our calculated label. The Companion describes the additional layers; this guided lab ends with the calculated field and validation.

Check your understanding ​

Explain these distinctions in your own words:

  • Why a C1 release alone does not establish permission to extend a CDS view.
  • What a BTP application would need to become an S/4HANA side-by-side integration.
  • Why InspectorName needs storage while ZZPriorityLabelZ20 can be calculated.

18. Common classroom mistakes ​

SymptomWhat to inspect
CDS extension will not activateextensible, elementSuffix, dataSources, enhancement category
New field missingActivate extension; preview root CDS, not table
Validation cannot be declaredBase BDEF extensible and with validations on save;
Method missingGenerate handler method in extension pool via Quick Fix
Invalid X appears acceptedDid you check COMMIT response, not only MODIFY?
No error displayedFill both failed-inspection and reported-inspection
UI does not show the CDS fieldThis lesson tests with Data Preview; projections/metadata require additional extension steps

19. Completion checklist ​

20. Official technical references ​

Verification boundary: These patterns have been checked against current SAP documentation, but not compiled in your cohort's Trial tenant. Stop at the activation checkpoints and resolve any ADT/tooling variation rather than proceeding with a partially active BO.