Setting Values Using Numbering and Determinations

Objectives

After completing this lesson, you will be able to:
  • Describe the numbering concepts.
  • Define and implement determinations.

Standard Operation CREATE

Standard operation CREATE

To enable standard operation create for an entity, add the statement create; to the entity behavior body (curly brackets after statement define behavior for …). To disable the operation, remove the statement or turn it into a comment.

Note

In a managed implementation scenario, create can only be declared for root entities. Child entities are implicitly create-enabled for internal usage. That means, an external consumer can only create a new instance of a child entity via its parent (create-by-association operation). In unmanaged implementation scenarios, direct creates on child entities are possible but not recommended.

Interesting additions to the create statement are as follows:

Internal

Prefix which disables the operation for external consumers of the BO.

features: global

Enable dynamic feature control. The decision, when the operation is enabled, is made dynamically in a behavior implementation.

precheck

A method is called before the create request to prevent unwanted changes from reaching the application buffer.

authorization: none

Excludes the create operation from the global authorization checks.

CREATE in behavior projection

To make standard operation create available in OData and SAP Fiori, it has to be exposed in the behavior definition on projection level (behavior projection).

To include standard operation create in the OData service, add the statement use create; to the entity behavior body (curly brackets after statement define behavior for …). To disable the operation, remove the statement or turn it into a comment.

Note

Behavior projections can have their own implementations, which can be used to augment the implementation of a standard operation or provide more prechecks. This is a special case that we don't cover in this class.

When the standard operation Create is available in the OData service, the service preview displays a Create button on the List Report Page for the related RAP BO entity. By choosing this button, the user navigates to an Object Page for a new entity instance.

Hint

After editing the behavior projection, you can press Ctrl + F5 to perform a hard refresh of the service preview before you can see the new button. Sometimes, you even have to clear your browsing data. (Press Ctrl + Shift + Del for that).

Numbering

A comparison between Primary Key in general and Primary Key used in RAP BOs

The primary key of a business object entity can be composed of one or more key fields, which are identified by the keyword key  in the underlying CDS view of the business object. The set of primary key fields uniquely identifies each instance of a business object entity.

The logic of the business object has to ensure that all entity instances are created with a unique set of primary key values and that the primary key values of an existing instance cannot be changed during the update operation.

The editing is easily prevented by listing the key fields after the field (readonly ) statement in the behavior definition body. To only prevent editing during the operation update, while still allowing the consumer to provide values during operation create, the keyword field ( readonly: update ) is used instead.

The process of providing a unique key during the creation of a new instance is called numbering.

There are various options to handle the numbering for primary key fields depending on when (early or late during the transactional processing) and by whom (consumer, application developer, or framework) the primary key values are set. You can assign a numbering type for each primary key field separately. The following options are available:

Distinction between external numbering, internal numbering and optional external numbering

Note the following distinctions:

External vs Internal

In External Numbering, the consumer hands over the primary key values for the CREATE operation, just like the values for the non key fields. The runtime framework (managed or unmanaged) takes over the value and processes it until finally writing it to the database. In this scenario, the behavior implementation has to ensure that the primary key value provided by the consumer is unique, for example by executing suitable validations. This is opposed to Internal Numbering where the key values are provided by the RAP BO logic. Here, validations are not needed, if the numbering logic ensures the uniqueness. Optional External Numbering is a combination of external and internal numbering: The RAP BO logic only provides key values in case the consumer hands over initial values.

Managed vs Unmanaged

Internal numbering can either be managed or unmanaged. In Managed Internal Numbering, the unique key is drawn automatically during the CREATE request by the RAP-managed runtime. This is opposed to Unmanaged Internal Numbering, where the key values are provided through a dedicated handler method, implemented by the application developer.

Note

Managed Internal Numbering is only supported in managed implementation scenarios and when the primary key is a single UUID-based field.
Early vs Late

Unmanaged internal numbering can be either early or late. In Early Numbering, the final key value is available in the transactional buffer directly after the MODIFY request for CREATE. This is opposed to Late Numbering, where the final number is only assigned just before the instance is saved on the database. Late numbering is used for scenarios that need gap-free numbers. As the final value is only set just before the SAVE, everything is checked before the number is assigned.

Note

External numbering and managed internal numbering are always early by design.
An example of external numbering

In External numbering, the consumer hands over the primary key values for the CREATE operation, just like the values for non key fields.

