Enterprise Resource Planning Blogs by Members
Gain new perspectives and knowledge about enterprise resource planning in blog posts from community members. Share your own comments and ERP insights today!
cancel
Showing results for 
Search instead for 
Did you mean: 
MarekTurczynski
Contributor

Introduction


 

FINT is a handy transaction for calculating of interest on customer items ( for vendor items FINTAP should be used). It is the successor of the old transactions F.2A/ F.2B/ F.2C and F.24 which were using program RFDUZI00 for calculation.

FINT works, like most of periodically scheduled programs in SAP, using a 2-step approach: first a proposal is created and then the production run can be executed. It is also possible to directly start a production run without seeing the proposal run but this approach is rather not used.

Whereas the transactions based on RFDUZI00 program supported Sap Script only, FINT supports Smartforms and Adobe PDF and has a BADI where all actions performed by the calculation program can be influenced.

There are already well-documented examples how to steer with email sending of different correspondence in different SAP forums on the Internet and OSS notes:

  • Payment Advice: OSS 1033893

  • Dunning: 1042992

  • Balance Confirmation: 1377820

  • Other correspondence: 1360070


In terms of FINT SAP only delivered note 956981, which by standard supports only email sending for forms created using Adobe PDF but not Smartforms

This gap in documentation made me write the blog to show you how you can archive the interest email sending and what to consider during the programming.

 

FINT Enhancement Possibilities


FINT, in opposite to classic transactions based on RFDUZI00, has a BADI where many of the interest parameters, including the whole output and calculation, can be adjusted to customer needs. The BADI name is FI_INT_CUS01 – in fact in the coding 2 BADIs can be found, the other one is FI_INT_SAP01, which rather should be used by SAP only. It only seems to be a mistake that SAP didn’t mark it as ‘To-be-used by SAP only’.

Anyway the processing of each method available in these BADIs is always called in sequence – first FI_INT_SAP01 and immediately below it FI_INT_CUS01.

Besides that the selection screen as well as ALV output and Functions can be enhanced via program RFINTITUSEREXT.

FINT Email sending: Logic


The code presented in further sections of this blog is based on assumption that interest notes are to be send out to a customer who has a main E-mail address provided. If the email is marked as ‘standard’ then such is set as email recipient, otherwise it is set as secondary recipient (carbon copy).

FINT Email Sending: Enhance Screen


At first, to make email sending controllable, the selection screen will be enhanced by a new field using the screen enhancement program.

Program RFINTITUSEREXT provides function to:

  • Add / delete fields in calculation and display structure

  • Add / delete custom parameters

  • Add / delete custom select-options


The parameters and select-options have a predefined naming convention – they must begin with ‘A_’.  Using data elements created in a customer namespace, like /SAP/ should not be be used for any kind of abovementioned screen enhancement – it happened to me several times that system adds it (at least it says so) but it cannot be named and stays only as an untranslated parameter. Therefore it is advisable to use only Z/Y for data elements used as base of new fields (besides of course the standard standard ones that can also be used).

A newly created data element can be added using following option:



By adding a new parameter system will modify the include RFINTITARUSEREXT and FI_INT_USEREXT so you will be prompted to provide an object key for modification.

Note that the description of the data element is not taken automatically over in the selection screen, so you need to adjust it in SE38 of program RFINTITAR by choosing Text Elements -> Selection Text.


FINT Email Sending: Methods used


This implementation is called only when productive run of interest calculation is done.

In implementation of BADI FI_INT_CUS01 email can be archived by using 2 methods:

  1. INT_PRINT_OPTIONS - determination of email recipients and conversion to OTF

  2. INT_PRINT_SPOOL_CLOSED – actual sending of emails


In this example the content and subject of email are hardcoded – this is only for presentation purpose – in your system do not follow this approach!

FINT Email Sending: Class Attributes


 

Method INT_PRINT_OPTIONS contains all data about the interest calculated, business partners and output options. It is called when sample or final printout is selected in FINT.

In this method parameters for email recipients etc. already exists but filling them does not have any effect when using Smartforms-based output. This means that the recipients need to be saved in  a class attribute. We will use attribute ‘mt_email_recipients’ for that.

In addition the processing sequence of each output is only available in this method so it is advisable to save it in an attribute as well. In this example it will be saved in attribute ‘mt_ipf_proc_seq’.

To determine proper language of email text the output language will be stored in attribute ‘mt_output_language’. It won't actually be used in the class for determining the language of email and is included here only for demonstration purposes.

