Adding ABAP logic

Objectives
After completing this lesson, you will be able to:

After completing this lesson, you will be able to:

  • Implement the behavior of a RAP Business Object

Validations

Checking the Semantic Key

As shown in the figure, in an RAP data model, the key of a database table is often made up of the client field and a UUID field, whose value is assigned automatically by the RAP runtime when you create a new instance of the business object. This field combination is sufficient to ensure that the system can identify each record in the table uniquely. However, as well as this technical key, our object also has a semantic key - in this case the combination of airline and flight number, which must also be unique according to the business logic. In order to ensure the uniqueness of this field combination, you must implement your own check in the form of a validation.

You declare validations in the behavior definition of the CDS view entity, and implement them in the behavior implementation class.

Input Checks in the App

As well as checking the semantic key, there are other checks that you need to perform. For example, although the generated app allows you to create, read, update and delete data, it does not yet contain any consistency checks. Consequently, you can create flight connections for airlines that don't exist, or where the departure and destination airports are the same.

To prevent this from happening, you define further validations in the behavior definition, and implement them in the behavior implementation class.

Creating Message Texts

Before you create the validation, you must create the texts that you want to display. You do this using a message class. A message class is a collection of up to 1000 messages that belong to a particular application area. As shown in the figure, each text has a number which identifies the message uniquely within the message class.

To create a new message class, proceed as follows:

  1. Choose FileNewOther… and type message into the filter field.

  2. Double-click the Message Class entry in the hit list, then enter a package, name, and description for the new message class. Choose Next.

  3. Assign the message class to a transport request and choose Finish.

Messages can also contain placeholders, which are replaced with concrete values when the message is displayed. Placeholders are denoted by the ampersand symbol followed by a number. You can use up to four placeholders in each message.

Defining the Validation

To define a validation, you add a validation declaration to the behavior definition of your business object. In this example, the validation should be performed whenever the user saves a data record, and this could be either when they create the record or if they subsequently change it.

When you define the validation in the behavior definition, a warning tells you that the corresponding method does not exist. Use a quick fix (key combination CTRL + 1) to add the method to the behavior implementation. The behavior implementation is a local class within your behavior pool. The method definition contains the addition FOR VALIDATE ON SAVE, which identifies it as the implementation of the validation. It has an importing parameter KEYS. This is an internal table containing the keys of the created or changed objects. You use these to read the actual data that the user has entered.

The addition FOR Connection~CheckSemanticKey links the method with the validation CheckSemanticKey from the behavior definition. Here, Connection is the alias name of the view entity Z_R_CONNECTION.

The Validation Process

Note
Some code examples in this section use SELECT statements inside of loops. This has been done to keep the examples simple. Note that SELECTs in loops can cause performance problems and should be avoided.

How to Validate the Semantic Key

Validate the Semantic Key

