DEV Community

Cover image for Your first SAP RAP Application
Vimal Sharma
Vimal Sharma

Posted on

1

Your first SAP RAP Application

Hello,

Below is a attempt to show , how to build a RAP based application. If you are coming with core ABAP experience and want to learn how to build your first RAP application , you can follow along the blog.

The best way is to create , your own small version of every step. No need to copy the code in this blog rather create your own objects and follow the steps.

This will give you idea of how to go for RAP based applications including any custom API to call backend.

Below things are covered in this:

  1. How to fetch data via CDS view
  2. How to create provider view - alternative to MDE(Meta Data extension)
  3. How to create Behavior definition class and write validation in it.
  4. Add a custom button "Bulk Upload"

This is how it will look like at the end:

A computer showing fiori element report

Let's start..

  • 1. As a first step, we will be fetching records from table, So calling it root view(RV).
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Root view for Mapping table'
define root view entity ZR2R_E_073_RV_MAPPING_TAB as 
select from zr2r_e_073_t_map as mapping
    left outer join makt as mtext on mtext.matnr = mapping.service_material
                                    and mtext.spras = $session.system_language
    left outer join but000 as CustomerMaster on CustomerMaster.partner = mapping.third_party_cust
    left outer join cskt as TechReceiverCCText on TechReceiverCCText.kostl = mapping.tech_receiver_cc
                                         and TechReceiverCCText.spras = $session.system_language
                                         and TechReceiverCCText.datbi >= $session.system_date
    left outer join cskt as SenderCCText on SenderCCText.kostl = mapping.sending_cc
                                       and SenderCCText.spras = $session.system_language
                                       and SenderCCText.datbi >= $session.system_date
    left outer join cskt as Techsendccenter on Techsendccenter.kostl = mapping.tech_sender_cc
                                       and Techsendccenter.spras = $session.system_language
                                       and Techsendccenter.datbi >= $session.system_date
    left outer join zr2r_e_073_t_pri as pid on pid.lauf_id = mapping.lauf_id
{
  key mapping.service_material as ServiceMaterial,
  key mapping.sending_cc       as SendingCC,
  key mapping.lauf_id          as LaufID,
  key mapping.tech_receiver_cc as TechReceiverCC, 
      TechReceiverCCText.ktext as TechReceiverCCDesc,
      SenderCCText.ktext       as SenderCCDesc,
      Techsendccenter.ktext    as Techsendccdesc,
      mtext.maktx              as MaterialDescription,
      CustomerMaster.name_org1     as CustomerName,
      pid.lauf_id_description   as lid_desc,
      mapping.tech_sender_cc   as TechSenderCC,
      mapping.third_party_cust as ThirdPartyCust,
      mapping.asset_class as AssetClass,
      mapping.created_on as CreatedOn,
      mapping.created_by as CreatedBy,
      mapping.changed_on as ChangedOn,
      mapping.changed_by as ChangedBy
}
Enter fullscreen mode Exit fullscreen mode
  • As a 2nd step, Let's create provider view, Provider view(PV) is basically a substitute of Meta data extension. We will provide annotations to make sure application look the way we want it to be. It includes any F4 help, sequence of fields etc...
  • So you can see here we are giving reference of our Root view here while creating provider view and all annotations are used to display records.
