Cinema Demo
In this demo we explore an advanced usecase, where we build a full end-to-end application.
Features:
- RAP App
- bgPF Integration
- Print Queue Integration
- Custom fonts for rendering
Preview: 
Setup
Upload form template
- Download it here
- Upload it to the abap system, as data source link it to: ZCINE_TICKET_SRVD

- Enable embed font usage, so custom fonts will be embedded to the output
Upload custom font
Download the SAP-Icon Font and follow the system specific section below.
S/4 HANA Cloud Public Edition
- Navigate to the app: Adobe Document Services Config
TIP
Your user needs the role: SAP_BR_XXXISTRATOR to see the app
- Upload the font

- If everything works and you press: Go, the fonts should appear in the list

BTP, ABAP environment
- Login to BTP Cloud Foundry Cockpit
- Open the subaccount, where Forms service by Adobe is subscribed
- Create and Assign yourself a role collection that contains the role ADSAdmin
- Navigate to Instances and Subscriptions
- Press on the Application: Forms Service by Adobe
- Press on Setup
TIP
Note: If you see 'Forbidden' error, check if the role was assigned to your user and try again in a private browser session
- Navigate on the left to Fonts Press upload and select your local font file

Clone the ABAPgit sample package
- Clone the following package and all of its sub packages to the system: ZFOBA_CINEMA_DEMO
- Locally publish the service binding ZCINE_APP_UI_SB
- Run ZCL_FDP_CINEMA_FILL_DATA once in ADT, to reset and populate demo data to the system. This can be repeated later directly in the app.
Upload the UI5 App
A detailed guide is available here
- Copy the UI development to your local disk: https://github.com/SAP-samples/forms-service-by-adobe-samples/tree/main/ui/cinema
- Install npm dependencies by running
npm install - Modify the files by replacing XXX with your system url:
- ui5-deploy.yaml
- ui5-local.yaml
- ui5-mock.yaml
- ui5.yaml
- Deploy the application via:
npm deploy - Your application should now be deployed to: /sap/bc/ui5_ui5/sap/zcin_bsp_demo
- It is not yet accessible. Follow this guide (Step 9) to setup the authorization
- After the setup is completed, you can follow the link from vscode to open the app
Setup Print Queue
You need to configure two print queues, to simulate different output devices. Here a suggestion:

The how to is documented here
You want a short summary, what needs to be done? Expand this section:
Details
- Setup SAP_COM_0466
- First we need to create a new communication user and assign it to our ABAP system
- Go to App: Communication Systems
- Select your current system (system name is a random string + hostname should be the same as in our current browser url)
- The header should read: This is your own SAP cloud system

- Press Edit
- Go to tab Users for Inbound Communication
- Press "+"
- Press New User
- Choose User Name + Description + Password to your liking
- Press Create
- Press OK
- Press Save
- Go to App: Communication Arrangements
- Press "New" 15 Select Scenario: SAP_COM_0466

- Communication System: Select your own system we previously modified
- Inbound Communication: Choose previously created user and press OK

- Press "Save"
- Go to App: Maintain Print Queues
- For demo purpose we will create two queues to showcase different behavior based on desired output format
- Queue for PDF output
- Name: DEMO_PDF
- Format: PDF
- Queue for ZPL output (format for zebra label printer, output is plain text)
- Name: DEMO_ZPL
- Format: Zebra 203 dpi
- Queue for PDF output
- Configure SAP Cloud Print Manager (CPM) for Pull Integration Using your system url + user / password of the inbound communication user, you should be able to add the print queues to client

