Skip to Content
Technical Articles
Author's profile photo Abhishek Dalakoti

Preview/Download Fragmented Forms from Fiori List Report (Part -2)

This Blog will be continuation of the 1st part of the blog where I explained how we can generate Fragmented Forms from Fiori List report using SEGW (OData) Project.

Part-1 – Preview/Download Fragmented Forms From Fiori List Report

In this part of the blog, I will be explaining how we can switch/ convert SEGW project to Unmanaged query (Custom CDS view) to make the solution as cloud ready.

 

Technical details

Backend API (Unmanaged Query)

For Creating the Form API in backend, we will use custom entity (ZCUSTOM_FORMAPI) with billing doc as parameter which will be used to get the billing document of the selected line.

Custom CDS entity will have one below field.

  • Stream_data – This field will hold the data for the PDF file and has a custom data type of RAWSTRING with length 0.

Note:  We cannot use RAWSTRING domain directly so we have to create a custom data element with data type as RAWSTRING and Length as ‘0’. This is important as length being ‘0’ would indicate that the RAWSTRING has No length restriction and can accommodate file of larger size.

Custom%20CDS%20View

Custom CDS View

The above custom CDS entity will not have any select rather the data will be fetched at runtime from the class defined using annotation @ObjectModel.query.implementedBy

It requires an ABAP class that implements the select method of the interface IF_RAP_QUERY_PROVIDER.

 

Query Implementation Class Logic

  1. Code first check if data is being requested or not (using io request->requested() )
  2. Using method io_request->get_parameters( ) to get the selected billing document sent from UI
  3. Create keys for content and master form.
  4. Call the get_document() API and send the response back to UI.
CLASS zcl_otc_print DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    INTERFACES if_rap_query_provider .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS ZCL_OTC_PRINT IMPLEMENTATION.


  METHOD if_rap_query_provider~select.

    DATA: lt_tab                   TYPE TABLE OF ZCUSTOM_FORMAPI.
    DATA: lt_master_keys           TYPE cl_somu_form_services=>ty_gt_key.
    DATA: lv_content               TYPE  xstring.
    DATA: lo_cl_somu_form_services TYPE REF TO cl_somu_form_services,
          lt_keys                  TYPE cl_somu_form_services=>ty_gt_key.


    TRY.
** requested data
        IF io_request->is_data_requested(  ).
**paging
          DATA(lv_offset) = io_request->get_paging( )->get_offset( ).
          DATA(lv_page_size) = io_request->get_paging( )->get_page_size( ).
          DATA(lv_max_rows) = COND #( WHEN lv_page_size = if_rap_query_paging=>page_size_unlimited
                                        THEN 0 ELSE lv_page_size )  .

** Get the selected billing document from UI
          TRY.
              DATA(lt_parameters) = io_request->get_parameters( ).
** P_BILLINGDOC is the parameter of custom entity
              DATA(lv_billdoc) =   VALUE #( lt_parameters[ parameter_name =  'P_BILLINGDOC' ]-value OPTIONAL ).

       * " --------------------------- Key Value for the Billing Doc content form --------
    
              lt_keys =  VALUE #(  ( name = 'BillingDocument'     value = lv_billdoc )
                                   ( name = 'SenderCountry'       value = 'DE'            )
                                   ( name = 'Language'(040)       value = 'E' ) ) .


