Skip to content

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: Cinema Demo

Setup

Upload form template

  1. Download it here
  2. Upload it to the abap system, as data source link it to: ZCINE_TICKET_SRVDUpload ZF_CINE_TICKET
  3. 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

  1. Navigate to the app: Adobe Document Services Config

TIP

Your user needs the role: SAP_BR_XXXISTRATOR to see the app

  1. Upload the font Upload font
  2. If everything works and you press: Go, the fonts should appear in the list Upload font success

BTP, ABAP environment

  1. Login to BTP Cloud Foundry Cockpit
  2. Open the subaccount, where Forms service by Adobe is subscribed
  3. Create and Assign yourself a role collection that contains the role ADSAdmin
  4. Navigate to Instances and Subscriptions
  5. Press on the Application: Forms Service by Adobe
  6. 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

  1. Navigate on the left to Fonts Press upload and select your local font file Upload font success

Clone the ABAPgit sample package

  1. Clone the following package and all of its sub packages to the system: ZFOBA_CINEMA_DEMO
  2. Locally publish the service binding ZCINE_APP_UI_SB
  3. 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

  1. Copy the UI development to your local disk: https://github.com/SAP-samples/forms-service-by-adobe-samples/tree/main/ui/cinema
  2. Install npm dependencies by running npm install
  3. Modify the files by replacing XXX with your system url:
    • ui5-deploy.yaml
    • ui5-local.yaml
    • ui5-mock.yaml
    • ui5.yaml
  4. Deploy the application via: npm deploy
  5. Your application should now be deployed to: /sap/bc/ui5_ui5/sap/zcin_bsp_demo
  6. It is not yet accessible. Follow this guide (Step 9) to setup the authorization
  7. 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:

Print Queues

The how to is documented here

You want a short summary, what needs to be done? Expand this section:

Details
  1. Setup SAP_COM_0466
    1. First we need to create a new communication user and assign it to our ABAP system
    2. Go to App: Communication Systems
    3. Select your current system (system name is a random string + hostname should be the same as in our current browser url)
    4. The header should read: This is your own SAP cloud systemOwn System
    5. Press Edit
    6. Go to tab Users for Inbound Communication
    7. Press "+"
    8. Press New User
    9. Choose User Name + Description + Password to your liking
    10. Press Create
    11. Press OK
    12. Press Save
    13. Go to App: Communication Arrangements
    14. Press "New" 15 Select Scenario: SAP_COM_0466 Own System
    15. Communication System: Select your own system we previously modified
    16. Inbound Communication: Choose previously created user and press OKOwn System
    17. Press "Save"
    18. Go to App: Maintain Print Queues
    19. For demo purpose we will create two queues to showcase different behavior based on desired output format
      1. Queue for PDF output
        • Name: DEMO_PDF
        • Format: PDF
      2. Queue for ZPL output (format for zebra label printer, output is plain text)
        • Name: DEMO_ZPL
        • Format: Zebra 203 dpi
  2. 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 CPM

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. Plantuml

abapcds
@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;
}
abapcds
@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
}
abapcds
@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
}
abapcds
@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
}
abapcds
@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
}
abapcds
@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
<?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. Previewer Cinema Example

Usage of the UI application

Book a new Ticket

  1. Start the application Start
  2. You can click on any movie currently airing
  3. It will show you an overview of available seats Start
  4. Select ticket type + seat
  5. Press Book, a notification will popup informing you of your selection and the seat will be blocked
  6. 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: Preview

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: Preview

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. Start

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.

  1. Add a table of processes to the behavior class
abap
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.
  1. Add the annotation with additional save to unlock the RAP saver methods
abapcds
define behavior for ZCINE_I_TICKET //alias <alias_name>
persistent table zcine_a_buy
lock master
authorization master ( instance )
with additional save
  1. In the local type of the behavior implementation class, we can now queue all temporary saved processes and cleanup the global data
abap
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.
  1. 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
abap
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.

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.

abap
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 }| 
).