To ensure that the consumer provides the primary key during a create operation, but is not able to change it later during update, list the primary key fields in the entity behavior body afterfield (readonly: update, mandatory: create ). In an Optional External Numbering scenario, when there is an additional internal numbering logic in place, omit the mandatory: create option.

In external numbering scenarios, validations and pessimistic concurrency control are essential to avoid duplicate key errors during the save phase.

Managed internal numbering

To enable managed internal numbering, you have to list the key field after the field( numbering: managed )keyword in the entity behavior body. On creation of a new entity instance, a unique value is assigned to this field automatically. No implementation in the ABAP behavior pool is required.

The following prerequisites exist:

  • Only available in managed implementation scenarios
  • Technical type of the key field: RAW(16).

In addition, the field must be defined as readonly or readonly: update.

Unmanaged Numbering

Definition of early numbering

The early numbering addition of the define behavior for statement defines unmanaged early numbering for all primary key fields of the RAP BO entity. This addition requires the definition and implementation of a handler method with the for numbering addition in the local handler class of the BO entity. The affected key fields must be specified as readonly or as readonly: modify – in case of optional external numbering.

This numbering technique is called early numbering, because the key value for an instance is available in the transactional buffer instantly after the MODIFY request for CREATE.

Note

If a business object consists of several entities, unmanaged early numbering is defined for each entity separately. That means that some BO entities can use unmanaged early numbering, while others use different methods to fill the primary key fields.

Usage of quick fix to create the numbering handler method

You can use a quick fix to add the missing method to the local handler class. To invoke the quick fix, the usual options are available: Choose the warning icon with light bulb, right-click the create keyword and choose Quick Fix, or place the cursor on the create keyword and press Ctrl + 1.

Implementation of early numbering

Early numbering is implemented in a handler method that is defined with the addition FOR NUMBERING. The importing parameter is named entities and contains the newly created entity instances, that is, the instances for which the framework requires key values.

Although not visible in the method definition, the method has three changing parameters: failed, reported, and mapped. The purpose of failed and reported is the same as in validations. The mapped parameter is used to return the assigned primary key values.

Like the other response parameters, mapped is a deep structure with table-like components. There is one component for each entity of the RAP BO. If the behavior definition defines an entity alias, this alias is used as a component name.

The line type of the components of mapped consist of the primary key fields of the entity plus the generic %cid field. The value of %cid is used to identify the instance of the entity. The value must be taken from the corresponding column in the entities parameter.

Caution

When you implement early numbering, it is mandatory that for each %cid value in entities you return a corresponding entry in either failed or mapped. If you don't, the framework raises a runtime error.

Definition of late numbering

The late numbering addition of the define behavior for statement defines unmanaged late numbering for all primary key fields of the RAP BO entity. This addition requires the redefinition and implementation of the adjust_numbers method in the local saver class for the RAP BO.

The affected key fields must be specified as readonly or, in case of optional external numbering, as readonly: modify.

This numbering technique is called late numbering, because the key values are assigned just before the instance is saved on the database. Thus, a gap-less assignment of unique keys is ensured.

Note

If a business object consists of several entities, and a parent entity defines late numbering, then all its child entities must also define late numbering.

Ways to create the ADJUST_NUMBERS Method

You can use a quick fix to add the missing method to the local saver class. If the behavior pool does not yet contain a local saver class, it is generated by the quick fix. To invoke the quick fix, the usual options are available: Choose the warning icon with light bulb, right-click the late keyword and choose Quick Fix, or place the cursor on the late keyword and press Ctrl + 1.

Implementation of late numbering

Late numbering is implemented in the saver class, that is, in a local class that inherits from the global class CL_ABAP_BEHAVIOR_SAVER. This local saver class is not related to a single BO entity but to the business object as a whole.

Note

We learn more about the saver class in the context of unmanaged scenarios.

For late numbering, you redefine and implement the inherited adjust_numbers method. The method has a mapped and a reported parameter.

Note

There is no failed parameter, because there is no turning back that late in the process.

The adjust_numbers method does not have importing parameters. Instead, the mapped parameter is used for input and output. When the method is called, mapped already contains a row for each new instance, identified by a temporary UUID key (column %pid). In case of optional external numbering, the sub-components of column %tmp contain the key values provided by the consumer.

Implement Unmanaged Early Numbering

In this exercise, you enable the creation of flight travels. You activate unmanaged early numbering and provide the required key values through a dedicated handler method.