*         " --------------------------- Key Value for the master form template ---------------------------

              lt_master_keys = VALUE #(         ( name = 'PrintFormDerivationRule'            value = 'ZINVOICE_FORM'  )
                                                ( name = 'WatermarkText'                      value = space                       )
                                                ( name = 'LocaleCountry'                      value = 'DE')
                                                ( name = 'LocaleLanguage'                     value = 'E'  )
                                                ( name = 'OutputControlApplicationObjectType' value = 'BILLING_DOCUMENT' )
                                                ( name = 'OutputControlApplicationObject'     value =  lv_billdoc )
                                                ( name = 'OutputRequestItem'                  value = '000001' )
                                                ( name = 'OutputDocumentType'                 value = 'BILLING_DOCUMENT'  )
                                                ( name = 'Recipient'(041)                     value = '00000001003' )
                                                ( name = 'RecipientRole'                      value = 'RE'  )
                                                ( name = 'SenderCountry'                      value = 'DE' )
                                                ( name = 'ReceiverPartnerNumber'              value = '00000001003'  ) ).
              lo_cl_somu_form_services = cl_somu_form_services=>get_instance( ).

              TRY.
     *  " --------------------------- Call GET_DOCUMENT API ---------------------------
                  lo_cl_somu_form_services->get_document( EXPORTING iv_master_form_name  = 'ZZ1_OTC_INVOICE_MASTER_A4'
                                                                    iv_form_name         = 'ZZ1_OTC_INVOICE_OUTPUT'
                                                                    it_key               = lt_keys
                                                                    it_master_key        = lt_master_keys
                                                                    iv_form_language     = 'E'
                                                                    iv_form_country      = 'DE'
                                                          IMPORTING ev_content           = lv_content
                                                          ).
                CATCH cx_somu_error INTO DATA(lv_formerror).

              ENDTRY.
              lt_tab = VALUE #( ( stream_data = lv_content
                                   ) ).
              io_response->set_total_number_of_records( 1 ).
  *  " -------------- Send the response back to UI------------
              io_response->set_data( lt_tab ).

            CATCH cx_rap_query_filter_no_range INTO DATA(lv_range).
              DATA(lv_msg) = lv_range->get_text( ).
          ENDTRY.


        ENDIF.
      CATCH cx_rap_query_provider.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

 

Create service definition.

Service%20Definition

Service Definition

 

Create service binding for the above service and publish it.

 

Service%20binding

Service binding

 

Frontend

Define additional data source for the backend service (Custom CDS entity service binding) which we have created, under the “dataSources” section of manifest file for fetching PDF data.

Data%20Source

Data Source

 

This OData data source should be mapped to a UI5 model in manifest file under “models” section.

 

Model

Model

 

Controller File Logic –

  1. onPrintPreview method will be called when user select a line and clicks on Print Preview button.
  2. Create object of PDF Viewer and add it to current view.
  3. Open the busy indicator and call the getPDFData() method.
  4. In getPDFData() method, get call was made to get the PDF data from ZAPI_FORM API by passing billing document as a parameter to API.
  5. Promise data (PDF data) will be returned which will be decoded using atob()
  6. Create a Blob (binary large object) using byte array (decoded content) and set the type as  ‘application/pdf’
  7. Create the URL by passing the Blob as parameter to createObjectURL method and set it to the source of PDF viewer
 getPDFData: function () {
     
         // get the Model reference
            var oModel = this.getView().getModel("oFormModel");
          
         //get selected line index
           var selectedIndex = this.getView().byId('xx.sellbuybackreport::sap.suite.ui.generic.template.ListReport.view.ListReport::ZC_OTC_SELLBUYBACK_DETAILS--GridTable')
           .getAggregation("plugins")[0].getSelectedIndex() ;

        // get billing document  from selected index
            var vSelectedBillDoc =  this.getView().byId('xx.sellbuybackreport::sap.suite.ui.generic.template.ListReport.view.ListReport::ZC_OTC_SELLBUYBACK_DETAILS--GridTable').
            getContextByIndex(selectedIndex).getProperty('CorrectBillingDocZG2');
      
            return new Promise((resolve,reject)=>{
            
       // Perform Read operation and pass billingdoc as parameter to URL
               oModel.read( "/ZCUSTOM_FORMAPI(p_billingdoc='" + vSelectedBillDoc + "')/Set"  , 
                   {
                success: function (oData,oResponse){ 
                    resolve(oData);
                },
                error: function (oError) {
                    reject(oError);
                }
            });
            })
        },
        onPrintPreview: async function(oEvent) {
        
            var opdfViewer = new PDFViewer();
            this.getView().addDependent(opdfViewer);   
            var oBusyDialog = new sap.m.BusyDialog({
                title: 'Generating Form...'
            } );
            oBusyDialog.open();

    // Get the PDF data 
            var vPDF = await this.getPDFData();
                         
                let base64EncodedPDF = vPDF.results[0].stream_data;
               
                    let decodedPdfContent = atob(base64EncodedPDF);
                    let byteArray = new Uint8Array(decodedPdfContent.length);
                    for (var i = 0; i < decodedPdfContent.length; i++) {
                    byteArray[i] = decodedPdfContent.charCodeAt(i);
                    }
                    var blob = new Blob([byteArray.buffer], {
                    type: 'application/pdf'
                    });
                    var pdfurl = URL.createObjectURL(blob); 
                    jQuery.sap.addUrlWhitelist("blob"); // register blob url as whitelist
                    opdfViewer.setSource(pdfurl);
                    opdfViewer.setVisible(true);

                opdfViewer.setTitle("Billing Document ");
                console.log('Reached to PDF')
                opdfViewer.open();
                oBusyDialog.close();

            
        }

 