Data Model
Here you can see the sample data model. You can toggle through the different objects and see how the data is modeled. There is also an example XML available to see the potential output.
@EndUserText.label: 'Cinema Demo - Service Definition'
@ObjectModel.leadingEntity.name: 'ZCINE_I_TICKET'
define service ZCINE_TICKET_SRVD {
expose ZCINE_I_TICKET as Ticket;
expose ZCINE_I_MOVIE as Movie;
expose ZCINE_I_ROOM as Room;
expose ZCINE_I_SEAT as Seat;
expose ZCINE_I_CAL as Schedule;
}@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Cinema Demo - Ticket'
@ObjectModel.supportedCapabilities: [ #OUTPUT_FORM_DATA_PROVIDER ]
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZCINE_I_TICKET as select from zcine_a_buy
association [1..1] to ZCINE_I_CAL as _schedule
on $projection.Scheduleid = _schedule.Id
association [1..1] to ZCINE_I_SEAT as _seat
on $projection.Seatid = _seat.Id
{
key id as Id,
scheduleid as Scheduleid,
seatid as Seatid,
pricingtier as Pricingtier,
currency as CurrencyCode,
@Semantics.amount.currencyCode: 'CurrencyCode'
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_FDP_CINEMA_CALC_PRICE'
cast( 0 as abap.curr( 16,2 )) as TotalPrice,
pdf as Pdf,
rendered as Rendered,
/* Associations */
_seat as Seat,
_schedule as Schedule
}@EndUserText.label: 'Cinema Demo - Movie'
@AccessControl.authorizationCheck: #NOT_REQUIRED
define view entity ZCINE_I_MOVIE as select from zcine_a_mov
{
key id as Id,
title as Title,
description as Description,
duration as Duration,
logo as Logo
}@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Cinema Demo - Room'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZCINE_I_ROOM as select from zcine_a_roo
{
key id as Id,
name as Name,
address as Address,
zip as Zip,
country as Country
}@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Cinema Demo - Seat'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZCINE_I_SEAT as select from zcine_a_seat
association [1..1] to ZCINE_I_ROOM as _room
on $projection.Roomid = _room.Id
{
key id as Id,
roomid as Roomid,
name as Name,
pricetier as Pricetier,
_room as Room
}@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Cinema Demo - Schedule'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZCINE_I_CAL as select from zcine_a_cal
association [1..1] to ZCINE_I_ROOM as _room
on $projection.Roomid = _room.Id
association [1..1] to ZCINE_I_MOVIE as _movie
on $projection.Movieid = _movie.Id
{
key id as Id,
begindate as Begindate,
enddate as Enddate,
begintime as Begintime,
endtime as Endtime,
movieid as Movieid,
roomid as Roomid,
_room as Room,
_movie as Movie
}<?xml version="1.0" encoding="utf-8"?>
<Form version="2">
<Ticket>
<Id>BtvpcPMMH+CvmRqWLRHhew==</Id>
<Scheduleid>BtvpcPMMH+CvmRqGdT8Bew==</Scheduleid>
<Seatid>BtvpcPMMH+CvmRqGdUBBew==</Seatid>
<Pricingtier>2</Pricingtier>
<CurrencyCode>EUR</CurrencyCode>
<TotalPrice>12.00</TotalPrice>
<Pdf></Pdf>
<Rendered>false</Rendered>
<Schedule>
<Id>BtvpcPMMH+CvmRqGdT8Bew==</Id>
<Begindate>20241101</Begindate>
<Enddate>20241101</Enddate>
<Begintime>100000</Begintime>
<Endtime>113000</Endtime>
<Movieid>BtvpcPMMH+CvmRqGdT6hew==</Movieid>
<Roomid>BtvpcPMMH+CvmRqGdT4Bew==</Roomid>
<Movie>
<Id>BtvpcPMMH+CvmRqGdT6hew==</Id>
<Title>UI5 - Rise of the Phoenix</Title>
<Description>From the ashes of webdynpro a new framework may arise.</Description>
<Duration>5400 </Duration>
<Logo>::base64_img::</Logo>
</Movie>
<Room>
<Id>BtvpcPMMH+CvmRqGdT4Bew==</Id>
<Name>Auditorium</Name>
<Address>SAP-Allee 37</Address>
<Zip>68789</Zip>
<Country>Germany</Country>
</Room>
</Schedule>
<Seat>
<Id>BtvpcPMMH+CvmRqGdUBBew==</Id>
<Roomid>BtvpcPMMH+CvmRqGdT4Bew==</Roomid>
<Name>B0</Name>
<Pricetier>2</Pricetier>
<Room>
<Id>BtvpcPMMH+CvmRqGdT4Bew==</Id>
<Name>Auditorium</Name>
<Address>SAP-Allee 37</Address>
<Zip>68789</Zip>
<Country>Germany</Country>
</Room>
</Seat>
</Ticket>
</Form>PDF Preview
You can also use the Previewer to review your PDF before calling the app. 
Usage of the UI application
Book a new Ticket
- Start the application

- You can click on any movie currently airing
- It will show you an overview of available seats

- Select ticket type + seat
- Press Book, a notification will popup informing you of your selection and the seat will be blocked
- Refresh the list Generated Tickets and wait for your ticket to appear
Preview Ticket
Once a ticket was generated, it will be listed under: Generated Tickets In the list, press the first action button:
This will load the persisted PDF from the database table.
Sent Ticket
Once a ticket was generated, it will be listed under: Generated Tickets In the list, press the second action button:
This will show a dialog, where you can input the name of your created print queue. Depending on the output type defined in the print queue, the resulting document format will change. 
Reset Demo
It can happen that the demo becomes quite messy after a while. Click the button at the upper right, to reset the demo, deleting all tickets and generating a new airing schedule.
Further details
Usage of bgPF
This demo is using bgPF for rendering the document. This has a couple of benefits:
- The RAP transactional flow is not blocked and document rendering is deligated to the background
- In case unexpected errors occur, the rendering job can be retried
- The transactional data is saved independant from document rendering
You can check the implementation for these processes here:
By default queuing bgPF processes requires to be in the RAP transactional state save. When we call actions directly this state might be unreachable from the action itself.
In order for this to work, the following technique is applied.
- Add a table of processes to the behavior class
CLASS zbp_cine_i_ticket DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zcine_i_ticket.
PUBLIC SECTION.
CLASS-DATA ct_processes TYPE STANDARD TABLE OF REF TO if_bgmc_process.
ENDCLASS.
CLASS zbp_cine_i_ticket IMPLEMENTATION.
ENDCLASS.- Add the annotation with additional save to unlock the RAP saver methods
define behavior for ZCINE_I_TICKET //alias <alias_name>
persistent table zcine_a_buy
lock master
authorization master ( instance )
with additional save- In the local type of the behavior implementation class, we can now queue all temporary saved processes and cleanup the global data
CLASS lsc_ZCINE_I_TICKET IMPLEMENTATION.
METHOD save_modified.
LOOP AT zbp_cine_i_ticket=>ct_processes ASSIGNING FIELD-SYMBOL(<process>).
TRY.
<process>->save_for_execution( ).
CATCH cx_bgmc.
" Skip error handling
ENDTRY.
DELETE zbp_cine_i_ticket=>ct_processes.
ENDLOOP.
ENDMETHOD.
METHOD cleanup_finalize.
ENDMETHOD.
ENDCLASS.- Inside of our actions, we can now create new bgPF processes and queue them to our temporary table and wait for them to be picked up later
METHOD AddRenderOperation.
DATA factory TYPE REF TO if_bgmc_process_factory.
DATA operation TYPE REF TO zcl_cine_app_op_render.
DATA process TYPE REF TO if_bgmc_process.
DATA(op_input) = input.
DATA(formname) = CONV fpname( |ZF_CINE_TICKET| ).
factory = cl_bgmc_process_factory=>get_default( ).
DATA(form) = cl_fp_form_reader=>create_form_reader( formname ).
DATA(fdp) = cl_fp_fdp_services=>get_instance( form->get_fdp_name( ) ).
DATA(fdp_keys) = fdp->get_keys( ).
fdp_keys[ name = 'ID' ]-value = id.
operation = NEW zcl_cine_app_op_render( ).
op_input-formname = formname.
MOVE-CORRESPONDING fdp_keys TO op_input-fdp_it_select.
operation->if_bgmc_operation_aif~set_input( op_input ).
process = factory->create( )->set_name( 'RENDER_PDF' )->set_operation_tx_uncontrolled( operation ).
APPEND process TO zbp_cine_i_ticket=>ct_processes.
ENDMETHOD.Print Queue Integration
The reuse classes for interacting with the print queue should only be called in a RAP save context. Since we are making use of the bgPF uncontrolled feature, this limitation does not apply here.
Sending items to the print queue is straight forward. Simply create a new print queue with our document as input and provide a name.
cl_fp_ads_util=>render_4_pq(
EXPORTING
iv_pq_name = input-send_to_pq
iv_locale = iso
iv_xml_data = xml
iv_xdp_layout = form->get_layout( )
is_options = VALUE #( trace_level = input-render_trace )
IMPORTING
ev_pdl = pdf ).
cl_print_queue_utils=>create_queue_item_by_data(
" Name of the print queue where result should be stored
iv_qname = input-send_to_pq
iv_print_data = pdf
iv_name_of_main_doc = |Ticket-{ id }|
).