Note

In this exercise, replace ## with your group number.

Solution

Repository Object TypeRepository Object ID
CDS Behavior Definition (Model)/LRN/437C_R_TRAVEL
CDS Behavior Definition (Projection)/LRN/437C_C_TRAVEL
ABAP Class/LRN/BP_437C_R_TRAVEL

Task 1: Enable the Creation of Data

Enable the creation of flight travels in the OData UI service. To achieve this, make sure that the behavior definition and behavior projection contain the necessary statements.

Steps

  1. Open the behavior definition for your business object Z##_R_TRAVEL and make sure it defines the create operation. If the statement is missing, add it. If it is commented out, remove the comment signs.

    1. Make sure that the behavior definition contains the following statement:

      Code Snippet
      1
      create;
  2. Activate the behavior definition.

    1. Press Ctrl + F3 to activate the development object.

  3. Open the behavior projection, that is, the behavior definition on projection level Z##_C_TRAVEL and make sure it exposes the create operation to the OData service. If the statement is missing, add it. If it is commented out, remove the comment signs.

    1. Make sure that the behavior projection contains the following statement:

      Code Snippet
      1
      use create;
  4. Activate the behavior projection and test the preview of your OData UI service to see the effect.

    1. Press Ctrl + F3 to activate the development object.

    2. Reopen the preview for the OData UI service.

      Result

      You see a Create button in the toolbar of the table.
  5. Try to create a new flight travel with initial values in all fields.

    1. In the preview of the OData UI service, choose Create.

    2. Leave all fields empty and choose Create.

      Result

      You see the error messages Enter a value for the Agency ID field and Enter a value for the Travel ID field.

Task 2: Implement Unmanaged Early Numbering

Enable unmanaged early numbering and implement the related handler method. Use suitable methods of class /LRN/CL_S4D437_MODEL to derive values for the key fields AgencyId and TravelId. Finally, disable any direct editing for the key fields.

Steps

  1. Edit the behavior definition on data model level Z##_R_TRAVEL. At the end of the DEFINE BEHAVIOR statement, add the necessary addition to enable unmanaged early numbering.

    1. Adjust the code as follows:

      Code Snippet
      1234567
      define behavior for Z##_R_Travel alias Travel persistent table z##_travel lock master authorization master ( instance ) etag master ChangedAt early numbering {
  2. Activate the behavior definition and use a quick fix to add the required numbering method to the local handler class.

    1. Press Ctrl + F3 to activate the development object.

    2. Place the cursor on the create keyword and press Ctrl + 1 to invoke the quick assist proposals. Alternatively, choose the warning icon with a light bulb on the left of the code row with this keyword.

    3. From the list of available quick fixes, choose Add earlynumbering method for create of entity z##_r_travel in local class zbp_##_r_travel->lhc_travel.

  3. In the earlynumbering_create method, call the get_agency_by_user method of the /LRN/CL_S4D437_MODEL class and store the result in an inline declared variable (suggested name: agencyid).

    1. Adjust the code as follows:

      Code Snippet
      12345
      METHOD earlynumbering_create. DATA(agencyid) = /lrn/cl_s4d437_model=>get_agency_by_user( ). ENDMETHOD.
  4. Fill the travel component of the mapped parameter with one row for each new flight travel instance for which you must set the key values.

    Hint

    Use a CORRESPONDING expression to transfer the temporary key in the %cid column from the entities parameter to the travel component of the mapped parameter.
    1. Adjust the code as follows:

      Code Snippet
      1234567
      METHOD earlynumbering_create. DATA(agencyid) = /lrn/cl_s4d437_model=>get_agency_by_user( ). mapped-travel = CORRESPONDING #( entities ). ENDMETHOD.
  5. Implement a loop over the travel component of mapped, assigning an inline-declared field symbol (suggested name: <mapping>).

    1. Adjust the code as follows:

      Code Snippet
      1234567891011
      METHOD earlynumbering_create. DATA(agencyid) = /lrn/cl_s4d437_model=>get_agency_by_user( ). mapped-travel = CORRESPONDING #( entities ). LOOP AT mapped-travel ASSIGNING FIELD-SYMBOL(<mapping>). ENDLOOP. ENDMETHOD.
  6. Inside the loop, update the current row with values for the key fields AgencyId and TravelId.

    Note

    Call the get_next_travelid method of the /LRN/CL_S4D437_MODEL class to retrieve a new travel number for each entity.
    1. Adjust the code as follows:

      Code Snippet
      123456789101112
      METHOD earlynumbering_create. DATA(agencyid) = /lrn/cl_s4d437_model=>get_agency_by_user( ). mapped-travel = CORRESPONDING #( entities ). LOOP AT mapped-travel ASSIGNING FIELD-SYMBOL(<mapping>). <mapping>-AgencyId = agencyid. <mapping>-TravelId = /lrn/cl_s4d437_model=>get_next_travelid( ). ENDLOOP. ENDMETHOD.
  7. Activate the behavior implementation.

    1. Press Ctrl + F3 to activate the development object.

  8. Open the behavior definition for your business object Z##_R_TRAVEL and adjust the static field properties of the key fields AgencyId and TravelId to disable any direct changes to those fields.

    1. Adjust the code as follows:

      Code Snippet
      1234567
      // field ( readonly : update, mandatory : create ) // AgencyId, // TravelId; field ( readonly ) AgencyId, TravelId;
  9. Activate the behavior definition and test the preview of your OData UI service to see the effect.

    1. Press Ctrl + F3 to activate the development object.

    2. Reopen the preview for the OData UI service and choose Create.

      Result

      The key fields must be displayed as read-only.
    3. Maintain all required fields with valid values, and choose Create.

      Result

      Once the travel is created successfully, you see the new values for the key fields.