Steps

  1. Define a validation CheckSemanticKey to check that a particular flight number has not already been used.

    1. In the Project Explorer, navigate to the behavior definition Z##_R_CONNECTION and add the following line: validation CheckSemanticKey on save { create; update; }.

    2. Activate the behavior definition.

  2. Create the implementation of the validation using a Quick Fix.

    1. Position the cursor on the name of the validation and choose Ctrl + 1.

    2. Double-click the entry Add validation....

  3. Use the EML READ ENTITIES statement to read the data that the user entered. Ensure that the fields CarrierID, and ConnectionID are read. Use an inline declaration for the result set.

    1. Enter the following code in the method implementation. Replace ## with your group number:

      Code snippet
      
           READ ENTITIES OF z##_r_connection IN LOCAL MODE
                  ENTITY Connection
                  FIELDS ( CarrierID ConnectionID )
                    WITH CORRESPONDING #( keys )
                  RESULT DATA(connections).
      
      Copy code
  4. In a loop over the data that you just read, select the UUIDs of all other data sets with the same combination of airline ID and flight number. Use a union to address the active and the draft tables at the same time.

    Note
    Make sure you only read other data sets, that is, exclude data sets with the same UUID from the selection.
    1. Enter the following code in the method implementation. Replace ## with your group number:

      Code snippet
      
          SELECT FROM z##aconn
                 FIELDS uuid
                  WHERE carrier_id    = @connection-CarrierID
                    AND connection_id = @connection-ConnectionID
                    AND uuid          <> @connection-uuid
            UNION
            SELECT FROM z##dconn
                 FIELDS uuid
                  WHERE carrierid     = @connection-CarrierID
                    AND connectionid  = @connection-ConnectionID
                    and uuid          <> @connection-uuid
           
             INTO TABLE @DATA(check_result).
      
      Copy code
  5. If the internal table check_result contains any records create a new message with message classZS4D400, message number 001 and severity ms-error. Pass connection-CarrierID to parameter v1 and connection-ConnectionID to parameter v2.

    1. Enter the following code in the method implementation:

      Code snippet
      
           IF check_result IS NOT INITIAL.
      
              DATA(message) = me->new_message(
                                id       = 'ZS4D400'
                                number   = '001'
                                severity = ms-error
                                v1       = connection-CarrierID
                                v2       = connection-ConnectionID
                              ).
      
      
      Copy code
  6. Declare the structure reported_record with the type LIKE LINE OF reported-connection. Add the record to the reported structure.

    1. Enter the following code in the method implementation:

      Code snippet
      
              DATA reported_record LIKE LINE OF reported-connection.
      
              reported_record-%tky = connection-%tky.
              reported_record-%msg = message.
              reported_record-%element-CarrierID    = if_abap_behv=>mk-on.
              reported_record-%element-ConnectionID = if_abap_behv=>mk-on.
      
              APPEND reported_record TO reported-connection.
      
      Copy code
  7. Declare the structure failed_record with the type LIKE LINE OF failed-connection. Add the record to the failed structure and close the open IF and LOOP control structures.

    1. Enter the following code in the method implementation:

      Code snippet
      
              DATA failed_record like line of failed-connection.
      
              failed_record-%tky = connection-%tky.
              APPEND failed_record TO failed-connection.
      
            ENDIF.
          ENDLOOP.
      
      Copy code

Validate the Airline

Steps

  1. Define a validation CheckCarrierID to check that the airline that the user entered exists.

    1. In the Project Explorer, navigate to the behavior definition Z##_R_CONNECTION and add the following line: validation CheckCerrierID on save { create; field CarrierID; }.

    2. Activate the behavior definition.

  2. Create the implementation of the validation using a Quick Fix.

    1. Position the cursor on the name of the validation and choose Ctrl + 1.

    2. Double-click the entry Add validation....

  3. Use the EML READ ENTITIES statement to read the data that the user entered. Ensure that the field CARRID is read. Use an inline declaration for the result set.

    1. Enter the following code in the method implementation. Replace ## with your group number:

      Code snippet
      
          READ ENTITIES OF z##_r_connection IN LOCAL MODE
                 ENTITY Connection
                 FIELDS (  CarrierID )
                   WITH CORRESPONDING #(  keys )
                 RESULT DATA(connections).
      
      Copy code
  4. In a loop over the data that you just read, check that the airline exists. Use a SELECT statement that returns the literal abap_true if the airline exists. Use the CDS View /dmo/i_carrier as the data source of the statement.

    1. Enter the following code in the method implementation. Replace ## with your group number:

      Code snippet
      
          LOOP AT connections INTO DATA(connection).
      
            SELECT SINGLE
              FROM /DMO/I_Carrier
            FIELDS @abap_true
             WHERE airlineid = @connection-CarrierID
             INTO @DATA(exists).
      
      Copy code
  5. If the value of exists is abap_false, create a message object with message ID ZS4D400, number 002, severity ms-error and parameter v1 connection-CarrierID.

    1. Enter the following code in the method implementation:

      Code snippet
      
      
       IF exists = abap_false.   
      
         DATA(message) = me->new_message(
                             id       = 'ZS4D400'
                             number   = '002'
                             severity =  ms-error
                             v1       = connection-CarrierID
                           ) .
      
      Copy code
  6. Declare the structure reported_record with the type LIKE LINE OF reported-connection. Fill the fields in fields group %tky with the corresponding values of the current data set, store the reference to the message object in field %msg, and link the message to view element CarrierID. Finally, add the record to the reported structure.

    1. Enter the following code in the method implementation:

      Code snippet
      
              DATA reported_record LIKE LINE OF reported-connection.
      
              reported_record-%tky = connection-%tky.
              reported_record-%msg = message.
              reported_record-%element-carrierid = if_abap_behv=>mk-on.
      
              APPEND reported_record TO reported-connection.
      
      Copy code
  7. Declare the structure failed_record with the type LIKE LINE OF failed-connection. Fill the fields in fields group %tky with the corresponding values of the current data set. Add the record to the failed structure and close the open IF and LOOP control structures.

    1. Enter the following code in the method implementation:

      Code snippet
      
              DATA failed_record LIKE LINE OF failed-connection.
      
              failed_record-%tky = connection-%tky.
              APPEND failed_Record TO failed-connection.
      
            ENDIF.
          ENDLOOP.
      
      Copy code

