Introduction:
In this blog I need to demonstrate the process of enabling a smart form PDF download within a RAP application using a custom action button in an on-premises setup.
This is a common requirement for generating and downloading documents like invoices, Purchase orders etc., from a Fiori elements app.
In classic ABAP, generating and downloading files was often done using methods like GUI_DOWNLOAD, but this approach is not compatible with RAP.
In RAP, the recommended way to implement download functionality is by using streams, combined with additional logic to handle the pdf generation and delivery to the front-end.
Step 1 : Create Database Tables
First we will be creating a database table to store the sales header(ZCH_DT_SALES_HED) and sales item (ZCH_DT_SALES_IT)data.
@EndUserText.label : ‘sales header table’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zch_dt_sales_hed
{
key sid : abap.char(10) not null;
created_on : abap.dats;
document_type : abap.char(4);
customer : abap.char(10);
attachment : abap.rawstring(0);
mimetype : abap.char(128);
filename : abap.char(128);
last_changed_at : abp_lastchange_tstmpl;
}
Item table
@EndUserText.label : ‘sales item table’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zch_dt_sales_it
{
key sid : abap.char(10) not null;
key sdoc_item : abap.numc(6) not null;
material_no : abap.char(40);
@Semantics.quantity.unitOfMeasure : ‘zch_dt_sales_it.unit’
quantity : abap.quan(15,3);
unit : abap.unit(3);
@Semantics.amount.currencyCode : ‘zch_dt_sales_it.currency’
net_value : abap.curr(15,2);
currency : abap.cuky;
}
Step 2 : Define Interface view on top of Sales Header and item tables
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘base view for sales header’
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define root view entity zch_i_saleshed
as select from zch_dt_sales_hed
composition [0..*] of zch_i_salesit as _sitem
{
key sid as Sid,
created_on as CreatedOn,
document_type as DocumentType,
customer as Customer,
@Semantics.largeObject : { fileName: ‘filename’ , mimeType: ‘Mimetype’ ,
contentDispositionPreference: #ATTACHMENT}
attachment as Attachment,
@Semantics.mimeType: true
mimetype as Mimetype,
filename as Filename,
last_changed_at as LastChangedAt,
_sitem
}@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘base view for sales item’
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity zch_i_salesit as select from zch_dt_sales_it
association to parent zch_i_saleshed as _sheader on $projection.Sid = _sheader.Sid
{
key sid as Sid,
key sdoc_item as SdocItem,
material_no as MaterialNo,
@Semantics.quantity.unitOfMeasure : ‘Unit’
quantity as Quantity,
unit as Unit,
@Semantics.amount.currencyCode : ‘Currency’
net_value as NetValue,
currency as Currency,
_sheader
}
Step 3 : Define Consumption view on top of header and item interface views
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘projection view for sales header’
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define root view entity zch_c_saleshed
provider contract transactional_query
as projection on zch_i_saleshed
{
key Sid,
CreatedOn,
DocumentType,
Customer,
@Semantics.largeObject : { fileName: ‘filename’ , mimeType: ‘Mimetype’ ,
contentDispositionPreference: #ATTACHMENT}
Attachment,
@Semantics.mimeType: true
Mimetype,
Filename,
LastChangedAt,
/* Associations */
_sitem : redirected to composition child zch_c_salesit
}@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘projection view for sales item’
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define view entity zch_c_salesit
as projection on zch_i_salesit
{
key Sid,
key SdocItem,
MaterialNo,
@Semantics.quantity.unitOfMeasure : ‘unit’
Quantity,
Unit,
@Semantics.amount.currencyCode: ‘Currency’
NetValue,
Currency,
/* Associations */
_sheader : redirected to parent zch_c_saleshed
}
Step 4 : Define Metadata Extension for Header and item data
@Metadata.layer:#CORE
annotate entity zch_c_saleshed
with
{
.facet: [
{
id: ‘SalesDocHeader’,
purpose: #STANDARD,
position: 10,
label: ‘Header’,
type: #IDENTIFICATION_REFERENCE
},
{
id: ‘SalesDocItem’,
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: ‘Items’,
position: 20,
targetElement: ‘_sitem’
}
]
: { lineItem: [{position: 10 , label: ‘Salesorder Id’ }],
identification: [{ position: 10 ,label :’Salesorder Id’ },
{ type: #FOR_ACTION, position:10 ,dataAction: ‘download’, label: ‘Download Invoice’ }] }
Sid;
: { lineItem: [{position: 20 , label: ‘Created on’ }],
identification: [{ position: 20 ,label :’Created on’ }] }
CreatedOn;
: { lineItem: [{position: 30 , label: ‘Document type’ }],
identification: [{ position: 30 ,label :’Document type’ }] }
DocumentType;
: { lineItem: [{position: 40 , label: ‘Customer id’ }],
identification: [{ position: 40 ,label :’Customer id’ }] }
Customer;
.identification: [
{ position: 60, label: ‘Attachment’ }
]
Attachment;
}@Metadata.layer: #CORE
annotate entity zch_c_salesit
with
{
.facet: [
{
id: ‘SalesDocItem’,
purpose: #STANDARD,
position: 10,
label: ‘Header’,
type: #IDENTIFICATION_REFERENCE
}]
@UI : { lineItem: [{position: 10 , label: ‘Salesorder Id’ }],
identification: [{ position: 10 , label :’Salesorder Id’ }] }
Sid;
: { lineItem: [{position: 20 , label: ‘Sales Document Item’ }],
identification: [{ position: 20 ,label :’Sales Document Item’ }] }
SdocItem;
: { lineItem: [{position: 30 , label: ‘Material no’ }],
identification: [{ position: 30 ,label :’Material no’ }] }
MaterialNo;
: { lineItem: [{position: 40 , label: ‘Quantity’ }],
identification: [{ position: 40 ,label :’Quantity’ }] }
Quantity;
: { lineItem: [{position: 50 , label: ‘Unit’ }],
identification: [{ position: 50 ,label :’Unit’ }] }
Unit;
: { lineItem: [{position: 60 , label: ‘Net value’ }],
identification: [{ position: 60 ,label :’Net value’ }] }
NetValue;
: { lineItem: [{position: 70 , label: ‘Currency’ }],
identification: [{ position: 70 ,label :’Currency’ }] }
Currency;
}
Step 5: Define Behavior Definitions
We need to define the behavior definition for sales order header and item. This includes setting up the managed implementation, defining standard CRUD operations, defining early numbering for header and item keys and defining the Download action.
*Here Download action button returns the abstract entity instead of sales order itself. This abstract entity is used only for transferring the file back to the UI.
managed implementation in class zbp_ch_i_saleshed unique;
strict ( 2 );
define behavior for zch_i_saleshed //alias salesheader
persistent table zch_dt_sales_hed
lock master
authorization master ( instance )
//etag master <field_name>
early numbering
{
create;
update;
delete;
action download result [1] zch_download_file;
side effects { action download affects $self ;}
association _sitem { create; }
field ( readonly ) Sid,Attachment, Mimetype, Filename;
mapping for zch_dt_sales_hed
{
Sid = sid;
CreatedOn = created_on;
DocumentType = document_type;
Customer = customer;
Filename = filename;
Mimetype = mimetype;
Attachment = attachment;
LastChangedAt = last_changed_at;
}
}
define behavior for zch_i_salesit //alias salesitem
persistent table zch_dt_sales_it
lock dependent by _sheader
authorization dependent by _sheader
//etag master <field_name>
early numbering
{
update;
delete;
field ( readonly ) sid,SdocItem ;
association _sheader;
mapping for zch_dt_sales_it
{
Sid = sid;
SdocItem = sdoc_item;
MaterialNo = material_no;
Quantity = quantity;
Unit = unit;
NetValue = net_value;
Currency = currency;
}
}
Abstract Entity :
@EndUserText.label: ‘abstract entity for file download’
define root abstract entity Zch_Download_File
{
@Semantics.largeObject : { fileName: ‘filename’ , mimeType: ‘Mimetype’ ,
contentDispositionPreference: #ATTACHMENT }
attachment : abap.rawstring(0);
@Semantics.mimeType: true
mimetype : abap.char(128);
filename : abap.char(128);
}
Define behavior definition on top of projection view
projection;
strict ( 2 );
define behavior for zch_c_saleshed //alias <alias_name>
{
use create;
use update;
use delete;
use action download ;
use association _sitem { create; }
}
define behavior for zch_c_salesit //alias <alias_name>
{
use update;
use delete;
use association _sheader;
}
Step 6: Implement the Behavior handler class
In this step we will be implementing the behavior handler for our sales order RAP BO.
This class takes care of early numbering as well as the download action which generates and streams the invoice pdf back to UI.
CLASS lhc_zch_i_saleshed DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR zch_i_saleshed RESULT result.
METHODS download FOR MODIFY
IMPORTING keys FOR ACTION zch_i_saleshed~download RESULT result.
METHODS earlynumbering_cba_sitem FOR NUMBERING
IMPORTING entities FOR CREATE zch_i_saleshed_sitem.
METHODS earlynumbering_create FOR NUMBERING
IMPORTING entities FOR CREATE zch_i_saleshed.
ENDCLASS.
CLASS lhc_zch_i_saleshed IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
METHOD earlynumbering_create.
select max( sid ) from zch_dt_sales_hed
into (lv_max_id).
data(lv_num) = CONV numc08( lv_max_id ).
if lv_num is initial.
lv_max_id = ‘SO00000001’.
else.
lv_num += 1.
lv_max_id = |SO{ lv_num }|.
ENDIF.
data(ls_entity) = entities[ 1 ].
APPEND VALUE #( %cid = ls_entity-%cid
sid = lv_max_id ) to mapped-zch_i_saleshed.
ENDMETHOD.
METHOD earlynumbering_cba_Sitem.
data lv_number(6) type n.
select max( sdoc_item ) from zch_dt_sales_it
into (lv_max_id).
if sy-subrc = 0.
lv_number = lv_max_id + 1.
else.
lv_number = 1.
ENDIF.
loop at entities into data(ls_entities).
loop at ls_entities-%target ASSIGNING FIELD-SYMBOL(<fs_target>).
APPEND CORRESPONDING #( <fS_target> ) to mapped-zch_i_salesit ASSIGNING FIELD-SYMBOL(<ls_item>).
<ls_item>-SdocItem = lv_number.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
METHOD download.
data: lt_salesi type ZCH_T_SALESI.
loop at keys into data(ls_keys).
READ ENTITIES OF zch_i_saleshed IN LOCAL MODE
ENTITY zch_i_saleshed
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_sales_header).
data(ls_salesh) = CORRESPONDING zch_i_saleshed( lt_sales_header[ 1 ] ).
READ ENTITIES OF zch_i_saleshed IN LOCAL MODE
ENTITY zch_i_saleshed by _sitem
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_sales_item).
LOOP AT lt_sales_item INTO DATA(ls_item).
APPEND CORRESPONDING zch_i_salesit( ls_item ) TO lt_salesi.
ENDLOOP.
DATA(lv_pdf_xstring) = NEW ZCL_CH_HELPER_CLS( )->generate_form(
is_sales_header = ls_salesh
it_sales_items = lt_salesi ).
APPEND VALUE #(
%tky = ls_keys-%tky
%param-attachment = lv_pdf_xstring
%param-mimetype = ‘application/pdf’
%param-filename = |Invoice_{ ls_salesh-sid }.pdf|
) TO result.
ENDLOOP.
MODIFY ENTITY IN LOCAL MODE zch_i_saleshed
UPDATE FIELDS ( Attachment Mimetype Filename )
WITH value #( ( %key = ls_keys-%key
Attachment = lv_pdf_xstring
Filename = |Invoice_{ ls_salesh-sid }.pdf|
Mimetype = ‘application/pdf’ ) )
REPORTED reported
FAILED failed.
ENDMETHOD.
ENDCLASS.
The following helper class generates the Smart form output as an XSTRING .
CLASS zcl_ch_helper_cls DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
TYPES: ty_sales_items_tt TYPE STANDARD TABLE OF zch_i_salesit.
METHODS generate_form
IMPORTING
is_sales_header TYPE zch_i_saleshed
it_sales_items TYPE ty_sales_items_tt
RETURNING
VALUE(rv_pdf) TYPE xstring.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_ch_helper_cls IMPLEMENTATION.
METHOD generate_form.
data : lv_fm_name type RS38L_FNAM,
lv_ctr_par type ssfctrlop,
lv_output_options type SSFCOMPOP,
lv_job_output type SSFCRESCL,
Gt_pdf_lines type table of tline,
lv_size type i,
lv_pdf_xstring type xstring.
CALL FUNCTION ‘SSF_FUNCTION_MODULE_NAME’
EXPORTING
formname = ‘ZCH_INVOICE’
* VARIANT = ‘ ‘
* DIRECT_CALL = ‘ ‘
IMPORTING
FM_NAME = lv_fm_name
EXCEPTIONS
NO_FORM = 1
NO_FUNCTION_MODULE = 2
OTHERS = 3
.
lv_ctr_par-no_dialog = ‘X’.
lv_ctr_par-preview = ‘X’.
lv_ctr_par-getotf = ‘X’.
lv_output_options-TDDEST = ‘LP01’.
CALL FUNCTION lv_fm_name
EXPORTING
CONTROL_PARAMETERS = lv_ctr_par
OUTPUT_OPTIONS = lv_output_options
USER_SETTINGS = ”
im_sales = is_sales_header-Sid
IMPORTING
JOB_OUTPUT_INFO = lv_job_output
EXCEPTIONS
FORMATTING_ERROR = 1
INTERNAL_ERROR = 2
SEND_ERROR = 3
USER_CANCELED = 4
OTHERS = 5
.
CALL FUNCTION ‘CONVERT_OTF’
EXPORTING
FORMAT = ‘PDF’
IMPORTING
bin_filesize = lv_size
bin_file = lv_pdf_xstring
TABLES
otf = lv_job_output-otfdata
lines = Gt_pdf_lines
EXCEPTIONS
ERR_MAX_LINEWIDTH = 1
ERR_FORMAT = 2
ERR_CONV_NOT_POSSIBLE = 3
ERR_BAD_OTF = 4
OTHERS = 5.
rv_pdf = lv_pdf_xstring.
ENDMETHOD.
ENDCLASS.
Define Service definition
@EndUserText.label: ‘Service definition for sales header’
define service Zch_srv_saleshed {
expose zch_c_saleshed;
expose zch_c_salesit;
}
Define Service binding and preview the application
Create Sales Header data
Create item data
Click on the Download invoice button and refresh
Now Click on Open file ,the form will be downloaded
Conclusion :
This approach enables seamless Smart Form PDF generation and download within RAP, combining classic ABAP capabilities with modern Fiori user experience in an on-premise setup.
Introduction: In this blog I need to demonstrate the process of enabling a smart form PDF download within a RAP application using a custom action button in an on-premises setup. This is a common requirement for generating and downloading documents like invoices, Purchase orders etc., from a Fiori elements app. In classic ABAP, generating and downloading files was often done using methods like GUI_DOWNLOAD, but this approach is not compatible with RAP. In RAP, the recommended way to implement download functionality is by using streams, combined with additional logic to handle the pdf generation and delivery to the front-end. Step 1 : Create Database Tables First we will be creating a database table to store the sales header(ZCH_DT_SALES_HED) and sales item (ZCH_DT_SALES_IT)data. @EndUserText.label : ‘sales header table’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zch_dt_sales_hed
{
key sid : abap.char(10) not null;
created_on : abap.dats;
document_type : abap.char(4);
customer : abap.char(10);
attachment : abap.rawstring(0);
mimetype : abap.char(128);
filename : abap.char(128);
last_changed_at : abp_lastchange_tstmpl;
}Item table @EndUserText.label : ‘sales item table’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zch_dt_sales_it
{
key sid : abap.char(10) not null;
key sdoc_item : abap.numc(6) not null;
material_no : abap.char(40);
@Semantics.quantity.unitOfMeasure : ‘zch_dt_sales_it.unit’
quantity : abap.quan(15,3);
unit : abap.unit(3);
@Semantics.amount.currencyCode : ‘zch_dt_sales_it.currency’
net_value : abap.curr(15,2);
currency : abap.cuky;
}Step 2 : Define Interface view on top of Sales Header and item tables @AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘base view for sales header’
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define root view entity zch_i_saleshed
as select from zch_dt_sales_hed
composition [0..*] of zch_i_salesit as _sitem
{
key sid as Sid,
created_on as CreatedOn,
document_type as DocumentType,
customer as Customer,
@Semantics.largeObject : { fileName: ‘filename’ , mimeType: ‘Mimetype’ ,
contentDispositionPreference: #ATTACHMENT}
attachment as Attachment,
@Semantics.mimeType: true
mimetype as Mimetype,
filename as Filename,
last_changed_at as LastChangedAt,
_sitem
}@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘base view for sales item’
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity zch_i_salesit as select from zch_dt_sales_it
association to parent zch_i_saleshed as _sheader on $projection.Sid = _sheader.Sid
{
key sid as Sid,
key sdoc_item as SdocItem,
material_no as MaterialNo,
@Semantics.quantity.unitOfMeasure : ‘Unit’
quantity as Quantity,
unit as Unit,
@Semantics.amount.currencyCode : ‘Currency’
net_value as NetValue,
currency as Currency,
_sheader
}Step 3 : Define Consumption view on top of header and item interface views @AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘projection view for sales header’
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define root view entity zch_c_saleshed
provider contract transactional_query
as projection on zch_i_saleshed
{
key Sid,
CreatedOn,
DocumentType,
Customer,
@Semantics.largeObject : { fileName: ‘filename’ , mimeType: ‘Mimetype’ ,
contentDispositionPreference: #ATTACHMENT}
Attachment,
@Semantics.mimeType: true
Mimetype,
Filename,
LastChangedAt,
/* Associations */
_sitem : redirected to composition child zch_c_salesit
}@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘projection view for sales item’
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define view entity zch_c_salesit
as projection on zch_i_salesit
{
key Sid,
key SdocItem,
MaterialNo,
@Semantics.quantity.unitOfMeasure : ‘unit’
Quantity,
Unit,
@Semantics.amount.currencyCode: ‘Currency’
NetValue,
Currency,
/* Associations */
_sheader : redirected to parent zch_c_saleshed
}Step 4 : Define Metadata Extension for Header and item data @Metadata.layer:#CORE
annotate entity zch_c_saleshed
with
{
.facet: [
{
id: ‘SalesDocHeader’,
purpose: #STANDARD,
position: 10,
label: ‘Header’,
type: #IDENTIFICATION_REFERENCE
},
{
id: ‘SalesDocItem’,
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: ‘Items’,
position: 20,
targetElement: ‘_sitem’
}
]
: { lineItem: [{position: 10 , label: ‘Salesorder Id’ }],
identification: [{ position: 10 ,label :’Salesorder Id’ },
{ type: #FOR_ACTION, position:10 ,dataAction: ‘download’, label: ‘Download Invoice’ }] }
Sid;
: { lineItem: [{position: 20 , label: ‘Created on’ }],
identification: [{ position: 20 ,label :’Created on’ }] }
CreatedOn;
: { lineItem: [{position: 30 , label: ‘Document type’ }],
identification: [{ position: 30 ,label :’Document type’ }] }
DocumentType;
: { lineItem: [{position: 40 , label: ‘Customer id’ }],
identification: [{ position: 40 ,label :’Customer id’ }] }
Customer;
.identification: [
{ position: 60, label: ‘Attachment’ }
]
Attachment;
}@Metadata.layer: #CORE
annotate entity zch_c_salesit
with
{
.facet: [
{
id: ‘SalesDocItem’,
purpose: #STANDARD,
position: 10,
label: ‘Header’,
type: #IDENTIFICATION_REFERENCE
}]
@UI : { lineItem: [{position: 10 , label: ‘Salesorder Id’ }],
identification: [{ position: 10 , label :’Salesorder Id’ }] }
Sid;
: { lineItem: [{position: 20 , label: ‘Sales Document Item’ }],
identification: [{ position: 20 ,label :’Sales Document Item’ }] }
SdocItem;
: { lineItem: [{position: 30 , label: ‘Material no’ }],
identification: [{ position: 30 ,label :’Material no’ }] }
MaterialNo;
: { lineItem: [{position: 40 , label: ‘Quantity’ }],
identification: [{ position: 40 ,label :’Quantity’ }] }
Quantity;
: { lineItem: [{position: 50 , label: ‘Unit’ }],
identification: [{ position: 50 ,label :’Unit’ }] }
Unit;
: { lineItem: [{position: 60 , label: ‘Net value’ }],
identification: [{ position: 60 ,label :’Net value’ }] }
NetValue;
: { lineItem: [{position: 70 , label: ‘Currency’ }],
identification: [{ position: 70 ,label :’Currency’ }] }
Currency;
}Step 5: Define Behavior Definitions We need to define the behavior definition for sales order header and item. This includes setting up the managed implementation, defining standard CRUD operations, defining early numbering for header and item keys and defining the Download action.*Here Download action button returns the abstract entity instead of sales order itself. This abstract entity is used only for transferring the file back to the UI. managed implementation in class zbp_ch_i_saleshed unique;
strict ( 2 );
define behavior for zch_i_saleshed //alias salesheader
persistent table zch_dt_sales_hed
lock master
authorization master ( instance )
//etag master <field_name>
early numbering
{
create;
update;
delete;
action download result [1] zch_download_file;
side effects { action download affects $self ;}
association _sitem { create; }
field ( readonly ) Sid,Attachment, Mimetype, Filename;
mapping for zch_dt_sales_hed
{
Sid = sid;
CreatedOn = created_on;
DocumentType = document_type;
Customer = customer;
Filename = filename;
Mimetype = mimetype;
Attachment = attachment;
LastChangedAt = last_changed_at;
}
}
define behavior for zch_i_salesit //alias salesitem
persistent table zch_dt_sales_it
lock dependent by _sheader
authorization dependent by _sheader
//etag master <field_name>
early numbering
{
update;
delete;
field ( readonly ) sid,SdocItem ;
association _sheader;
mapping for zch_dt_sales_it
{
Sid = sid;
SdocItem = sdoc_item;
MaterialNo = material_no;
Quantity = quantity;
Unit = unit;
NetValue = net_value;
Currency = currency;
}
}Abstract Entity :@EndUserText.label: ‘abstract entity for file download’
define root abstract entity Zch_Download_File
{
@Semantics.largeObject : { fileName: ‘filename’ , mimeType: ‘Mimetype’ ,
contentDispositionPreference: #ATTACHMENT }
attachment : abap.rawstring(0);
@Semantics.mimeType: true
mimetype : abap.char(128);
filename : abap.char(128);
}Define behavior definition on top of projection view projection;
strict ( 2 );
define behavior for zch_c_saleshed //alias <alias_name>
{
use create;
use update;
use delete;
use action download ;
use association _sitem { create; }
}
define behavior for zch_c_salesit //alias <alias_name>
{
use update;
use delete;
use association _sheader;
}Step 6: Implement the Behavior handler class In this step we will be implementing the behavior handler for our sales order RAP BO.This class takes care of early numbering as well as the download action which generates and streams the invoice pdf back to UI.CLASS lhc_zch_i_saleshed DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR zch_i_saleshed RESULT result.
METHODS download FOR MODIFY
IMPORTING keys FOR ACTION zch_i_saleshed~download RESULT result.
METHODS earlynumbering_cba_sitem FOR NUMBERING
IMPORTING entities FOR CREATE zch_i_saleshed_sitem.
METHODS earlynumbering_create FOR NUMBERING
IMPORTING entities FOR CREATE zch_i_saleshed.
ENDCLASS.
CLASS lhc_zch_i_saleshed IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
METHOD earlynumbering_create.
select max( sid ) from zch_dt_sales_hed
into (lv_max_id).
data(lv_num) = CONV numc08( lv_max_id ).
if lv_num is initial.
lv_max_id = ‘SO00000001’.
else.
lv_num += 1.
lv_max_id = |SO{ lv_num }|.
ENDIF.
data(ls_entity) = entities[ 1 ].
APPEND VALUE #( %cid = ls_entity-%cid
sid = lv_max_id ) to mapped-zch_i_saleshed.
ENDMETHOD.
METHOD earlynumbering_cba_Sitem.
data lv_number(6) type n.
select max( sdoc_item ) from zch_dt_sales_it
into (lv_max_id).
if sy-subrc = 0.
lv_number = lv_max_id + 1.
else.
lv_number = 1.
ENDIF.
loop at entities into data(ls_entities).
loop at ls_entities-%target ASSIGNING FIELD-SYMBOL(<fs_target>).
APPEND CORRESPONDING #( <fS_target> ) to mapped-zch_i_salesit ASSIGNING FIELD-SYMBOL(<ls_item>).
<ls_item>-SdocItem = lv_number.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
METHOD download.
data: lt_salesi type ZCH_T_SALESI.
loop at keys into data(ls_keys).
READ ENTITIES OF zch_i_saleshed IN LOCAL MODE
ENTITY zch_i_saleshed
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_sales_header).
data(ls_salesh) = CORRESPONDING zch_i_saleshed( lt_sales_header[ 1 ] ).
READ ENTITIES OF zch_i_saleshed IN LOCAL MODE
ENTITY zch_i_saleshed by _sitem
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_sales_item).
LOOP AT lt_sales_item INTO DATA(ls_item).
APPEND CORRESPONDING zch_i_salesit( ls_item ) TO lt_salesi.
ENDLOOP.
DATA(lv_pdf_xstring) = NEW ZCL_CH_HELPER_CLS( )->generate_form(
is_sales_header = ls_salesh
it_sales_items = lt_salesi ).
APPEND VALUE #(
%tky = ls_keys-%tky
%param-attachment = lv_pdf_xstring
%param-mimetype = ‘application/pdf’
%param-filename = |Invoice_{ ls_salesh-sid }.pdf|
) TO result.
ENDLOOP.
MODIFY ENTITY IN LOCAL MODE zch_i_saleshed
UPDATE FIELDS ( Attachment Mimetype Filename )
WITH value #( ( %key = ls_keys-%key
Attachment = lv_pdf_xstring
Filename = |Invoice_{ ls_salesh-sid }.pdf|
Mimetype = ‘application/pdf’ ) )
REPORTED reported
FAILED failed.
ENDMETHOD.
ENDCLASS.The following helper class generates the Smart form output as an XSTRING . CLASS zcl_ch_helper_cls DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
TYPES: ty_sales_items_tt TYPE STANDARD TABLE OF zch_i_salesit.
METHODS generate_form
IMPORTING
is_sales_header TYPE zch_i_saleshed
it_sales_items TYPE ty_sales_items_tt
RETURNING
VALUE(rv_pdf) TYPE xstring.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_ch_helper_cls IMPLEMENTATION.
METHOD generate_form.
data : lv_fm_name type RS38L_FNAM,
lv_ctr_par type ssfctrlop,
lv_output_options type SSFCOMPOP,
lv_job_output type SSFCRESCL,
Gt_pdf_lines type table of tline,
lv_size type i,
lv_pdf_xstring type xstring.
CALL FUNCTION ‘SSF_FUNCTION_MODULE_NAME’
EXPORTING
formname = ‘ZCH_INVOICE’
* VARIANT = ‘ ‘
* DIRECT_CALL = ‘ ‘
IMPORTING
FM_NAME = lv_fm_name
EXCEPTIONS
NO_FORM = 1
NO_FUNCTION_MODULE = 2
OTHERS = 3
.
lv_ctr_par-no_dialog = ‘X’.
lv_ctr_par-preview = ‘X’.
lv_ctr_par-getotf = ‘X’.
lv_output_options-TDDEST = ‘LP01’.
CALL FUNCTION lv_fm_name
EXPORTING
CONTROL_PARAMETERS = lv_ctr_par
OUTPUT_OPTIONS = lv_output_options
USER_SETTINGS = ”
im_sales = is_sales_header-Sid
IMPORTING
JOB_OUTPUT_INFO = lv_job_output
EXCEPTIONS
FORMATTING_ERROR = 1
INTERNAL_ERROR = 2
SEND_ERROR = 3
USER_CANCELED = 4
OTHERS = 5
.
CALL FUNCTION ‘CONVERT_OTF’
EXPORTING
FORMAT = ‘PDF’
IMPORTING
bin_filesize = lv_size
bin_file = lv_pdf_xstring
TABLES
otf = lv_job_output-otfdata
lines = Gt_pdf_lines
EXCEPTIONS
ERR_MAX_LINEWIDTH = 1
ERR_FORMAT = 2
ERR_CONV_NOT_POSSIBLE = 3
ERR_BAD_OTF = 4
OTHERS = 5.
rv_pdf = lv_pdf_xstring.
ENDMETHOD.
ENDCLASS.Define Service definition @EndUserText.label: ‘Service definition for sales header’
define service Zch_srv_saleshed {
expose zch_c_saleshed;
expose zch_c_salesit;
} Define Service binding and preview the application Create Sales Header data Create item data Click on the Download invoice button and refresh Now Click on Open file ,the form will be downloaded Conclusion : This approach enables seamless Smart Form PDF generation and download within RAP, combining classic ABAP capabilities with modern Fiori user experience in an on-premise setup. Read More Technology Blog Posts by Members articles
#SAP
#SAPTechnologyblog