Determination Definition

The following list outlines the overview of Determinations in RAP:

  • Determinations in RAP

    • Part of business object behavior
    • Implemented in local handler class
    • Executed based on trigger conditions and trigger time
  • Trigger Conditions

    • Modify operations (create, update, delete)
    • Modified fields
  • Trigger Times

    • on modify or on save
  • Purpose

    • Compute data
    • Modify entity instances using EML
  • Response

    • Return messages (REPORTED parameter)
  • Restriction

    • Not available for unmanaged, non-draft scenarios

A determination is an optional part of the business object behavior that modifies instances of business objects based on trigger conditions. Like actions and validations, determinations are defined in the behavior definition of the RAP BO and implemented in the behavior pool through a dedicated method of the local handler class.

A determination is implicitly invoked by the business objects framework if the trigger condition of the determination is fulfilled. Trigger conditions can be modify operations (create, update, delete) and modified fields. Two types of determinations are distinguished, depending on the stage of the program flow the determination is executed: on modify determinations and on save determinations.

An invoked determination can compute data. Use EML to modify entity instances according to the computation result and return messages to the consumer by passing them to the corresponding table in the REPORTED structure.

Note

There is no FAILED parameter in determinations.

An example of determination definition

Determinations are defined in the entity behavior definition with the following statement: determination <determination_name> <trigger time> { <trigger_conditions> }.

For determinations, the following trigger times are available:

on modify

The determination is executed immediately after data changes take place in the transactional buffer so that the result is available during the transaction.

on save

The determination is executed during the save sequence at the end of an transaction, when changes from the transactional buffer are persisted on the database.

Note

For determinations, two trigger times are available. Validations are only available with trigger time on save.

It is mandatory to provide at least one trigger condition within the curly brackets.

Like with validations The following trigger conditions are supported:

Create;

The determination is executed when an instance is created.

Update;

The determination is executed when an instance is updated.

Delete;

The determination is executed when an instance is deleted.

Field <field1>, <field2>, …;

The determination is executed when the value of one of the specified fields is changed by a create or update operation.

Multiple trigger conditions can be combined. The conditions are linked with OR that means at runtime it is sufficient that one of the trigger conditions is met.

Note

For determinations defined as on save, trigger condition update; only works with the trigger condition create;.

The behavior definition in the example defines one determination. It is executed during the modify phase, and it is triggered by the create operation alone. It is not triggered during update operations on existing entity instances.

Note

The execution order of determinations is not fixed. If more than one determination is triggered by the same condition, you cannot know which determination is executed first.

Determination Implementation

Ways to create the determination handler method

If the behavior pool already exists when you add the determination definition, you can use a quick fix to add the missing method to the local handler class. To invoke the quick fix, the usual options are available: Choose the warning icon with light bulb, right-click the validation name and choose Quick Fix, or place the cursor on the name of the validation and press Ctrl + 1.