Validate the Airport Codes

Steps

  1. Define a validation CheckOriginDestination to check that the departure and arrival airports of the flight connection are different.

    1. In the Project Explorer, navigate to the behavior definition Z##_R_CONNECTION and add the following line: validation CheckOriginDestination on save { create; field AirportFromID, AirportToID; }.

    2. Activate the behavior definition.

  2. Create the implementation of the validation using a Quick Fix.

    1. Position the cursor on the name of the validation and choose Ctrl + 1.

    2. Double-click the entry Add validation....

  3. Use the EML READ ENTITIES statement to read the data that the user entered. Ensure that the fields AirportFromID and AirportToID are read. Use an inline declaration for the result set.

    1. Enter the following code in the method implementation. Replace ## with your group number:

      Code snippet
      
           READ ENTITIES OF z##_r_Connection IN LOCAL MODE 
                  ENTITY Connection
                  FIELDS ( AirportFromID AirportToID )
                    WITH CORRESPONDING #(  keys )
                  RESULT DATA(connections).
      
      Copy code
  4. In a loop over the data that you just read, check whether AirportFromID and AirportToID are the same. If they are, create a message with id ZSD4D00, number 003, and severity ms-error.

    1. Enter the following code in the method implementation.

      Code snippet
      
          LOOP AT connections INTO DATA(connection).
            IF connection-AirportFromID = connection-AirportToID.
              DATA(message) = me->new_message(
                                id       = 'ZS4D400'
                                number   = '003'
                                severity = ms-error
                             ).
      
      Copy code
  5. Declare the structure reported_record with the type LIKE LINE OF reported-connection. Fill the fields in fields group %tky with the corresponding values of the current data set, store the reference to the message object in field %msg, and link the message to both airport fields. Finally, add the record to the reported structure.

    1. Enter the following code in the method implementation:

      Code snippet
      
              DATA reported_record LIKE LINE OF reported-connection.
      
              reported_record-%tky =  connection-%tky.
              reported_record-%msg = message.
              reported_record-%element-AirportFromID = if_abap_behv=>mk-on.
              reported_record-%element-AirportToID   = if_abap_behv=>mk-on.
      
              APPEND reported_record TO reported-connection.
      
      Copy code
  6. Declare the structure failed_record with the type LIKE LINE OF failed-connection. Fill the fields in fields group %tky with the corresponding values of the current data set. Add the record to the failed structure and close the open IF and LOOP control structures.

    1. Enter the following code in the method implementation:

      Code snippet
      
              DATA failed_record LIKE LINE OF failed-connection.
      
              failed_record-%tky = connection-%tky.
              APPEND failed_record TO failed-connection.
      
            ENDIF.
          ENDLOOP.
      
      Copy code

Determinations

In this section, you will learn how to complete the data using determinations.

