Learn how to implement an Excel upload feature in ABAP RAP using a clean custom action popup—without relying on UI5 extensions or object page workarounds. A simple, efficient, and UI-friendly approach for seamless data import.

ABAP RAP: Excel upload through custom action popup (No UI5 Extension, No Object Page workaround)

A frequent business requirement involves enabling mass changes to business objects via Excel uploads executed through a custom action popup. Historically, achieving this functionality has necessitated various workarounds, often involving UI5 extensions, third-party solutions, or Object Page manipulations, all of which present specific implementation challenges.

The existing workaround approaches present several drawbacks:

  • Custom UI Extensions: Require specialized UI5 development expertise.
  • Third-Party Solutions: Introduce risks related to licensing compliance and potential security vulnerabilities.
  • Object Page Manipulations: Involve complex, multi-step processes, such as creating a dummy object page, facilitating file upload, temporarily storing the file data in a table field, and requiring a final user action (a button press) to initiate processing. This temporary data storage is often unnecessary, complicating the data model.

However, SAP has recently introduced ABAP / CAP annotations that offer a cloud-ready solution, potentially eliminating approximately 95% of the development effort typically associated with integrating an Excel upload into the backend. This innovation allows developers to prioritize implementing core business logic over developing reusable technical artifacts.

I will now detail the implementation steps.

A business requirement to manage mass processing listings for a library was selected to demonstrate this use case. The implementation requires several steps, with steps 3 through 6 being the special or additional configurations needed, while all others are considered routine.

Implementation Steps

1. A database table for the listing entity is created. This involves fields such as Id, Title, Type, and Author.

@EndUserText.label : 'Library Listings'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zrk_lib_listings {

  key client            : abap.clnt not null;
  key listing_uuid      : sysuuid_x16 not null;
  id                    : abap.numc(10);
  title                 : abap.char(40);
  type                  : abap.char(5);
  author                : abap.char(40);
  publisher_studio      : abap.char(40);
  isbn_ean              : abap.char(40);
  language_code         : abap.lang;
  publication_year      : abap.numc(4);
  description           : abap.char(40);
  totalcopies           : abap.int2;
  available_copies      : abap.int2;
  location_shelf_id     : abap.char(40);
  lending_duration_days : abap.int2;
  status                : abap.char(40);
  cover_image_url       : abap.char(100);
  local_created_by      : abp_creation_user;
  local_created_at      : abp_creation_tstmpl;
  local_last_changed_by : abp_locinst_lastchange_user;
  local_last_changed_at : abp_locinst_lastchange_tstmpl;
  last_changed_at       : abp_lastchange_tstmpl;

}

2. A RAP Business Object (BO) is generated, followed by the requisite UI artifacts. The specific RAP BO scenario (Managed, Unmanaged, Draft, or Non-Draft) is noted as not influencing the core Excel upload use case. The RAP Generator is used to simplify the demonstration.

3. A root abstract entity is created for the file to be uploaded. (This entity is highly reusable and can be applied across different RAP BOs).

@EndUserText.label: 'Abs. Entity For Attachment'
define root abstract entity ZRK_D_FILE_STREAM
{
  @Semantics.largeObject.mimeType: 'MimeType'
  @Semantics.largeObject.fileName: 'FileName'
  @Semantics.largeObject.contentDispositionPreference: #INLINE
  @EndUserText.label: 'Select Excel file'
  StreamProperty : abap.rawstring(0);
  
  .hidden: true
  MimeType : abap.char(128);
  
  .hidden: true
  FileName : abap.char(128);   
}

4. The abstract behavior definition for the file entity is implemented.

abstract;
strict(2);
with hierarchy;
define behavior for ZRK_D_FILE_STREAM {
}

5. A second abstract entity is created to serve as an action parameter. This entity includes an association to the file abstract entity (from Step 3).

@EndUserText.label: 'Action Param for Uploading Excel'
define root abstract entity ZRK_D_UPLOAD_EXCEL
{
// Dummy is a dummy field
@UI.hidden: true
dummy : abap_boolean;
     _StreamProperties : association [1] to ZRK_D_FILE_STREAM on 1 = 1;
    
}

6. The abstract behavior definition for the action parameter is implemented, including the association to the earlier entity.

abstract;
strict ( 2 );
with hierarchy;
define behavior for ZRK_D_UPLOAD_EXCEL //alias <alias_name>
{
association _StreamProperties with hierarchy;
}

7. An action is defined on the RAP BO Behavior definition, with the parameter specified in Step 5.

static action ExcelUpload deep parameter ZRK_D_UPLOAD_EXCEL ;
managed implementation in class ZRK_BP_R_LIB_LISTINGS unique;
strict ( 2 );
with draft;
extensible;
define behavior for ZRK_R_LIB_LISTINGS alias Listings
persistent table ZRK_LIB_LISTINGS
extensible
draft table ZRK_LIB_LSTNGS_D
etag master LocalLastChangedAt
lock master total etag LastChangedAt
authorization master( global )
{
  field ( readonly )
   ListingUUID,
   LocalCreatedBy,
   LocalCreatedAt,
   LocalLastChangedBy,
   LocalLastChangedAt,
   LastChangedAt;

  field ( numbering : managed )
   ListingUUID;


  create;
  update;
  delete;

  draft action Activate optimized;
  draft action Discard;
  draft action Edit;
  draft action Resume;
  draft determine action Prepare;

  static action ExcelUpload deep parameter ZRK_D_UPLOAD_EXCEL ;

  mapping for ZRK_LIB_LISTINGS corresponding extensible
  {
    ListingUUID = listing_uuid;
    ID = id;
    Title = title;
    Type = type;
    Author = author;
    PublisherStudio = publisher_studio;
    IsbnEan = isbn_ean;
    LanguageCode = language_code;
    PublicationYear = publication_year;
    Description = description;
    Totalcopies = totalcopies;
    AvailableCopies = available_copies;
    LocationShelfID = location_shelf_id;
    LendingDurationDays = lending_duration_days;
    Status = status;
    CoverImageUrl = cover_image_url;
    LocalCreatedBy = local_created_by;
    LocalCreatedAt = local_created_at;
    LocalLastChangedBy = local_last_changed_by;
    LocalLastChangedAt = local_last_changed_at;
    LastChangedAt = last_changed_at;
  }

}