@EndUserText.label: 'Provider Contract view of Mapping table'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.ignorePropagatedAnnotations: true
@UI: {
  headerInfo: {
    typeName: 'Mapping',
    typeNamePlural: 'Mappings',
    title: { type: #STANDARD, value: 'ServiceMaterial' }
  }
}
define root view entity ZR2R_E_073_PV_CDS_MAPPING_TAB
  provider contract transactional_query
  as projection on ZR2R_E_073_RV_MAPPING_TAB
{
         @UI.facet: [
           {
             id: 'Mapping',
             purpose: #STANDARD,
             type: #IDENTIFICATION_REFERENCE,
             label: 'Mapping',
             position: 10
           }
         ]
         @UI: {
             lineItem: [{ position: 10, label: 'Service Material' }],
             identification: [{ position: 10 }],
             selectionField: [{ position: 10 }]
           }

         @Consumption.valueHelpDefinition: [{entity: {name: 'I_Productvh', element: 'Product' }}]
         @EndUserText.label: 'Service Material'
  key    ServiceMaterial,

         @UI:{
           lineItem: [{ position: 30, label: 'Sending Cost Center' }],
           identification: [{ position: 30 }],
           selectionField: [{ position: 20 }]
         }
         @Consumption.valueHelpDefinition: [{entity: {name: 'I_CostCenterVH', element: 'CostCenter' }}]
         @EndUserText.label: 'Sending Cost Center'
  key    SendingCC,

         @UI:{
         lineItem: [{ position: 50, label: 'Process ID' }],
         identification: [{ position: 50 }],
         selectionField: [{ position: 30 }]
         }
         @Consumption.valueHelpDefinition: [{entity: {name: 'ZR2R_E_073_VH_LAUF_ID', element: 'Lauf_ID' }}]
         @EndUserText.label: 'Process ID'
  key    LaufID,

         @UI:{
            lineItem: [{ position: 110, label: 'Technical Receiving Cost Center' }],
            identification: [{ position: 110 }]
          }
         @Consumption.valueHelpDefinition: [{entity: {name: 'I_CostCenterVH', element: 'CostCenter' }}]
         @EndUserText.label: 'Technical Receiving Cost Center'
  key    TechReceiverCC,

         @UI:{
             lineItem: [{ position: 20, label: 'Material Description' }],
             identification: [{ position: 20, label: 'Material Description' }]
           }
         MaterialDescription,
        @UI:{
             lineItem: [{ position: 40, label: 'Sending cost center Desc' }],
             identification: [{ position: 40, label: 'Sending cost center Description' }]
           }
         SenderCCDesc,
         @UI:{
             lineItem: [{ position: 120, label: 'Technical rec. cost center Description' }],
             identification: [{ position: 120, label: 'Tech. rec. cost center Description' }]
           }
         TechReceiverCCDesc,
         @UI:{
             lineItem: [{ position: 60, label: 'Process id Desc' }],
             identification: [{ position: 60, label: 'Process id description' }]
           }
         lid_desc,
         @UI:{
           lineItem: [{ position: 70, label: 'Technical Sending Cost Center' }],
           identification: [{ position: 70, label: 'Technical Sending Cost Center' }]
         }
         @Consumption.valueHelpDefinition: [{entity: {name: 'I_CostCenterVH', element: 'CostCenter' }}]
         @EndUserText.label: 'Technical Sending Cost Center'
         TechSenderCC,
         @UI:{
             lineItem: [{ position: 80, label: 'Tech. Sending cost center Description' }],
             identification: [{ position: 80, label: 'Tech. Sending cost center Description' }]
           }
         Techsendccdesc,
         @UI:{
           lineItem: [{ position: 90, label: 'Customer' }],
           identification: [{ position: 90 }]
         }
         @Consumption.valueHelpDefinition: [{entity: {name: 'I_Customer', element: 'Customer' }}]
         @EndUserText.label: 'Customer'
         ThirdPartyCust,
         @UI:{
             lineItem: [{ position: 100, label: 'Customer Name' }],
             identification: [{ position: 100, label: 'Customer Name' }]
           }
         CustomerName,

         @UI:{
           lineItem: [{ position: 130, label: 'Assset Class' }],
           identification: [{ position: 130 }]
         }
         @Consumption.valueHelpDefinition: [{entity: {name: 'I_AssetClass', element: 'AssetClass' }}]
         @EndUserText.label: 'Asset Class'
         AssetClass,

         @UI.hidden: true
         @Semantics.systemDateTime.createdAt: true
         CreatedOn as CreatedOn,

         @UI.hidden: true
         @Semantics.user.createdBy: true
         CreatedBy as CreatedBy,

         @UI.hidden: true
         @Semantics.systemDateTime.lastChangedAt: true
         ChangedOn as ChangedOn,

         @UI.hidden: true
         @Semantics.user.lastChangedBy: true
         ChangedBy as ChangedBy
}

Enter fullscreen mode Exit fullscreen mode
  • As a 3rd step, right click on your root view and choose "New Behavior definition" as shown below:

Image description

  • In 4th step, we will write below code for validation, making fields mandatory etc..
managed implementation in class zbp_r2r_e_073_rv_mapping_tab unique;
strict;

define behavior for ZR2R_E_073_RV_MAPPING_TAB //alias <alias_name>
persistent table zr2r_e_073_t_map
lock master
authorization master ( instance )
etag master ChangedOn
{
  create;
  update;
  delete;
  field ( mandatory : create ) ServiceMaterial, SendingCC, LaufID;
  field ( readonly : update ) ServiceMaterial, SendingCC, LaufID,TechReceiverCC;
  field ( readonly ) CreatedBy, CreatedOn, ChangedBy, ChangedOn,TechReceiverCCDesc,SenderCCDesc,
                              Techsendccdesc,MaterialDescription,CustomerName,lid_desc;
  //  static action Bulk_up result [1] $self;

  determination setAdministrativeData on modify
  { field ServiceMaterial,
    SendingCC, TechSenderCC, LaufID, ThirdPartyCust, TechReceiverCC; }

  // Validations
  validation validateServiceMaterial on save { create; update; }
  validation validateSendingCC on save { create; update; }
  validation validateTechSenderCC on save { create; update; }
  validation validateLaufID on save { create; update; }
  validation validateThirdPartyCust on save { create; update; }
  validation validateTechReceiverCC on save { create; update; }
  validation validassetclass on save { create; update; }

  mapping for ZR2R_E_073_T_MAP
  {
    ServiceMaterial = service_material;
    SendingCC = sending_cc;
    TechSenderCC = tech_sender_cc;
    LaufID = lauf_id;
    ThirdPartyCust = third_party_cust;
    TechReceiverCC = tech_receiver_cc;
    AssetClass = asset_class;
    CreatedOn = created_on;
    CreatedBy = created_by;
    ChangedBy = changed_by;
    ChangedOn = changed_on;
  }

}
Enter fullscreen mode Exit fullscreen mode
  • As a next step, you need to create the class and validation methods in Behavior definition class as shown below:
CLASS lhc_zr2r_e_073_rv_mapping_tab DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

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

*    METHODS bulk_up FOR MODIFY
*      IMPORTING keys FOR ACTION zr2r_e_073_rv_mapping_tab~bulk_up RESULT result.
    METHODS validatelaufid FOR VALIDATE ON SAVE
      IMPORTING keys FOR zr2r_e_073_rv_mapping_tab~validatelaufid.

    METHODS validatesendingcc FOR VALIDATE ON SAVE
      IMPORTING keys FOR zr2r_e_073_rv_mapping_tab~validatesendingcc.

    METHODS validateservicematerial FOR VALIDATE ON SAVE
      IMPORTING keys FOR zr2r_e_073_rv_mapping_tab~validateservicematerial.

    METHODS validatetechreceivercc FOR VALIDATE ON SAVE
      IMPORTING keys FOR zr2r_e_073_rv_mapping_tab~validatetechreceivercc.

    METHODS validatetechsendercc FOR VALIDATE ON SAVE
      IMPORTING keys FOR zr2r_e_073_rv_mapping_tab~validatetechsendercc.

    METHODS validatethirdpartycust FOR VALIDATE ON SAVE
      IMPORTING keys FOR zr2r_e_073_rv_mapping_tab~validatethirdpartycust.
    METHODS setadministrativedata FOR DETERMINE ON MODIFY
      IMPORTING keys FOR zr2r_e_073_rv_mapping_tab~setadministrativedata.
    METHODS validassetclass FOR VALIDATE ON SAVE
      IMPORTING keys FOR zr2r_e_073_rv_mapping_tab~validassetclass.

ENDCLASS.

CLASS lhc_zr2r_e_073_rv_mapping_tab IMPLEMENTATION.

  METHOD get_instance_authorizations.
  ENDMETHOD.

  METHOD validatelaufid.
    "Variable declaration
    DATA: lt_lauf TYPE STANDARD TABLE OF zr2r_e_073_t_pri.
    "logic to validate lauf id
    READ ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
        ENTITY zr2r_e_073_rv_mapping_tab
          FIELDS ( laufid )
          WITH CORRESPONDING #( keys )
        RESULT DATA(lt_mapping).

    lt_lauf = CORRESPONDING #( lt_mapping MAPPING lauf_id = laufid ).
    DELETE lt_lauf WHERE lauf_id IS INITIAL.
    READ TABLE lt_lauf ASSIGNING FIELD-SYMBOL(<fs_mapping>) INDEX 1.
    IF sy-subrc IS INITIAL.
      SELECT SINGLE @abap_true
        FROM zr2r_e_073_t_pri
        WHERE lauf_id = @<fs_mapping>-lauf_id
        INTO @DATA(lv_exists).
      IF sy-subrc IS NOT INITIAL.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky ) TO failed-zr2r_e_073_rv_mapping_tab.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky
                      %msg = new_message( id       = 'ZR2R_E_073_MESSAGE_C'
                                          number   = '018'
                                          severity = if_abap_behv_message=>severity-error
                                          v1       = <fs_mapping>-lauf_id )
                      %element-laufid = if_abap_behv=>mk-on ) TO reported-zr2r_e_073_rv_mapping_tab.
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD validatesendingcc.

    DATA: lt_csks TYPE STANDARD TABLE OF csks.
    DATA: lv_exists TYPE abap_bool.

    READ ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
        ENTITY zr2r_e_073_rv_mapping_tab
          FIELDS ( sendingcc )
          WITH CORRESPONDING #( keys )
        RESULT DATA(lt_mapping).

    lt_csks = CORRESPONDING #( lt_mapping MAPPING kostl = sendingcc ).
    DELETE lt_csks WHERE kostl IS INITIAL.
    READ TABLE lt_csks ASSIGNING FIELD-SYMBOL(<fs_mapping>) INDEX 1.
    IF sy-subrc IS INITIAL.
      SELECT SINGLE @abap_true
        FROM csks
        WHERE kostl = @<fs_mapping>-kostl
          AND kokrs = 'BMG1'
          AND datab <= @sy-datum " Start date is less than or equal to current date
          AND datbi >= @sy-datum
        INTO @lv_exists.
      IF sy-subrc IS NOT INITIAL.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky ) TO failed-zr2r_e_073_rv_mapping_tab.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky
                      %msg = new_message( id       = 'ZR2R_E_073_MESSAGE_C'
                                          number   = '002'
                                          severity = if_abap_behv_message=>severity-error
                                          v1       = <fs_mapping>-kostl )
                      %element-sendingcc = if_abap_behv=>mk-on ) TO reported-zr2r_e_073_rv_mapping_tab.
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD validateservicematerial.
    READ ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
       ENTITY zr2r_e_073_rv_mapping_tab
         FIELDS ( servicematerial )
         WITH CORRESPONDING #( keys )
       RESULT DATA(lt_mapping).

    DATA: lt_mara TYPE STANDARD TABLE OF mara.
    DATA: lv_exists TYPE abap_bool.

    lt_mara = CORRESPONDING #( lt_mapping MAPPING matnr = servicematerial ).
    DELETE lt_mara WHERE matnr IS INITIAL.
    READ TABLE lt_mara ASSIGNING FIELD-SYMBOL(<fs_mapping>) INDEX 1.
    IF sy-subrc IS INITIAL.
      SELECT SINGLE @abap_true
        FROM mara
        WHERE matnr = @<fs_mapping>-matnr
        INTO @lv_exists.
      IF sy-subrc IS NOT INITIAL.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky ) TO failed-zr2r_e_073_rv_mapping_tab.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky
                      %msg = new_message( id       = 'ZR2R_E_073_MESSAGE_C'
                                          number   = '001'
                                          severity = if_abap_behv_message=>severity-error
                                          v1       = <fs_mapping>-matnr )
                      %element-servicematerial = if_abap_behv=>mk-on ) TO reported-zr2r_e_073_rv_mapping_tab.
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD validatetechreceivercc.
    READ ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
      ENTITY zr2r_e_073_rv_mapping_tab
        FIELDS ( techreceivercc )
        WITH CORRESPONDING #( keys )
      RESULT DATA(lt_mapping).

    DATA: lt_csks TYPE STANDARD TABLE OF csks.
    DATA: lv_exists TYPE abap_bool.

    lt_csks = CORRESPONDING #( lt_mapping MAPPING kostl = techreceivercc ).
    DELETE lt_csks WHERE kostl IS INITIAL.
    READ TABLE lt_csks ASSIGNING FIELD-SYMBOL(<fs_mapping>) INDEX 1.
    IF sy-subrc IS INITIAL.
      DATA(lv_kostl_no_zeros) = <fs_mapping>-kostl.
      " Remove leading zeros
      SHIFT lv_kostl_no_zeros LEFT DELETING LEADING '0'.
      "validation only if it's less then 10 chars
      IF strlen( lv_kostl_no_zeros ) = 10.
        SELECT SINGLE @abap_true
          FROM csks
          WHERE kostl = @<fs_mapping>-kostl
            AND kokrs = 'BMG1'
          INTO @lv_exists.
        IF sy-subrc IS NOT INITIAL.
          APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky ) TO failed-zr2r_e_073_rv_mapping_tab.
          APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky
                        %msg = new_message( id       = 'ZR2R_E_073_MESSAGE_C'
                                            number   = '002'
                                            severity = if_abap_behv_message=>severity-error
                                            v1       = <fs_mapping>-kostl )
                        %element-techreceivercc = if_abap_behv=>mk-on ) TO reported-zr2r_e_073_rv_mapping_tab.
        ENDIF.
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD validatetechsendercc.
    READ ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
      ENTITY zr2r_e_073_rv_mapping_tab
        FIELDS ( techsendercc )
        WITH CORRESPONDING #( keys )
      RESULT DATA(lt_mapping).

    DATA: lt_csks TYPE STANDARD TABLE OF csks.
    DATA: lv_exists TYPE abap_bool.

    lt_csks = CORRESPONDING #( lt_mapping MAPPING kostl = techsendercc ).
    DELETE lt_csks WHERE kostl IS INITIAL.
    READ TABLE lt_csks ASSIGNING FIELD-SYMBOL(<fs_mapping>) INDEX 1.
    IF sy-subrc IS INITIAL.
      SELECT SINGLE @abap_true
        FROM csks
        WHERE kostl = @<fs_mapping>-kostl
          AND kokrs = 'BMG1'
          AND datab <= @sy-datum " Start date is less than or equal to current date
          AND datbi >= @sy-datum
        INTO @lv_exists.
      IF sy-subrc IS NOT INITIAL.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky ) TO failed-zr2r_e_073_rv_mapping_tab.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky
                      %msg = new_message( id       = 'ZR2R_E_073_MESSAGE_C'
                                          number   = '002'
                                          severity = if_abap_behv_message=>severity-error
                                          v1       = <fs_mapping>-kostl )
                      %element-techsendercc = if_abap_behv=>mk-on ) TO reported-zr2r_e_073_rv_mapping_tab.
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD validatethirdpartycust.
    READ ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
      ENTITY zr2r_e_073_rv_mapping_tab
        FIELDS ( thirdpartycust )
        WITH CORRESPONDING #( keys )
      RESULT DATA(lt_mapping).

    DATA: lt_kna1 TYPE STANDARD TABLE OF kna1.
    DATA: lv_exists TYPE abap_bool.

    lt_kna1 = CORRESPONDING #( lt_mapping MAPPING kunnr = thirdpartycust ).
    DELETE lt_kna1 WHERE kunnr IS INITIAL.
    READ TABLE lt_kna1 ASSIGNING FIELD-SYMBOL(<fs_mapping>) INDEX 1.
    IF sy-subrc IS INITIAL.
      SELECT SINGLE @abap_true
        FROM kna1
        WHERE kunnr = @<fs_mapping>-kunnr
        INTO @lv_exists.
      IF sy-subrc IS NOT INITIAL.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky ) TO failed-zr2r_e_073_rv_mapping_tab.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky
                      %msg = new_message( id       = 'ZR2R_E_073_MESSAGE_C'
                                          number   = '003'
                                          severity = if_abap_behv_message=>severity-error
                                          v1       = <fs_mapping>-kunnr )
                      %element-thirdpartycust = if_abap_behv=>mk-on ) TO reported-zr2r_e_073_rv_mapping_tab.
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD setadministrativedata.
    DATA:
        current_user TYPE syuname.

    DATA(current_date) = sy-datum.
    current_user = cl_abap_context_info=>get_user_technical_name( ).

    " Read the entities
    READ ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
      ENTITY zr2r_e_073_rv_mapping_tab
      FIELDS ( servicematerial sendingcc createdon createdby changedon changedby )
      WITH CORRESPONDING #( keys )
      RESULT DATA(lt_entities).

    " Prepare modifications
    DATA: lt_update TYPE TABLE FOR UPDATE zr2r_e_073_rv_mapping_tab.

    LOOP AT lt_entities ASSIGNING FIELD-SYMBOL(<entity>).
      " Check if this is a new entity (create operation)
      IF <entity>-createdon IS INITIAL.
        APPEND VALUE #(
          %tky      = <entity>-%tky
          createdon = current_date
          createdby = current_user
          changedon = current_date
          changedby = current_user
        ) TO lt_update.
      ELSE.
        " This is an update operation
        APPEND VALUE #(
          %tky      = <entity>-%tky
          changedon = current_date
          changedby = current_user
        ) TO lt_update.
      ENDIF.
    ENDLOOP.

    " Modify the entities
    IF lt_update IS NOT INITIAL.
      MODIFY ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
        ENTITY zr2r_e_073_rv_mapping_tab
        UPDATE FIELDS ( createdon createdby changedon changedby )
        WITH lt_update.
    ENDIF.
  ENDMETHOD.

  METHOD validassetclass.

    TYPES : BEGIN OF ty_asset,
              assetclass TYPE anlkl,
              laufid     TYPE zde_lauf_id,
            END OF ty_asset.

    DATA : lt_asset  TYPE STANDARD TABLE OF ty_asset,
           lv_exists TYPE abap_bool.

    READ ENTITIES OF zr2r_e_073_rv_mapping_tab IN LOCAL MODE
      ENTITY zr2r_e_073_rv_mapping_tab
      FIELDS ( AssetClass LaufID )
      WITH CORRESPONDING #( keys )
      RESULT DATA(lt_asset_class).

    lt_asset = CORRESPONDING #( lt_asset_class MAPPING assetclass = AssetClass laufid = LaufID ).
    READ TABLE lt_asset ASSIGNING FIELD-SYMBOL(<fs_asset>) INDEX 1.
    IF sy-subrc IS INITIAL.

      IF <fs_asset>-laufid CA 'Dep' AND <fs_asset>-assetclass IS INITIAL.


        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky ) TO failed-zr2r_e_073_rv_mapping_tab.
        APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky
                      %msg = new_message( id       = 'ZR2R_E_073_MESSAGE_C'
                                          number   = '030'
                                          severity = if_abap_behv_message=>severity-error
                                          v1       = <fs_asset>-laufid )
                      %element-assetclass = if_abap_behv=>mk-on ) TO reported-zr2r_e_073_rv_mapping_tab.


      ENDIF.

      IF <fs_asset>-assetclass IS NOT INITIAL.

        SELECT SINGLE @abap_true
        FROM anla
        WHERE anlkl = @<fs_asset>-assetclass
        INTO @lv_exists.
        IF sy-subrc IS NOT INITIAL.
          APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky ) TO failed-zr2r_e_073_rv_mapping_tab.
          APPEND VALUE #( %tky = keys[ sy-tabix ]-%tky
                        %msg = new_message( id       = 'ZR2R_E_073_MESSAGE_C'
                                            number   = '031'
                                            severity = if_abap_behv_message=>severity-error
                                            v1       = <fs_asset>-assetclass )
                        %element-assetclass = if_abap_behv=>mk-on ) TO reported-zr2r_e_073_rv_mapping_tab.

        ENDIF.

      ENDIF.
    ENDIF.

  ENDMETHOD.

