Summary : In my previous blog I have covered, how to process/save pdf documents (can have multiple) which are received in e-mail into SAP. In this blog, we will see how we can enable auto print functionality for PDF documents on dedicated physical printer or on local printer (LP01) in spool.
Additional Business requirement : All the PDF documents (if there are multiple attachments in e-mail) should get generated as one PDF in spool and in case of two-sided printing, one documents shouldn’t get printed on back side of another one.
As per previous article, we have already implemented the two methods of Interface IF_INBOUND_EXIT_BCS in inbound Exit(Z-class : ZCL_BILLING_EMAIL_IN).
Method : IF_INBOUND_EXIT_BCS~CREATE_INSTANCE (Same as previous article)
*Create instance of the object with the class itself
DATA(lo_ref_instance) = NEW zcl_billing_email_in( ).
IF lo_ref_instance IS BOUND.
ro_ref = lo_ref_instance.
ENDIF.
Method : IF_INBOUND_EXIT_BCS~PROCESS_INBOUND
We need to write code in 2nd method of the interface to implement the printing functionality as per below code snippet with following steps.
- Get the PDF documents (hex format) from the e-mail object
- Convert hex format into XSTRING format
- Get total no. of pages in each PDF document and identify if total no of pages are odd : Method – GET_TOTAL_PAGES.
- Add additional blank page in PDF document if total no of pages are odd to avoid any issue in case of two sided printing. (Additional requirement) : Method – GET_BLANK_PDF_PAGE
- Merge all the XSTRING data
- Print PDF document on printer : Method – PRINT_PDF_DOC
CONSTANTS: lc_pdf TYPE char3 VALUE 'PDF'. "Document Type PDF
DATA: lv_printer TYPE rspopshort, "Spool: short name for output device
lv_blank_page TYPE xstring,
lv_total_pages TYPE i.
CLEAR: e_retcode, es_t100msg.
lv_printer = 'LP01'. "Or any physical printer in SAP but with short name
*Extract Attachment from e-mail
TRY .
IF io_sreq IS BOUND.
* Get document
DATA(lo_ref_document) = io_sreq->get_document( ).
IF lo_ref_document IS BOUND.
* Find all the PDF attachments and process it.
TRY.
* Create PDF Merge Object to be printed in spool as one document
TRY.
DATA(lo_ref_pdf_merger) = NEW cl_rspo_pdf_merge( ).
CATCH cx_rspo_pdf_merge INTO DATA(lv_merge_ex).
ENDTRY.
DO lo_ref_document->get_body_part_count( ) TIMES.
TRY.
DATA(lv_doc_type) = lo_ref_document->get_body_part_attributes( im_part = sy-index )-doc_type.
TRANSLATE lv_doc_type TO UPPER CASE.
* If Document type is PDF then only proceed as program needs to look into PDF attachments only
IF lv_doc_type = lc_pdf.
* Get the file name of attached document
DATA(lv_filename) = lo_ref_document->get_body_part_attributes( im_part = sy-index )-filename.
* Get the content of attached file in hex format.
DATA(ls_body_part_content) = lo_ref_document->get_body_part_content( sy-index ).
IF lv_filename IS NOT INITIAL AND ls_body_part_content IS NOT INITIAL.
TRY.
*Convert Hex data into Xstring
CALL METHOD cl_bcs_convert=>xtab_to_xstring
EXPORTING
it_xtab = iv_body_part_content-cont_hex
RECEIVING
rv_xstring = DATA(lv_xstring).
CATCH cx_bcs .
continue.
ENDTRY.
IF lv_xstring IS NOT INITIAL.
IF lo_ref_pdf_merger IS BOUND.
lo_ref_pdf_merger->add_document( lv_xstring ).
ENDIF.
* Get total no of pages in PDF document
CALL METHOD me->get_total_pages
EXPORTING
iv_content = lv_xstring
IMPORTING
ev_pages = DATA(lv_pages)
ev_odd_pages = DATA(lv_odd_flag).
lv_total_pages = lv_total_pages + lv_pages.
IF lv_odd_flag EQ abap_true.
* If there is odd number of pages, then add one blank PDF page at the end to avoid back to back printing
IF lv_blank_page IS INITIAL.
CALL METHOD me->get_blank_pdf_page
IMPORTING
ev_blank_content = lv_blank_page.
ENDIF.
IF lv_blank_page IS NOT INITIAL.
* Merge all PDF documents in one PDF
lv_total_pages = lv_total_pages + 1.
lo_ref_pdf_merger->add_document( lv_blank_page ).
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
CATCH cx_document_bcs.
CONTINUE.
ENDTRY.
CLEAR: lv_xstring,ls_body_part_content,
lv_doc_type, lv_filename.
ENDDO.
* Merge all the PDF documents (in XSTRING format)
IF lo_ref_pdf_merger IS BOUND.
lo_ref_pdf_merger->merge_documents(
IMPORTING
merged_document = DATA(lv_pdf_all)
rc = DATA(lv_rc) ).
IF lv_rc NE 0.
lo_ref_pdf_merger->get_err_doc_index(
IMPORTING
index = DATA(lv_docindex) ).
MESSAGE e895(po) WITH lv_docindex INTO DATA(lv_errortext).
ENDIF.
ENDIF.
* Print the PDF document on dedicated printer
CALL METHOD me->print_pdf_doc
EXPORTING
iv_content = lv_pdf_all
iv_dest = lv_printer
iv_pages = lv_total_pages.
ENDIF.
CATCH cx_abap_error_analyze.
e_retcode = if_inbound_exit_bcs=>gc_continue.
ENDTRY.
ENDIF.
ENDIF.
CATCH cx_os_object_not_found.
e_retcode = if_inbound_exit_bcs=>gc_continue.
ENDTRY.
For point 3: Method- GET_TOTAL_PAGES, code will check if total no of pages are odd or not (by using MOD function on integer ev_pages)
DATA: lt_result TYPE match_result_tab.
* Convert XSTRING to STRING format
CLEAR: ev_odd_pages, ev_pages.
cl_bcs_convert=>xstring_to_string(
EXPORTING
iv_xstr = iv_content
iv_cp = 1100 " SAP character set identification
RECEIVING
rv_string = DATA(lv_cont) ).
* Get total no of pages in PDF document
FIND REGEX `/N (.{1,5})/` IN lv_cont IGNORING CASE RESULTS lt_result.
IF sy-subrc NE 0.
FIND ALL OCCURRENCES OF REGEX `/count (.{1,4})/` IN lv_cont IGNORING CASE RESULTS lt_result.
ENDIF.
DATA(lv_lines) = lines( lt_result ).
IF lv_lines IS NOT INITIAL.
READ TABLE lt_result ASSIGNING FIELD-SYMBOL(<fs_result>) INDEX lv_lines.
IF sy-subrc EQ 0.
READ TABLE <fs_result>-submatches ASSIGNING FIELD-SYMBOL(<fs_subm>) INDEX 1.
IF sy-subrc EQ 0.
DATA(lv_temp) = lv_cont+<fs_subm>-offset(<fs_subm>-length).
CONDENSE lv_temp NO-GAPS.
DATA(lv_pages) = CONV numc5( |{ lv_temp ALPHA = OUT }| ).
ev_pages = CONV i( lv_pages ).
IF ev_pages MOD 2 NE 0.
ev_odd_pages = abap_true.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
For point 4: Method – GET_BLANK_PDF_PAGE
If we have total no of pages odd, then add additional blank PDF page. We can store XSTRING data for blank PDF page in SO10 standard text and read it from there with function module READ_TEXT.
DATA: lt_lines TYPE STANDARD TABLE OF tline,
lv_data TYPE string.
CLEAR: ev_blank_content,
lt_lines.
CALL FUNCTION 'READ_TEXT'
EXPORTING
client = sy-mandt
id = 'ST'
language = 'E'
name = 'ZBLANK_PDF_XSTRING'
object = 'TEXT'
TABLES
lines = lt_lines
EXCEPTIONS
id = 1
language = 2
name = 3
not_found = 4
object = 5
reference_check = 6
wrong_access_to_archive = 7
OTHERS = 8.
IF sy-subrc EQ 0.
LOOP AT lt_lines ASSIGNING FIELD-SYMBOL(<fs_lines>).
CONCATENATE lv_data <fs_lines>-tdline INTO lv_data.
ENDLOOP.
IF lv_data IS NOT INITIAL.
ev_blank_content = CONV xstring( lv_data ).
ENDIF.
ENDIF.
To print the PDF document, we need to open ADS spool request and write in there as per below code logic.
Method – PRINT_PDF_DOC
DATA: lv_handle TYPE sy-tabix,
lv_partname TYPE adspart,
lv_globaldir TYPE text1024,
lv_dstfile TYPE text1024,
lv_filesize TYPE i.
* Open an ADS spool request for writing the attachment file for printing
CALL FUNCTION 'ADS_SR_OPEN'
EXPORTING
dest = iv_dest
doctype = 'ADSP'
copies = 1
immediate_print = abap_true
IMPORTING
handle = lv_handle
partname = lv_partname
EXCEPTIONS
device_missing = 1
no_such_device = 2
operation_failed = 3
wrong_doctype = 4
wrong_devicetype = 5
OTHERS = 6.
* If there is any error then set the variable lv_print_err_flag as X
IF sy-subrc NE 0.
DATA(lv_print_err_flag) = abap_true.
ELSE.
* Provides storage location for ADS spool requests
CALL FUNCTION 'ADS_GET_PATH'
IMPORTING
ads_path = lv_globaldir.
* Prepare the filepath to write the content
CONCATENATE lv_globaldir '/' lv_partname '.pdf' INTO lv_dstfile.
* Open the filepath and transfer the PDF content in Spool
TRY.
OPEN DATASET lv_dstfile FOR OUTPUT IN BINARY MODE.
IF sy-subrc NE 0.
lv_print_err_flag = abap_true.
ELSE.
TRANSFER iv_content TO lv_dstfile.
IF sy-subrc NE 0.
lv_print_err_flag = abap_true.
ELSE.
* Close the file when writing is completed
CLOSE DATASET lv_dstfile.
IF sy-subrc NE 0.
lv_print_err_flag = abap_true.
ELSE.
lv_filesize = xstrlen( iv_content ).
*Writing all lines in an open ADS spool request
CALL FUNCTION 'ADS_SR_CONFIRM'
EXPORTING
handle = lv_handle
partname = lv_partname
size = lv_filesize
pages = iv_pages
EXCEPTIONS
handle_not_valid = 1
operation_failed = 2
OTHERS = 3.
IF sy-subrc NE 0.
lv_print_err_flag = abap_true.
ELSE.
*Closing an ADS spool request
CALL FUNCTION 'ADS_SR_CLOSE'
EXPORTING
handle = lv_handle
EXCEPTIONS
handle_not_valid = 1
operation_failed = 2
OTHERS = 3.
IF sy-subrc NE 0.
lv_print_err_flag = abap_true.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
CATCH cx_sy_file_open
cx_sy_codepage_converter_init
cx_sy_conversion_codepage
cx_sy_file_authority
cx_sy_pipes_not_supported
cx_sy_too_many_files
cx_sy_file_close
cx_sy_file_io
cx_sy_file_open_mode
cx_sy_pipe_reopen.
lv_print_err_flag = abap_true.
ENDTRY.
ENDIF.
* If there is any error in printing then raise exception
IF lv_print_err_flag = abap_true.
RAISE EXCEPTION TYPE cx_abap_error_analyze
EXPORTING
message = CONV string( TEXT-001 )
kind = 'E'.
ENDIF.
Let’s execute the functionality after triggering the e-mail which has three separate PDF documents.
Now execute T-code SP01 with selection screen value Created By : MAILADM (or similar system ID which process the incoming email in SAP).
Note : Here we have addressed additional business functionality ( one PDF document in spool and adding blank PDF page, if no of pages in document is odd). These are not required to complete the auto-print functionality and code can be suppressed accordingly.
Hope you have enjoyed this article. Please keep reading along and feel free to provide any suggestions or comments. Thank you again for going through this document. Appreciate your valuable time!