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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
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.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
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