Additionally, as newly-added selection parameter ‘A_EMAIL’ is not available in INT_PRINT_SPOOL_CLOSED it will be saved as ‘mv_email’ attribute.

In the method INT_PRINT_SPOOL_CLOSED no information about customer etc. is available. It is only called for each global output table GT_IPF_DISP of the report (stored in function group SAPLFI_INT) transferring the output content itself.

At the end following attributes and types are defined in the implementation class:


public section.

interfaces IF_EX_FI_INT_CUS01 .

constants GC_ACCOUNT_TYPE_CUSTOMER type KOART value 'D' ##NO_TEXT.
constants GC_INTEREST_STATUS_NOINT type INT_STATUS value 'NOINT' ##NO_TEXT.
constants GC_INTEREST_STATUS_PROCESS type INT_STATUS value 'PROCESS' ##NO_TEXT.
constants GC_INTEREST_STATUS_NEW type INT_STATUS value 'NEW' ##NO_TEXT.

private section.

types:
"Types
" Key used to identity an interest package
BEGIN OF tp_fint_key,
bukrs TYPE bukrs,
koart TYPE koart,
konko TYPE konko,
END OF tp_fint_key .
types:
"Language of printout info
BEGIN OF tp_language_info.
INCLUDE TYPE tp_fint_key.
TYPES:
spras TYPE spras,
name1 TYPE name1,
END OF tp_language_info .
types:
tt_language_info TYPE STANDARD TABLE OF tp_language_info WITH NON-UNIQUE KEY bukrs koart konko .
types:
"Email recipient list
BEGIN OF tp_email_recipients.
INCLUDE TYPE tp_fint_key.
TYPES:
receiver TYPE ad_smtpadr,
copy TYPE so_snd_cp,
blind_copy TYPE so_snd_bc,
END OF tp_email_recipients .
types:
tt_email_recipients TYPE STANDARD TABLE OF tp_email_recipients WITH NON-UNIQUE KEY bukrs koart konko .
types:
"Processing sequence of output
tt_ipf_sequence TYPE STANDARD TABLE OF intipf WITH DEFAULT KEY .

"Contants for checks/ output info
constants C_FINTSHOW type SY-TCODE value 'FINTSHOW' ##NO_TEXT.
constants C_ATTACHMENT_TYPE_PDF type SO_OBJ_TP value 'PDF' ##NO_TEXT.
"Data for email processing
data MT_OUTPUT_LANGUAGE type TT_LANGUAGE_INFO .
data MT_IPF_PROC_SEQ type TT_IPF_SEQUENCE .
data MT_EMAIL_RECIPIENTS type TT_EMAIL_RECIPIENTS .
data MV_EMAIL type Z_FINT_EMAIL_SEND .
constants C_EMAIL type FIELDNAME value 'A_EMAIL' ##NO_TEXT.

 

FINT Email Sending: INT_PRINT_OPTIONS


This method is called in function module SF_PRINT_INTIT_NOTICE just before the SMARTFORM is called. The call happens in loop over T_IPF table storing each output line to be processed. T_IPF in the loop represents the same content as global table GT_IPF  of RFINTITAR.

Table GT_IPF contains all customers for which interest is to be created. Entries stored in the table show in which order the output is processed. Method INT_PRINT_OPTIONS is triggered for every single entry of GT_IPF.

As soon as the spool is closed these entries are not available any more in the method INT_PRINT_SPOOL_CLOSED that is why we’re storing it in class attribute to use it later on and send email.
  METHOD if_ex_fi_int_cus01~int_print_options.
DATA ls_cus_addrres TYPE szadr_addr1_complete.
"Email in FINT

TRY.
mv_email = it_frange[ fieldname = c_email ]-selopt_t[ 1 ]-low.

CATCH cx_sy_itab_line_not_found.
ENDTRY.

"no email sending from preview and print preview
IF sy-tcode = c_fintshow OR control_parameters-preview IS NOT INITIAL OR mv_email IS INITIAL.
RETURN.
ENDIF.



mt_ipf_proc_seq = VALUE #( BASE mt_ipf_proc_seq ( is_ipf ) ).

"read main email address of customer - can be changed to other type of read

"read email address from Customer master data
CALL FUNCTION 'ADDR_GET_COMPLETE'
EXPORTING
addrnumber = is_kna1-adrnr
IMPORTING
addr1_complete = ls_cus_addrres
EXCEPTIONS
parameter_error = 1
address_not_exist = 2
internal_error = 3
wrong_access_to_archive = 4
address_blocked = 5
OTHERS = 6.