8. The business logic is implemented to read the Excel content. A released API, XCO_CP_XLSX , is used for this demonstration.

METHOD ExcelUpload.
    TYPES : BEGIN OF ty_sheet_data,
              id                  TYPE zrk_r_lib_listings-id,
              title               TYPE zrk_r_lib_listings-title,
              type                TYPE zrk_r_lib_listings-Type,
              author              TYPE zrk_r_lib_listings-author,
              PublisherStudio     TYPE zrk_r_lib_listings-PublisherStudio,
              IsbnEan             TYPE zrk_r_lib_listings-IsbnEan,
              LanguageCode        TYPE zrk_r_lib_listings-LanguageCode,
              PublicationYear     TYPE zrk_r_lib_listings-PublicationYear,
              description         TYPE zrk_r_lib_listings-Description,
              Totalcopies         TYPE zrk_r_lib_listings-Totalcopies,
              AvailableCopies     TYPE zrk_r_lib_listings-AvailableCopies,
              LocationShelfID     TYPE zrk_r_lib_listings-LocationShelfID,
              LendingDurationDays TYPE zrk_r_lib_listings-LendingDurationDays,
              status              TYPE zrk_r_lib_listings-Status,
            END OF ty_sheet_data.

    DATA lv_file_content   TYPE xstring.
    DATA lt_sheet_data     TYPE STANDARD TABLE OF ty_sheet_data.
    DATA lt_listing_create TYPE TABLE FOR CREATE zrk_r_lib_listings.

    lv_file_content = VALUE #( keys[ 1 ]-%param-_streamproperties-StreamProperty OPTIONAL ).

    " Error handling in case file content is initial

    DATA(lo_document) = xco_cp_xlsx=>document->for_file_content( lv_file_content )->read_access( ).

    DATA(lo_worksheet) = lo_document->get_workbook( )->worksheet->at_position( 1 ).

    DATA(o_sel_pattern) = xco_cp_xlsx_selection=>pattern_builder->simple_from_to(
      )->from_column( xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )  " Start reading from Column A
      )->to_column( xco_cp_xlsx=>coordinate->for_alphabetic_value( 'N' )   " End reading at Column N
      )->from_row( xco_cp_xlsx=>coordinate->for_numeric_value( 2 )    " *** Start reading from ROW 2 to skip the header ***
      )->get_pattern( ).

    lo_worksheet->select( o_sel_pattern
                                     )->row_stream(
                                     )->operation->write_to( REF #( lt_sheet_data )
                                     )->set_value_transformation(
                                         xco_cp_xlsx_read_access=>value_transformation->string_value
                                     )->execute( ).

    lt_listing_create = CORRESPONDING #( lt_sheet_data ).

    MODIFY ENTITIES OF zrk_r_lib_listings IN LOCAL MODE
           ENTITY Listings
           CREATE AUTO FILL CID FIELDS ( Id Title Type author PublisherStudio IsbnEan LanguageCode PublicationYear description Totalcopies AvailableCopies LocationShelfID LendingDurationDays status )
           WITH lt_listing_create
           " TODO: variable is assigned but never used (ABAP cleaner)
           MAPPED DATA(lt_mapped)
           " TODO: variable is assigned but never used (ABAP cleaner)
           REPORTED DATA(lt_reported)
           " TODO: variable is assigned but never used (ABAP cleaner)
           FAILED DATA(lt_failed).

    " Communicate the messages to UI - not in scope of this demo
    IF lt_failed IS INITIAL.
      APPEND VALUE #( %msg = new_message_with_text( severity = if_abap_behv_message=>severity-success
                                                    text     = 'Listings have been uploaded - please refresh the list!!' ) )
             TO reported-listings.
    ENDIF.
  ENDMETHOD.

9. The action is utilized on the projection behavior and subsequently exposed in the metadata extension.

use action ExcelUpload;
projection implementation in class ZRK_BP_C_LIB_LISTINGS unique;
strict ( 2 );
extensible;
use draft;
use side effects;
define behavior for ZRK_C_LIB_LISTINGS alias Listings
extensible
use etag
{
  use create;
  use update;
  use delete;

  use action Edit;
  use action Activate;
  use action Discard;
  use action Resume;
  use action Prepare;

  use action ExcelUpload;

}
.lineItem: [{ type:#FOR_ACTION , dataAction: 'ExcelUpload' , label: 'Upload Excel' }]

10. The service binding is published, and the application is then ready for execution.

Note:

This feature is currently functional on the BTP ABAP Environment. However, an issue appears to exist with metadata generation on S/4HANA 2023 On-Premise deployments, even though the objects are syntactically correct. It is anticipated that this constraint will be addressed in the S/4HANA 2025 release, making the full feature set available on the S/4HANA On-Premise version following a brief waiting period.

Rating: 5 / 5 (1 votes)