ENDCLASS.
Enter fullscreen mode Exit fullscreen mode
  • As a last step, we will add custom button to bulk upload the data via excel sheet. So in this there are 2 things:
  1. Front-end changes
  2. Back-end API creation

Front-end changes will be done by fiori team e.g. addition of button and calling API with GET method.

As a 2nd step , we will be creating API , So we will be creating custom entity as shown below:

//@AbapCatalog.viewEnhancementCategory: [#NONE]
//@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Validation & Post for SO ready table'
@ObjectModel.query.implementedBy: 'ABAP:ZR2R_E_073_CL_VAL_POST'
//@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
define custom entity ZR2R_E_073_CE_VALIDATION_POST
{
  key zid       : abap.string;
    zvalue      : abap.string;
}

Enter fullscreen mode Exit fullscreen mode

In above , you can see that we have ZID and ZVALUE as parameters where front-end team will be converting all records into BASE64 and sending it in parameter ZVALUE and ZID is indicator that this record is coming via bulk upload, so indicator you can keep anything.

And below annotation is used to mention the class where we will be implementing the code.
@ObjectModel.query.implementedBy: 'ABAP:ZR2R_E_073_CL_VAL_POST'

This class will be called automatically as and when front-end team calls the API with Get method.

Below is the code for class:

CLASS zr2r_e_073_cl_val_post DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    INTERFACES if_rap_query_provider .
    INTERFACES if_rap_query_request .

          .

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS zr2r_e_073_cl_val_post IMPLEMENTATION.

  METHOD if_rap_query_request~get_aggregation.

  ENDMETHOD.


  METHOD if_rap_query_request~get_entity_id.

  ENDMETHOD.


  METHOD if_rap_query_request~get_filter.

  ENDMETHOD.


  METHOD if_rap_query_request~get_paging.

  ENDMETHOD.


  METHOD if_rap_query_request~get_parameters.

  ENDMETHOD.


  METHOD if_rap_query_request~get_requested_elements.

  ENDMETHOD.


  METHOD if_rap_query_request~get_search_expression.

  ENDMETHOD.


  METHOD if_rap_query_request~get_sort_elements.

  ENDMETHOD.


  METHOD if_rap_query_request~is_data_requested.

  ENDMETHOD.


  METHOD if_rap_query_request~is_total_numb_of_rec_requested.

  ENDMETHOD.


  METHOD if_rap_query_provider~select.
    DATA : lv_base64  TYPE string,
           lt_file    TYPE STANDARD TABLE OF ty_file,
           lv_process TYPE string,
           lv_period  TYPE string,
           lv_year    TYPE string,
           lv_zid     TYPE string,
           lt_resp    TYPE STANDARD TABLE OF ty_result,
           lt_final   TYPE STANDARD TABLE OF ty_result,
           ls_final   TYPE ty_result,

    "Get details from request
    TRY.
        DATA(dyn_clause) =  io_request->get_filter( )->get_as_sql_string( ).

        " Split the string into individual conditions
        SPLIT dyn_clause AT 'AND' INTO TABLE DATA(lt_conditions).

        " Process each condition
        LOOP AT lt_conditions INTO DATA(lv_condition).
          " Remove leading/trailing spaces
          CONDENSE lv_condition.
          " Extract values based on field names
          IF lv_condition CS 'ZPROCESS ='.
            SPLIT lv_condition AT '=' INTO DATA(lv_field) lv_process.
            REPLACE ALL OCCURRENCES OF `'` IN lv_process WITH space.
            CONDENSE lv_process.
          ELSEIF lv_condition CS 'ZPERIOD ='.
            SPLIT lv_condition AT '=' INTO lv_field lv_period.
            REPLACE ALL OCCURRENCES OF `'` IN lv_period WITH space.
            CONDENSE lv_period.
          ELSEIF lv_condition CS 'ZYEAR ='.
            SPLIT lv_condition AT '=' INTO lv_field lv_year.
            REPLACE ALL OCCURRENCES OF `'` IN lv_year WITH space.
            CONDENSE lv_year.
          ELSEIF lv_condition CS 'ZVALUE ='.
            SPLIT lv_condition AT '=' INTO lv_field lv_base64.
            REPLACE ALL OCCURRENCES OF `'` IN lv_base64 WITH space.
            CONDENSE lv_base64.
          ELSEIF lv_condition CS 'ZID ='.
            SPLIT lv_condition AT '=' INTO lv_field lv_zid.
            REPLACE ALL OCCURRENCES OF `'` IN lv_zid WITH space.
            CONDENSE lv_zid.
          ENDIF.
        ENDLOOP.
        "Encoding
        DATA(lo_object) = NEW  cl_http_utility( ).
        DATA(lv_json_data) = lo_object->decode_base64( encoded = lv_base64 ).
        /ui2/cl_json=>deserialize(
        EXPORTING
          json        = lv_json_data                              " JSON string
          pretty_name = /ui2/cl_json=>pretty_mode-camel_case      " Pretty Print property names
        CHANGING
          data        = lt_file                                  " Data to serialize
      ).
        "Clear variables
        CLEAR: lt_resp,lt_sor,
               lt_resp, gv_counter,gv_string.
        "Validation & Post
        CASE lv_zid.
          WHEN 'UPL_VAL'.  "Validation while uploading of excel sheet
Enter fullscreen mode Exit fullscreen mode

The above code is not complete code but enough to understand the logic.

In above code , you need to implement 2 interfaces mentioned above and you will get Select method which will be called when GET is done from front-end(on button click).

In above you can see , we are getting the data and then looping on the records to fetch the relevant fields from excel and processing it further.

ZVALUE which is in base64 needs to be decoded, So a method to convert from BASE64 is used. Just make sure that technical field names should be same in front-end and back-end while decoding because otherwise your data will not be fetched correctly. So create a type(in this case lt_file ) and give the technical names to front-end which you have kept in backend.

So this way you can try to create your own small application.

Let me know if there's any question and something missing .

Thanks,
Vimal

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post