IF sy-subrc NE 0.
RETURN.
ENDIF.

LOOP AT ls_cus_addrres-adsmtp_tab ASSIGNING FIELD-SYMBOL(<fs_adsmtp>) WHERE adsmtp-flg_nouse IS INITIAL AND adsmtp-smtp_addr IS NOT INITIAL.

IF <fs_adsmtp>-adsmtp-flgdefault EQ abap_true.

"add recipients
mt_email_recipients = VALUE #( BASE mt_email_recipients ( bukrs = is_ipf-bukrs
koart = is_ipf-koart
konko = is_ipf-account
receiver = <fs_adsmtp>-adsmtp-smtp_addr
) ).

ELSE.
mt_email_recipients = VALUE #( BASE mt_email_recipients ( bukrs = is_ipf-bukrs
koart = is_ipf-koart
konko = is_ipf-account
receiver = <fs_adsmtp>-adsmtp-smtp_addr
copy = abap_true
) ).
ENDIF.

ENDLOOP.

IF line_exists( mt_email_recipients[ bukrs = is_ipf-bukrs
koart = is_ipf-koart
konko = is_ipf-account ] ).

"prepare OTF data for PDF sending
control_parameters-getotf = abap_true.
output_options-tdnewid = abap_true.

IF line_exists( mt_output_language[ bukrs = is_ipf-bukrs
koart = is_ipf-koart
konko = is_ipf-account ] ).
ELSE.
"prepare list of output languages
mt_output_language = VALUE #( BASE mt_output_language ( bukrs = is_ipf-bukrs
koart = is_ipf-koart
konko = is_ipf-account
spras = control_parameters-langu
name1 = is_kna1-name1 ) ).
ENDIF.

ENDIF.

ENDMETHOD.

 

FINT Email Sending: INT_PRINT_SPOOL_CLOSED


At the point of time when this method is called the spool is already closed, interest tables are updated with proper statuses and now the email creation can happen undisturbed.

The logic is processed when the ‘preview’ option was not set and the status is not in process as well as the interest document number was assigned.

This option contains also a check if the data is delivered in OTF form.

As not further actions are executed after his method email sending is done using CL_BCS class.
METHOD if_ex_fi_int_cus01~int_print_spool_closed.
*------------------*
* Local Variables *
*------------------*
DATA: lv_dirty_assign_preview TYPE string VALUE '(SAPLFI_INT)GB_PREVIEW'. "holding print preview indicator
DATA: lv_doc_size TYPE so_obj_len. " PDF document size
DATA: lv_pdf_xstring TYPE xstring. "PDF conversion into xstring
DATA: ls_email_subject TYPE itcpo-tdtitle. "email subject
*------------------*
* Local Structures *
*------------------*
DATA: ls_ipf_global TYPE intipf.
*------------------*
* Local Tables *
*------------------*
DATA: lt_pdf_lines TYPE tline_tab. "converted lines to PDF
DATA: lt_otf_lines TYPE efg_tab_itcoo. "OTF lines delivered by smartforms
DATA: lt_objbin TYPE STANDARD TABLE OF solix. "PDF in HEX format



"1.0 get global variables
READ TABLE mt_ipf_proc_seq INTO ls_ipf_global INDEX 1. "processing one by one
DELETE mt_ipf_proc_seq INDEX 1.
ASSIGN (lv_dirty_assign_preview) TO FIELD-SYMBOL(<fs_preview>).
IF sy-subrc NE 0.
RETURN.
ENDIF.

"1.1 first check
IF ( ls_ipf_global-int_status = gc_interest_status_process AND ls_ipf_global-form_to IS INITIAL ) " form number not generated yet
OR ( ls_ipf_global-int_belnr IS INITIAL AND ls_ipf_global-int_gjahr IS INITIAL AND ls_ipf_global-form_to IS INITIAL ) "document not posted yet
OR ( <fs_preview> EQ abap_true )
OR ( mv_email IS INITIAL ). "print preview only
RETURN.
ENDIF.

"1.2 - conditions for Email: no printout was generated
IF job_output_info-otfdata IS NOT INITIAL.

lt_otf_lines = job_output_info-otfdata.

"2.0 Convert OTF data into PDF