Working of Application —

User%20selects%20a%20line%20and%20click%20on%20Print%20Preview

User selects a line and click on Print Preview

 

Below call was made to the backend API along with selected billing documented in parameter field

 

Print%20Preview

Print Preview

That’s the end of requirement. I appreciate that you have gone through the concept. This was one such requirement where extension is required on top of Fiori list report.

Thanks

Assigned Tags

      12 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Anjali Singh
      Anjali Singh

      This is exactly the same blog which i was searching for. Thanks! Abhishek 🙂

       

      Author's profile photo Abhishek Dalakoti
      Abhishek Dalakoti
      Blog Post Author

      Great Anjali !!

      Author's profile photo Diego Valdivia
      Diego Valdivia

      This was a great post. Thanks Abhishek!

      Author's profile photo Abhishek Dalakoti
      Abhishek Dalakoti
      Blog Post Author

      Thanks !!

      Author's profile photo Manjunath JN
      Manjunath JN

      Hi Abhishek,

      Thanks for writing these blogs, it is very helpful.

       

      Author's profile photo Abhishek Dalakoti
      Abhishek Dalakoti
      Blog Post Author

      Thanks Manjunath !!

      Author's profile photo Satyam Raj
      Satyam Raj

      Precisely explained blog.

      Author's profile photo Abhishek Dalakoti
      Abhishek Dalakoti
      Blog Post Author

      Thanks Satyam!!

      Author's profile photo Nishu Bajpai
      Nishu Bajpai

      Hi Abhishek,

      Thank you for the blog... This has saved us from lot of RnD. Just one learning would like to share here. For our case PDFViewer.Open was not opening the PDF Document and to handle this we have added below command for Generated BLOB:

      JQuery.sap.addURLWhitelist("blob")

      Hope this will help people struggling with same issue.

       

      Author's profile photo Abhishek Dalakoti
      Abhishek Dalakoti
      Blog Post Author

      Thanks Nishu!!

      Author's profile photo Pavan Kumar Jagadeesh
      Pavan Kumar Jagadeesh

      Hi Abhishek,

      Nice blog 🙂 .

      I have few observations from the blogpost:

      • Instead of creating a custom entity and another service just to retrieve the attachment data, you can use the same "print preview" instance action to fetch the attachment data (XSTRING and MIME type) and send the result in the abstract entity so that this overhead of creating the custom entity can be reduced.
      • The business object can hold attachments of different formats. Do you know about the attachment service reusable component which does the job for handling various formats in the application. Please refer the below blogpost for more details.                                                  https://blogs.sap.com/2019/12/16/attachment-service-to-your-rescue-in-s4hana-fiori-elements-using-reuse-components-gos-dms/

      Thanks,

      Pavan

      Author's profile photo Abhishek Dalakoti
      Abhishek Dalakoti
      Blog Post Author

      Thanks Pavan , will surely check this approach as well ..