Note : Une nouvelle version pour créer des application jobs est disponible et sera abordée dans un nouvel article. Cet article utilise d’anciennes interfaces.
Pour rappel, en ABAP Cloud (la seule version d’ABAP disponible pour S/4 HANA Cloud, Public Edition et celle recommandée pour les autres versions S/4 HANA Private Edition et On Premise), les écrans GUI ne sont plus disponibles, et toute UI doit être Fiori.
Qu’en est-il alors des REPORTs que nous avions l’habitude de programmer tous les jours ? toutes les heures ?
Une solution existe : Application Jobs disponible sur S/4 HANA cloud, Public/ Private Edition, On-Premise et BTP, Abap Environment. Comme indiquée dans la documentation, c’est une application Fiori standard pour planifier et surveiller des jobs.
Un ensemble d’applications standards est déjà disponible afin d’être planifié en arrière plan, mais il est également possible de créer ses propres jobs, c’est ce que nous allons décomposer ici.
Remplacer le REPORT
Si vous aviez pour habitude de programmer des Reports sur vos systèmes SAP, ce n’est plus possible avec la version ABAP Cloud.
En remplacement, nous créerons :
- Une classe qui contiendra toute la logique que nous souhaitons mettre en place lors de ce job (cette classe doit implémenter les interfaces IF_APJ_DT_EXEC_OBJECT et IF_APJ_RT_EXEC_OBJECT)
- Une fois cette classe créée nous créerons un « Job Catalog Entry », qui comme précisé dans la documentation : contient les informations nécessaires à la planification et à l’exécution d’une « application job ». Cela contient le nom de la classe créée précédemment et les informations sur la manière dont les champs de sélection sont rendus disponibles dans l’application.
- Enfin, pour que le job soit disponible dans l’application Fiori Application Jobs, il faut créer un « Job Template » qui se réfère au « Job Catalog Entry » et contient des valeurs pour certains ou tous les champs de sélection. Dans la tuile Fiori Application Jobs, les champs de sélection apparaissent et sont pré-remplis avec les valeurs définies dans le template. Elles peuvent être remplacées par l’utilisateur.
- Il est enfin possible de programmer son job dans la tuile Application Jobs OU d’utiliser l’API CL_APJ_RT_API pour effectuer cette programmation.
- De manière générale, un business user utilisera toujours la tuile Application Jobs pour programmer des jobs alors qu’un développeur pourra passer par un bout de code pour programmer son job.
- Il est également possible de créer des jobs lors de l’exécution de différents processus (comme ça pouvait être fait à l’époque avec les modules fonction JOB_OPEN, JOB_CLOSE, etc… mais désormais en passant par l’API CL_APJ_RT_API).
Créer la logique
Comme indiqué précédemment la première chose à faire est de créer une classe qui implémente les interfaces IF_APJ_DT_EXEC_OBJECT et IF_APJ_RT_EXEC_OBJECT
CLASS zjob_test DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_apj_dt_exec_object.
INTERFACES if_apj_rt_exec_object.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zjob_test IMPLEMENTATION.
METHOD if_apj_dt_exec_object~get_parameters.
" Return the supported selection parameters here
et_parameter_def = VALUE #(
( selname = 'S_ID' kind = if_apj_dt_exec_object=>select_option datatype = 'C' length = 10 param_text = 'My ID' changeable_ind = abap_true )
( selname = 'P_DESCR' kind = if_apj_dt_exec_object=>parameter datatype = 'C' length = 80 param_text = 'My Description' lowercase_ind = abap_true changeable_ind = abap_true )
( selname = 'P_COUNT' kind = if_apj_dt_exec_object=>parameter datatype = 'I' length = 10 param_text = 'My Count' changeable_ind = abap_true )
( selname = 'P_SIMUL' kind = if_apj_dt_exec_object=>parameter datatype = 'C' length = 1 param_text = 'My Simulate Only' checkbox_ind = abap_true changeable_ind = abap_true )
).
" Return the default parameters values here
et_parameter_val = VALUE #(
( selname = 'S_ID' kind = if_apj_dt_exec_object=>select_option sign = 'I' option = 'EQ' low = '4711' )
( selname = 'P_DESCR' kind = if_apj_dt_exec_object=>parameter sign = 'I' option = 'EQ' low = 'My Default Description' )
( selname = 'P_COUNT' kind = if_apj_dt_exec_object=>parameter sign = 'I' option = 'EQ' low = '200' )
( selname = 'P_SIMUL' kind = if_apj_dt_exec_object=>parameter sign = 'I' option = 'EQ' low = abap_true )
).
ENDMETHOD.
METHOD if_apj_rt_exec_object~execute.
TYPES ty_id TYPE c LENGTH 10.
DATA s_id TYPE RANGE OF ty_id.
DATA p_descr TYPE c LENGTH 80.
DATA p_count TYPE i.
DATA p_simul TYPE abap_boolean.
DATA: jobname type cl_apj_rt_api=>TY_JOBNAME.
DATA: jobcount type cl_apj_rt_api=>TY_JOBCOUNT.
DATA: catalog type cl_apj_rt_api=>TY_CATALOG_NAME.
DATA: template type cl_apj_rt_api=>TY_TEMPLATE_NAME.
" Getting the actual parameter values
LOOP AT it_parameters INTO DATA(ls_parameter).
CASE ls_parameter-selname.
WHEN 'S_ID'.
APPEND VALUE #( sign = ls_parameter-sign
option = ls_parameter-option
low = ls_parameter-low
high = ls_parameter-high ) TO s_id.
WHEN 'P_DESCR'. p_descr = ls_parameter-low.
WHEN 'P_COUNT'. p_count = ls_parameter-low.
WHEN 'P_SIMUL'. p_simul = ls_parameter-low.
ENDCASE.
ENDLOOP.
try.
* read own runtime info catalog
cl_apj_rt_api=>GET_JOB_RUNTIME_INFO(
importing
ev_jobname = jobname
ev_jobcount = jobcount
ev_catalog_name = catalog
ev_template_name = template ).
catch cx_apj_rt.
endtry.
" Implement the job execution
ENDMETHOD.
ENDCLASS.
Dans la méthode if_apj_dt_exec_object~get_parameters, on indique les paramètres disponibles pour se Job et leur valeur par défaut s’il y en a.
Dans la méthode if_apj_rt_exec_object~execute, c’est toute la logique qui s’exécute. Ici ça ne fait rien de spéciale sauf retrourner les informations du JOB en question, mais il aurait été possible de mettre toute la logique souhaitée.
Créer le Job Catalog Entry
Créer le Job Catalog Entry et sélectionner la classe créée précédemment qui sera exécutée
Créer le Job Template
Vous y retrouverez les paramètres et les valeurs par défaut configurables :
Planification du Job
- Via API
Créer une classe pouvant être executée directement en implementant l’interface if_oo_adt_classrun.
CLASS zcl_job_api_tst DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_job_api_tst IMPLEMENTATION.
" <SIGNATURE>---------------------------------------------------------------------------------------+
" | Instance Public Method Z_CL_RT_API_DEMO->IF_OO_ADT_CLASSRUN~MAIN
" +-------------------------------------------------------------------------------------------------+
" | [--->] OUT TYPE REF TO IF_OO_ADT_CLASSRUN_OUT
" +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD if_oo_adt_classrun~main.
DATA lv_job_text TYPE cl_apj_rt_api=>ty_job_text VALUE 'Demo_Job'.
DATA lv_template_name TYPE cl_apj_rt_api=>ty_template_name.
DATA ls_start_info TYPE cl_apj_rt_api=>ty_start_info.
DATA ls_scheduling_info TYPE cl_apj_rt_api=>ty_scheduling_info.
DATA ls_end_info TYPE cl_apj_rt_api=>ty_end_info.
DATA lt_job_parameters TYPE cl_apj_rt_api=>tt_job_parameter_value.
DATA ls_job_parameters TYPE cl_apj_rt_api=>ty_job_parameter_value.
DATA ls_value TYPE cl_apj_rt_api=>ty_value_range.
DATA lv_jobname TYPE cl_apj_rt_api=>ty_jobname.
DATA lv_jobcount TYPE cl_apj_rt_api=>ty_jobcount.
DATA lv_status TYPE cl_apj_rt_api=>ty_job_status.
DATA lv_statustext TYPE cl_apj_rt_api=>ty_job_status_text.
DATA lv_txt TYPE string.
DATA ls_ret TYPE bapiret2.
" Choose the name of the existing job template.
lv_template_name = 'ZJOB_TEMPLATE_TEST'.
" The immediate start can't be used when being called from within a RAP business object
" because the underlying API performs an implicit COMMIT WORK.
" ls_start_info-start_immediately = 'X'.
" Start the job using a timestamp instead. This will start the job immediately but can have a delay depending on the current workload.
GET TIME STAMP FIELD DATA(ls_ts1).
" Add 1 hour
DATA(ls_ts2) = cl_abap_tstmp=>add( tstmp = ls_ts1
secs = 3600 ).
ls_start_info-timestamp = ls_ts2.
" Periodicity
ls_scheduling_info-periodic_granularity = 'D'.
ls_scheduling_info-periodic_value = 1.
ls_scheduling_info-test_mode = abap_false.
ls_scheduling_info-timezone = 'CET'.
ls_end_info-type = 'NUM'.
ls_end_info-max_iterations = 3.
" Fill parameter table:
" Fill the table only if you want to overrule the parameter values
" which are stored in the template.
" The field names in this program must match the field names of the template.
ls_job_parameters-name = 'P_TEST1'.
ls_value-sign = 'I'.
ls_value-option = 'EQ'.
ls_value-low = 'Test 1'.
APPEND ls_value TO ls_job_parameters-t_value.
APPEND ls_job_parameters TO lt_job_parameters.
CLEAR ls_job_parameters.
ls_job_parameters-name = 'P_TEST2'.
ls_value-sign = 'I'.
ls_value-option = 'BT'.
ls_value-low = 'ATEST'.
ls_value-high = 'ZTEST'.
APPEND ls_value TO ls_job_parameters-t_value.
ls_job_parameters-name = 'P_TEST2'.
ls_value-sign = 'I'.
ls_value-option = 'BT'.
ls_value-low = '11111'.
ls_value-high = '99999'.
APPEND ls_value TO ls_job_parameters-t_value.
APPEND ls_job_parameters TO lt_job_parameters.
CLEAR ls_job_parameters.
ls_job_parameters-name = 'P_TEST3'.
ls_value-sign = 'I'.
ls_value-option = 'EQ'.
ls_value-low = '220'.
APPEND ls_value TO ls_job_parameters-t_value.
APPEND ls_job_parameters TO lt_job_parameters.
CLEAR ls_job_parameters.
TRY.
" Some scenarios require that the job key ( = jobname, jobcount) is already known
" before the job is created. The method generate_jobkey creates a valid job key.
" This key can then be passed later on to the method schedule_job, and a job with
" exactly this key is created.
" Optional. You need this call only if you have to know the job key in advance.
" cl_apj_rt_api=>generate_jobkey(
" importing
" ev_jobname = lv_jobname
" ev_jobcount = lv_jobcount ).
" If you pass the table lt_job_parameters , then the parameters
" contained in this table are used.
" If you don't pass the table, the parameters contained in the
" job template are used.
cl_apj_rt_api=>schedule_job(
EXPORTING
iv_job_template_name = lv_template_name
iv_job_text = lv_job_text
is_start_info = ls_start_info
is_scheduling_info = ls_scheduling_info
is_end_info = ls_end_info
it_job_parameter_value = lt_job_parameters
" The following two parameters are optional. If you pass them, they must have been generated
" with the optional call generate_jobkey above.
" iv_jobname = lv_jobname
" iv_jobcount = lv_jobcount
IMPORTING
ev_jobname = lv_jobname
ev_jobcount = lv_jobcount
).
out->write( lv_jobname ).
out->write( lv_jobcount ).
cl_apj_rt_api=>get_job_status(
EXPORTING
iv_jobname = lv_jobname
iv_jobcount = lv_jobcount
IMPORTING
ev_job_status = lv_status
ev_job_status_text = lv_statustext
).
out->write( lv_status ).
out->write( lv_statustext ).
" Via the following method you can cancel the job
" in the application job context 'cancel' means (as in the Fiori app):
" 1. if the job is running, it will be canceled
" 2. if the job has not yet started, it will be deleted.
" In case the job is periodic, the whole periodicity chain is deleted.
* To comment if you want to keep the job
cl_apj_rt_api=>cancel_job(
exporting
iv_jobname = lv_jobname "change with the job name you want to cancel
iv_jobcount = lv_jobcount "change with the job count you want to cancel
).
catch cx_apj_rt into data(exc).
lv_txt = exc->get_longtext( ).
ls_ret = exc->get_bapiret2( ).
out->write( 'ERROR:' ).
out->write( lv_txt ).
out->write( 'msg type =' ).
out->write( ls_ret-type ).
out->write( 'msg id =' ).
out->write( ls_ret-id ).
out->write( 'msg number =' ).
out->write( ls_ret-number ).
out->write( 'msg message =' ).
out->write( ls_ret-message ).
COMMIT WORK. "NOT NECESARY IN CASE OF RAP APPLICATION AS THE COMMIT IS HANDLED WITH THE RAP FRAMEWORK
ENDTRY.
ENDMETHOD.
ENDCLASS.
- Via la tuile Fiori Application Jobs
Si vous êtes sur un système On Premise ou Private Cloud, on peut même vérifier en SM37 que ça a été correctement programmé.
A noter, sur BTP, ABAP environment et S/4 Cloud Public edition, il y a la création d’une application IAM nécessaire pour l’attribution des autorisations. Vous pouvez retrouver la gestion des autorisations pour On-Premise et S/4 Cloud Private Edition ici.
Application Fiori : https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps(%27F1240%27)/S16OP