Depending on the trigger time specified in the behavior definition, the method definition is generated with the addition FOR DETERMINE ON MODIFY or with the addition FOR DETERMINE ON SAVE. If you change the trigger time later, there is a syntax error in the behavior pool, but no error or warning in the behavior definition. To fix the syntax error, you have to navigate to the method definition and adjust it manually.

Implementation of the Determination Handler Method

The implementation of a determination is contained in a local handler class as part of the behavior pool. This local class inherits from the base handler class CL_ABAP_BEHAVIOR_HANDLER.

The signature of a determination method is typed using the keyword FOR DETERMINE ON MODIFY or FOR DETERMINE ON SAVE, depending on the chosen determination time. The type of the importing parameter is an internal table containing the keys of the instances for which the determination is executed.

Although not visible in the method definition, all determination handler methods have a response parameter named reported which allows you to report messages.

The actual data changes are performed using the local variants of EML statements MODIFY ENTITY or MODIFY ENTITIES.

Define and Implement a Determination

In this exercise, you create a determination that is used to set the content of the Status field to the value N (= new) when a new flight travel is created.

Note

In this exercise, replace ## with your group number.

Solution

Repository Object TypeRepository Object ID
CDS Behavior Definition (Model)/LRN/437C_R_TRAVEL
ABAP Class/LRN/BP_437C_R_TRAVEL

Task 1: Define a Determination

In the behavior definition on data model level Z##_R_TRAVEL, define a determination (suggested name: determineStatus) that is to be used to set the initial status for a new flight travel.

Steps

  1. Open the behavior definition for your business object Z##_R_TRAVEL. Define a new determination (suggested name: determineStatus) that is only triggered for new instances. The determination should be executed immediately after data changes take place in the transactional buffer.

    1. Add the following code:

      Code Snippet
      123
      determination determineStatus on modify { create; }
  2. Activate the behavior definition and use a quick fix to add the determination method to the local handler class.

    1. Press Ctrl + F3 to activate the development object.

    2. Place the cursor on the name of the determination determineStatus and press Ctrl + 1 to invoke the quick assist proposals.

    3. From the list of available quick fixes, choose Add method for determination determineStatus of entity z##_r_travel in local class lhc_travel.

Task 2: Implement the Determination

In the implementation of the determineStatus method, use EML to set the content of the Status field of newly created flight travels to the value N (= new).

Steps

  1. Implement a READ ENTITIES statement at the beginning of the determineStatus method to read the value of the Status field for the affected flight travels into an inline declared table named travels.

    1. Your code should be similar to the following:

      Code Snippet
      123456789
      METHOD determineStatus. READ ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). ENDMETHOD.
  2. After the READ ENTITIES statement, delete all entries from the travels table for which the Status field is not initial. Make sure that the determineStatus method is exited if the cleaned travels table does not contain any entries.

    1. Your code should be similar to the following:

      Code Snippet
      123456789101112
      METHOD determineStatus. READ ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). DELETE travels WHERE Status IS NOT INITIAL. CHECK travels IS NOT INITIAL. ENDMETHOD.
  3. After checking for existing table entries, implement a MODIFY ENTITIES statement to set the content of the Status field to the value N (= new) for all flight travels in the travels table.

    1. Add the following code:

      Code Snippet
      123456789101112131415161718
      METHOD determineStatus. READ ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). DELETE travels WHERE Status IS NOT INITIAL. CHECK travels IS NOT INITIAL. MODIFY ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( FOR key IN travels ( %tky = key-%tky Status = 'N' ) ). ENDMETHOD.
  4. Finally, pass the error messages returned by the MODIFY ENTITIES statement to the reported parameter of the determineStatus method.

    1. Add the following code:

      Code Snippet
      123456789101112131415161718192021
      METHOD determineStatus. READ ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). DELETE travels WHERE Status IS NOT INITIAL. CHECK travels IS NOT INITIAL. MODIFY ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( FOR key IN travels ( %tky = key-%tky Status = 'N' ) ) REPORTED DATA(update_reported). reported = CORRESPONDING #( DEEP update_reported ). ENDMETHOD.

Task 3: Test the Determination

Now you will test your work.

Steps

  1. Activate the behavior implementation class.

    1. Press Ctrl + F3 to activate the development object.

  2. Test your determination in the preview of the OData UI service.

    1. Reopen the preview for your OData UI service.

    2. Choose Create from the toolbar of the table.

    3. On the object page, enter valid values in the mandatory fields and select the Create button.

      Result

      The flight travel is saved and the Travel Status field contains the value N.