Note: After publishing this guide I got a lot of positive feedback from the community but a lot of people were asking for a way to adopt this approach to S/4 HANA Cloud Developer Extensibility. Firstly I would like to thank you all for your active feedback. A guide to adopt this scenario to S/4 HANA Cloud was added to this blog post. Feel free to try it out.
I'm sure that by now you have already started your Cloud journey in ABAP. Maybe you've just published a new business scenario, extended a CDS view, or debugged in your classes.
In this blog, I want to present a business case almost every customer needs to implement in some way:
Printing!
This blog post might seem similar to this one:
https://blogs.sap.com/2020/04/03/generate-pdfs-in-sap-cloud-platform-abap-environment/. In this post however, we are exclusively using services in Cloud Foundry, offer a full product scenario, a setup guide and example objects.
This guide is designed to be used along with the objects published in the SAP-samples Github repository:
https://github.com/SAP-samples/forms-service-by-adobe-samples. This will create a working scenario out of the box and you only need to perform the initial setup. You can import the demo objects from the Github repository via ABAPgit, activate all objects, and execute
zcl_dsag_fill_data to write some test data to your database.
The form templates will be stored directly on the SAP Business Technology Platform. So they could theoretically be accessed by multiple systems. For this guide a demo template is provided and can be accessed here:
Demo Template. After the initial setup, you can publish a demo invoice to your print queue (see
Running the example).
- Learning by example
- Infrastructure
- Services and what they are for
- SAP Destination service
- ABAP environment
- SAP Forms Service by Adobe (+API)
- Setup
- SAP Forms Service by Adobe
- SAP Destination service
- ABAP environment
- Print Queue
- Connect to SAP Destination service
- SAP Forms Service by Adobe
- Running the example
- Upload the demo template
- Run the example program
- Understanding and extending the demo project
- Data service
- ABAP Template Store Client
- Initialize the client connection
- Upload a data schema to a form in the template store
- Download an existing template in a form on the template store
- Form Layout Development
- Support for S/4 HANA Cloud Developer Extensibility
- Connecting the template store
- Adaptation to ABAP Template Store Client
Learning by example
Imagine that your product management team starts a new project to realize a new purchase order solution in SAP Business Technology Platform. In this blog, we want to explore how such a requirement could be realized using SAP BTP services and the SAP BTP, ABAP environment. Our goal will be to create the following output:
Our desired output
Infrastructure
Before we start implementing this example, we first need to get a rough overview of our Cloud Foundry space.
Tools:
Here is a short list of services that are needed:
Services and what they are used for
Destination Service
The SAP Destination service is used to connect services and applications to third-party services. Connection information including credentials can be maintained centrally using this service.
The
SAP Destination service will be used by the SAP BTP, ABAP environment to connect to the
Template Store hosted by the
Forms Service by Adobe API.
ABAP environment
The "brain" of our operation. We will store and define our business data and data services in ABAP. The
ABAP environment will also connect to all the external services and provide the rendered print files via the integrated print queue.
SAP Forms service by Adobe is the cloud-based form rendering solution of SAP. The technology is based on the Adobe Document Services (ADS).
With the
Forms service by Adobe API we offer a central Adobe form template storage solution (template store).
Setup
Before we can start programming our example in ABAP and Adobe LiveCycle Designer 11.0 for SAP solutions we first need to set up all the dependent services. The following section assumes you are familiar with the SAP BTP, Cloud Foundry environment. Especially how to subscribe and consume services via the cockpit. If you are new to this product I recommend reading the dedicated
help page before continuing.
- Create a new subscription for Forms Service by Adobe (plan: default)
- Create a new role collection and assign the following roles to it:
- ADSAdmin for accessing Forms Service by Adobe Config UI (used for maintaining fonts, xdc, xci, etc.)
- TemplateStoreAdmin for accessing the template store UI
- Create a new instance and service key for the service: Forms Service by Adobe (plan: standard)
- Create a new instance and service key for the service: Forms Service by Adobe API (plan: standard)
You might be wondering why we need subscription and service instances. The initial subscription is necessary to enable the service config UIs (ADS Config UI / Template Store Admin UI) and to set up your tenant data. Without it, the services will not function as intended. To access the UIs, you need to authorize the users via a custom role collection.
Service instances are used to give external systems access to the service. External systems don't have a user maintained in Cloud Foundry, so we need specific credentials to authorize these systems.
The service instance for SAP Forms service will be used by the ABAP environment to render a print file (e.g. PDF), and the service instance to Forms Service by Adobe API enables us to connect to the template store.
Destination
To allow the ABAP environment to access the template store, we need to pass on the information of the service key (for the Forms Service by Adobe API). To avoid hardcoding this connection information we will create a specific destination to the template store using the Cloud Foundry destination service.
Once we have connected the destination service to the ABAP environment, we can reuse all the maintained destinations.
To create a destination to the template store you need to:
- Create a new instance and service key for the service: Destination
- Maintain a new destination pointing to the service instance of Forms Service by Adobe API using the corresponding service key
ABAP environment
Print Queue
To provide our physical printer with the print files stored in the print queue we need to use a proxy service that retrieves the files and sends them to the corresponding printer. To enable this service, we need to maintain a communication user, a communication arrangement and a new specific print queue.
Follow the setup guide here:
https://blogs.sap.com/2017/08/07/cloud-print-manager-installation-and-configuration/. Make sure you note down the name of your print queue. This guide assumes the name of the print queue to be
PRINT_QUEUE, if not, you need to adjust the final script accordingly.
Connect to CF Destination service
Create a new communication arrangement (scenario: SAP_COM_0276) with the service key of the destination service instance.
In the newest release of SAP BTP, ABAP environment this setup process has been simplified. Open the app Communication Arrangements, choose scenario SAP_COM_0503 and upload your service key (for Forms service by Adobe, not the REST API !). The arrangement as well as the communication systems will be automatically configured. You can then skip the steps below.
- Download the service key for the instance of Forms Service by Adobe (from SAP BTP Cockpit)
- Open the Communication Systems app.
- Create a new communication system (to connect to Forms Service by Adobe service instance):
- System ID: <any name>
- System Name: <any name>
- Host Name: <uri in service key>
- Auth. Endpoint: <uaa.url in service key>/oauth/authorize
- Token Endpoint: <uaa.url in service key>/oauth/token
- Create new users for outbound communication:
- Authentication Method: OAuth 2.0
- OAuth 2.0 Client ID: <uaa.clientid in service key>
- Client Secret: <uaa.clientsecret in service key>
- Save the system
- Open the Communication Arrangement app.
- Create a new communication arrangement (scenario: SAP_COM_0503):
- Communication System: <system that connects to the Forms Service by Adobe service instance>
- OAuth 2.0 Client ID: <Should be automatically selected>
- Path: </AdobeDocumentServicesSec/Config>
Expected result for check connection (Ping Error)
Running the example
If you're just interested in a quick proof-of-concept project or if you want to start extending a basic example, a sample project is provided.
Upload the demo template
- Access the Template Store Admin UI
- Create a new form
- Upload the demo template (https://github.com/SAP-samples/forms-service-by-adobe-samples/blob/main/abap/Form.xdp)
Run the example program
I created a short runnable class that renders a PDF using the business data and template from the template store. The generated print files will then be sent to the print queue. If you plan to run this, make sure the hard-coded values are adjusted to your environment.
Make sure you have generated the test data beforehand by running class
zcl_dsag_fill_data.
CLASS zcl_dsag_execute_fdp DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES: if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_dsag_execute_fdp IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
try.
"Initialize Template Store Client
data(lo_store) = new ZCL_FP_TMPL_STORE_CLIENT(
"name of the destination (in destination service instance) pointing to Forms Service by Adobe API service instance
iv_name = 'restapi'
"name of communication arrangement with scenario SAP_COM_0276
iv_service_instance_name = 'SAP_COM_0276'
).
out->write( 'Template Store Client initialized' ).
"Initialize class with service definition
data(lo_fdp_util) = cl_fp_fdp_services=>get_instance( 'ZDSAG_BILLING_SRV_DEF' ).
out->write( 'Dataservice initialized' ).
"Get initial select keys for service
data(lt_keys) = lo_fdp_util->get_keys( ).
lt_keys[ name = 'ID' ]-value = '1'.
data(lv_xml) = lo_fdp_util->read_to_xml( lt_keys ).
out->write( 'Service data retrieved' ).
data(ls_template) = lo_store->get_template_by_name(
iv_get_binary = abap_true
iv_form_name = 'DSAG_DEMO' "<= form object in template store
iv_template_name = 'TEMPLATE' "<= template (in form object) that should be used
).
out->write( 'Form Template retrieved' ).
cl_fp_ads_util=>render_4_pq(
EXPORTING
iv_locale = 'en_US'
iv_pq_name = 'PRINT_QUEUE' "<= Name of the print queue where result should be stored
iv_xml_data = lv_xml
iv_xdp_layout = ls_template-xdp_template
is_options = value #(
trace_level = 4 "Use 0 in production environment
)
IMPORTING
ev_trace_string = data(lv_trace)
ev_pdl = data(lv_pdf)
).
out->write( 'Output was generated' ).
cl_print_queue_utils=>create_queue_item_by_data(
"Name of the print queue where result should be stored
iv_qname = 'PRINT_QUEUE'
iv_print_data = lv_pdf
iv_name_of_main_doc = 'DSAG DEMO Output'
).
out->write( 'Output was sent to print queue' ).
catch cx_fp_fdp_error zcx_fp_tmpl_store_error cx_fp_ads_util.
out->write( 'Exception occurred.' ).
endtry.
out->write( 'Finished processing.' ).
ENDMETHOD.
ENDCLASS.
If everything worked correctly you should receive a PDF similar to the one shown at the beginning. You can now take a deeper look at the CDS views, classes, and form layout provided.
Understanding and extending the demo project
The example output works. Now you can proceed by extending the objects in your system to fit your use cases.
For form development in general, I recommend the following steps:
- Design your data service.
- Create your form template based on your data service.
- Design your output process in ABAP.
Data service
In our example, the root node will be ZI_DSAG_BILL_ORDER. The root node is the starting node of our resulting data tree. This node is usually determined by checking which nodes have no associations pointing to them.
Given the data service above, the output of the data service might look like this:
To generate data from our CDS views we need to expose them via a service definition (a service binding is not needed for our use case).
@EndUserText.label: 'Service Destination for billing example'
define service ZDSAG_BILLING_SRV_DEF {
expose ZI_DSAG_BILL_ITEM;
expose ZI_DSAG_BILL_ORDER;
expose ZI_DSAG_PRODUCT;
expose ZI_DSAG_RECEIVER;
}
The xml data can now be generated using the following example:
"Initiate data service from definition
data(lo_fdp_util) = cl_fp_fdp_services=>get_instance( 'ZDSAG_BILLING_SRV_DEF' ).
"Retrieve the key fields of the root node
data(lt_keys) = lo_fdp_util->get_keys( ).
"Fill out key fields (depends on your actual data)
lt_keys[ name = 'ID' ]-value = '1'.
"Execute the data service and retrieve the data tree
data(lv_xml) = lo_fdp_util->read_to_xml( lt_keys ).
"Generate data schema from your service
data(lv_schema) = lo_fdp_util->get_xsd( )
ABAP Template Store Client
To upload or download a form template design to the ABAP environment I implemented a template store client as Z-coding (available in the GitHub repository). Here are some essential examples showing how this client is used in ABAP:
Initialize the client connection
"Initialize Template Store Client
data(lo_store) = new ZCL_FP_TMPL_STORE_CLIENT(
"destination name connecting to the template store in cf destination service
iv_name = 'restapi'
"name of the comm. arrangement connecting the cf destination service
iv_service_instance_name = 'SAP_COM_0276'
).
try.
"Get data schema for form object
lo_store->get_schema_by_name( iv_form_name = 'DSAG_DEMO' ).
catch zcx_fp_tmpl_store_error into data(lo_tmpl_error).
"Error occurred
"Check if error occurred because no schema was found
if lo_tmpl_error->mv_http_status_code = 404.
"Upload a new schema to form object
lo_store->set_schema(
iv_form_name = 'DSAG_DEMO'
"Generate schema from the data service util
is_data = value #( note = '' schema_name = 'schema' xsd_schema = lo_fdp_util->get_xsd( ) )
).
else.
"Get error: lo_tmpl_error->get_longtext( ).
endif.
endtry.
data(ls_template) = lo_store->get_template_by_name(
"Should the xdp template be retrieved or only metadata
iv_get_binary = abap_true
"Name of the form object in the template store
iv_form_name = 'DSAG_DEMO'
"Name of the template in the template store
iv_template_name = 'TEMPLATE'
).
"To access the template data use:
ls_template-xdp_template
It's often very helpful to have the data schema of the business service available before you start to to develop your form template. It's advisable to first develop your data service and then execute a classrun to upload the schema to a form object in the template store (see the example above:
Upload a data schema to a form in the template store). You can then download the xsd schema file from the template store.
Alternatively, the generated xsd file is also available on
Github.
To import the schema into the Adobe LiveCycle Designer 11.0 for SAP solutions:
- Open the application and go to the tab: Data View.
- Right-click an empty area and select: New Data Connection...
- Create a new connection from a schema:
- Now the Adobe LiveCycle Designer 11.0 for SAP solutions will offer you syntax highlighting for the data binding of the fields.
Support for S/4 HANA Cloud Developer Extensibility
When first reading this guide you might be eager to try this guide using developer extensibility in S/4 HANA Cloud. But soon you might have noticed that neither the communication arrangement SAP_COM_0503 nor the SAP Destination service are available. In order to circumvent this limitation we need to make additional enhancements.
Firstly setting up the communication arrangement SAP_COM_0503 is not needed as S/4 HANA Cloud systems are shipped with a working connection to Forms service by Adobe, so no additional setup is needed from your side.
Secondly note that form templates uploaded to the system via app "Maintain Form Templates" are not accessible in the cloud developer extensibility (see
this limitation is documented here ).
Instead of using the forms in the system we will connect to the template store hosted in the SAP Forms service by Adobe REST API. Of course you can adapt this example to use your own external API to store and query templates.
Connecting the template store
In order to connect to our external template store we need to setup a custom communication arrangement. To do this, open the ADT tools and login to the system. After this create a new Outbound Service (e.g. ZADSTEMPLSTORE_REST). Choose HTTP as type.
Now create a new custom
communication scenario to connect to the template store.
Make sure to press the publish button after every modification you do to this scenario. The allowed instances should only by one per client.
Add a new outbound service that points to your newly created one.
You have now successfully created a custom communication scenario to integrate the template store to your system. Next we need to configure the connection details to our subscription of Forms service by Adobe REST API. Make sure you have the service key handy ( see chapter 4.3.2 ).
At first login to the Fiori Launchpad UI of the system and open the app Communication Systems.
Create a new Communication System (e.g. ID/Name: ADS_REST_API) and maintain all connection settings.
The system should be configured like this:
- System ID: <any name>
- System Name: <any name>
- Host Name: <uri in service key>
- Auth. Endpoint: <uaa.url in service key>/oauth/authorize
- Token Endpoint: <uaa.url in service key>/oauth/token
- Create new users for outbound communication:
- Authentication Method: OAuth 2.0
- OAuth 2.0 Client ID: <uaa.clientid in service key>
- Client Secret: <uaa.clientsecret in service key>
Almost done, now we simply open the app
Communication Arrangements. We create a new arrangement using our custom scenario (ZADSTEMPLSTORE) and point to our custom communication system.
Adaptation to ABAP Template Store Client
Due to the changed approach in providing a connection to the template store the reuse class was adjusted to reflect this change. If you have downloaded the example previously you need to pull the latest changes from GitHub again.
Now initialize the template store client like this:
"Initialize Template Store Client
data(lo_store) = new ZCL_FP_TMPL_STORE_CLIENT(
"name of the custom comm arrangement to connect to the template store
iv_service_instance_name = 'ZADSTEMPLSTORE'
"disable destination service, switch to custom communication arrangement
iv_use_destination_service = abap_false
).
And now you are good to go. For references how to create the data service / design the form template see the guide above.
I hope this guide gave you a good overview of what is possible in SAP BTP using cloud-native services.
Happy printing and best regards,
Pascal Bremer