CALL FUNCTION 'CONVERT_OTF'
EXPORTING
format = c_attachment_type_pdf
max_linewidth = 132
IMPORTING
bin_filesize = lv_doc_size
bin_file = lv_pdf_xstring " This is NOT Binary. This is Hexa
TABLES
otf = lt_otf_lines
lines = lt_pdf_lines
EXCEPTIONS
err_max_linewidth = 1
err_format = 2
err_conv_not_possible = 3
OTHERS = 4.
IF sy-subrc <> 0.
RETURN.
ENDIF.
REFRESH lt_otf_lines.

"2.1 prepare HEX for PDF as attachment
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lv_pdf_xstring
TABLES
binary_tab = lt_objbin[].

READ TABLE mt_output_language WITH TABLE KEY bukrs = ls_ipf_global-bukrs
koart = ls_ipf_global-koart
konko = ls_ipf_global-account
ASSIGNING FIELD-SYMBOL(<fs_customer>).
IF sy-subrc EQ 0.

"2.3 Get email subject
"additional actions on subject - add Customer number + name

ls_email_subject = |{ 'Interest Email' } { ls_ipf_global-account ALPHA = OUT } { <fs_customer>-name1 }|.
ELSE.

ls_email_subject = |{ 'Interest Email' } { ls_ipf_global-account ALPHA = OUT }|.
ENDIF.


"2.4 Get email content
DATA(lt_email_content) = VALUE soli_tab( ( |{ 'email text ' }| ) ).


TRY.

"3.0 Initiate object for email sending
DATA(lo_send_email) = cl_bcs=>create_persistent( ).

"3.1 Initialite attached document
DATA(lo_email_attachment) = cl_document_bcs=>create_document( i_type = 'RAW'
i_text = lt_email_content
i_subject = ls_email_subject ). "#EC NOTEXT

"3.2 add the PDF as attachment to document object
lo_email_attachment->add_attachment( i_attachment_type = c_attachment_type_pdf
i_attachment_subject = CONV so_obj_des( ls_email_subject )
i_att_content_hex = lt_objbin
).

"3.3 add document object to email
lo_send_email->set_document( lo_email_attachment ).

"3.4 add recipient
LOOP AT mt_email_recipients ASSIGNING FIELD-SYMBOL(<fs_email_recipient>) WHERE koart = ls_ipf_global-koart
AND konko = ls_ipf_global-account.

DATA(lo_email_recipient) = cl_cam_address_bcs=>create_internet_address( <fs_email_recipient>-receiver ).

CASE abap_true.
WHEN <fs_email_recipient>-copy.

lo_send_email->add_recipient( i_recipient = lo_email_recipient
i_copy = abap_true ).

WHEN <fs_email_recipient>-blind_copy.

lo_send_email->add_recipient( i_recipient = lo_email_recipient
i_blind_copy = abap_true ).

WHEN OTHERS.
lo_send_email->add_recipient( lo_email_recipient ).
ENDCASE.

ENDLOOP.

"3.5 Set status
lo_send_email->set_status_attributes( i_requested_status = CONV bcs_rqst('N') ).

"3.6 send email
DATA(ls_result) = lo_send_email->send( ).

COMMIT WORK.

"check the result of sending
IF ls_result IS INITIAL.
MESSAGE i500(sbcoms) WITH <fs_email_recipient>-receiver.
ROLLBACK WORK.
ELSE.
MESSAGE s022(so).
ENDIF.

CATCH cx_bcs INTO DATA(lo_email_exception).
MESSAGE i500(sbcoms) WITH <fs_email_recipient>-receiver.
ENDTRY. " on email sending
ENDIF. "on provided OTF data

ENDMETHOD.

This method ends the required implementation.

 

Example of use


To show how the solution works 3 customers were defined of which 2 only have email addresses updated.







Several invoices were posted and paid with some days in arrears:



Executing FINT shows the new field is available:



Executing FINT with ‚test run’ marked provides a list of selected and calculated interest. If this is not selected then the update of master data and printout will be triggered automatically.

For test purposes I run the report with test run enabled to see the proposal first.



Final processing is done with button



Then:

Items were processed successfully:



At the end 2 emails should be send out to customers – they can be visible in SOST:



With PDF attached to email:



Direct processing (without test run) is also supported:







At the end the result is the same - emails are generated and visible in SOST:



 

Thank you for reading - in next blog I will show how to enhance FINT output by custom fields like customer name.

Marek
22 Comments
Labels in this area