Determine Cities Based on Airport Codes

In the example app, the flight connection entity contains a departure airport, city, and country, and an arrival airport, city, and country. While it would be possible to force the user to enter all of this information, it is better in terms of user experience and data consistency to have the user enter only the airport codes and for the app to read the corresponding city and country information from the database. You can perform this kind of task in a RAP application using a determination.

You will first implement the determination. Afterwards, you will learn how to deactivate input for the fields that will be filled automatically.

Defining the Determination

You define a determination in the behavior definition of a business object. The determination here is called getCities, it will be called whenever the business object is saved and at least one of the AirportFromID and AirportToID fields has changed. You can use a quick fix in the behavior definition to create the corresponding method in the behavior implementation.

The Determination Process

Let's explore each step of the determination process.

How to Determine the Cities and Countries

Determine the Cities and Countries

Steps

  1. Define a determination getCities to fill the city and country fields automatically based on the airport code that the user entered.

    1. In the Project Explorer, navigate to the behavior definition Z##_R_CONNECTION and add the following line: determination GetCities on save { field airportFromID, AirportToID; }.

    2. Activate the behavior definition.

  2. Create the implementation of the determination using a Quick Fix.

    1. Position the cursor on the name of the determination and choose Ctrl + 1.

    2. Double-click the entry Add method for determination....

  3. Read the user input using an EML READ ENTITIES statement. Read the fields AirportFromID and AirportToID. Use an inline declaration for the result set.

    1. Enter the following coding in the method implementation. Replace ## with your group number.

      Code snippet
      
          READ ENTITIES OF z##_r_connection IN LOCAL MODE
                 ENTITY Connection
                 FIELDS ( AirportFromID AirportToID )
                   WITH CORRESPONDING #( keys )
                 RESULT DATA(connections).
      
      Copy code
  4. In a loop over the data, use two SELECT statements to read the city and country data for the two airports the user entered. Use the CDS view /DMO/I_Airport as the data source and read the fields City and CountryCode. For AirportFromID, fill the fields CityFrom and CountryFrom. For AirportToID, fill the fields CityTo and CountryTo. Remember that you need the MODIFY statement to write the changes back to the internal table.

    1. Enter the following coding in the method implementation:

      Code snippet
      
      LOOP AT connections INTO DATA(connection).
      
            SELECT SINGLE
              FROM /DMO/I_Airport
            FIELDS city, CountryCode
             WHERE AirportID = @connection-AirportFromID
              INTO ( @connection-CityFrom, @connection-CountryTo ).
      
            SELECT SINGLE
              FROM /DMO/I_Airport
            FIELDS city, CountryCode
             WHERE AirportID = @connection-AirportToID
              INTO ( @connection-CityTo, @connection-CountryTo ).
      
            MODIFY connections FROM connection.
      
          ENDLOOP.
      
      Copy code
  5. Declare an internal table connections_upd with the type TABLE FOR UPDATE z##_r_connections where ## is your group number. Copy the data from the internal table connections to the new table connections_upd.

    1. Enter the following coding in the method implementation:

      Code snippet
      
          DATA connections_upd TYPE TABLE FOR UPDATE z##_r_connection.
      
          connections_upd = CORRESPONDING #( connections ).
      
      Copy code
  6. Use an EML MODIFY ENTITIES statement to update the data in the transactional buffer. Restrict the update to the fields that you modified (CityFrom CityTo CountryFrom CountryTo). Use the REPORTED addition to receive any messages from the statement. Transfer any messages to the reported structure of your method.

    1. Enter the following coding in the method implementation:

      Code snippet
      
          MODIFY ENTITIES OF z##_r_connection IN LOCAL MODE
                   ENTITY Connection
                   UPDATE
                   FIELDS ( CityFrom CountryFrom CityTo CountryTo )
                     WITH connections_upd
                 REPORTED DATA(reported_records).
      
          reported-connection = CORRESPONDING #( reported_records-connection ).
      
      Copy code

Log in to track your progress & complete quizzes