logo

Are you need IT Support Engineer? Free Consultant

How to Create an Application Job (APJ) in SAP S/4H…

  • By sujay
  • 22/05/2026
  • 9 Views

This post covers end-to-end implementation of an Application Job using IF_APJ_RT_EXEC_OBJECT and IF_APJ_DT_EXEC_OBJECT interfaces, including both Single Value parameters and Select Option parameters.


What is an Application Job?

Application Jobs (APJ) are the modern replacement for classic SM36/SM37 background jobs in S/4HANA. They run via class-based execution and support proper parameter definition through the ADT (Eclipse) framework.


Step 1 — Create the Class

Your class must implement two interfaces:

  • IF_APJ_RT_EXEC_OBJECT — Runtime execution
  • IF_APJ_DT_EXEC_OBJECT — Design-time parameter definition

abap

CLASS zcl_my_apj_job DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES:
      if_oo_adt_classrun,
      if_apj_rt_exec_object,
      if_apj_dt_exec_object.

    " Single value parameter constants
    CONSTANTS: gv_date TYPE char8  VALUE 'SnapDate',
               gv_lang TYPE char5  VALUE 'LANGUAGE',
               gv_uom  TYPE char5  VALUE 'UOM'.

    " Select option parameter constants
    CONSTANTS: gv_contracttype TYPE char10 VALUE 'SO_CON',
               gv_compcode     TYPE char10 VALUE 'SO_COMP',
               gv_plant        TYPE char10 VALUE 'SO_Plant'.

ENDCLASS.

Step 2 — GET_PARAMETERS : Single Values

abap

METHOD if_apj_dt_exec_object~get_parameters.

  CLEAR: et_parameter_def, et_parameter_val.

  CONSTANTS: lc_langu TYPE char5 VALUE 'SPRAS',
             lc_date  TYPE char4 VALUE 'DATS',
             lc_uom   TYPE char5 VALUE 'MEINS'.

  et_parameter_def = VALUE #(

    ( selname        = gv_date      kind           = if_apj_dt_exec_object=>parameter      datatype       = lc_date      length         = 20
      param_text     = 'Snapshot Date'
      changeable_ind = abap_true )

    ( selname        = gv_lang      kind           = if_apj_dt_exec_object=>parameter      datatype       = lc_langu      length         = 20
      param_text     = 'Language'
      changeable_ind = abap_true )

    ( selname        = gv_uom      kind           = if_apj_dt_exec_object=>parameter      datatype       = lc_uom      length         = 20
      param_text     = 'Unit of Measure'
      changeable_ind = abap_true )
  ).

ENDMETHOD.

Step 3 — GET_PARAMETERS : Select Options

abap

METHOD if_apj_dt_exec_object~get_parameters.

  CLEAR: et_parameter_def, et_parameter_val.

  CONSTANTS: lc_char  TYPE char8 VALUE 'CHAR30',
             lc_lngth TYPE int2  VALUE 50.

  et_parameter_def = VALUE #(

    ( selname        = gv_contracttype      kind           = if_apj_dt_exec_object=>select_option      datatype       = lc_char      length         = lc_lngth      component_type = 'TCTYP'
      param_text     = 'Contract Type'
      changeable_ind = abap_true )

    ( selname        = gv_compcode      kind           = if_apj_dt_exec_object=>select_option      datatype       = lc_char      length         = lc_lngth      component_type = 'BUKRS'
      param_text     = 'Company Code'
      changeable_ind = abap_true )

    ( selname        = gv_plant      kind           = if_apj_dt_exec_object=>select_option      datatype       = lc_char      length         = lc_lngth      component_type = 'WERKS_D'
      param_text     = 'Plant'
      changeable_ind = abap_true )
  ).

ENDMETHOD.

Step 4 — EXECUTE Method : Single Values

METHOD if_apj_rt_exec_object~execute.

  DATA: lv_date      TYPE dats,
        lv_lang      TYPE spras,
        lv_uom       TYPE meins,
        lv_text_item TYPE string,
        lv_string    TYPE string,
        lv_timestamp TYPE timestamp,
        lv_filename  TYPE string,
        lt_final_data TYPE TABLE OF string.

  " Read incoming parameters
  LOOP AT it_parameters INTO DATA(ls_param) WHERE low IS NOT INITIAL.
    CASE ls_param-selname.
      WHEN gv_date. lv_date = ls_param-low.
      WHEN gv_lang. lv_lang = ls_param-low.
      WHEN gv_uom.  lv_uom  = ls_param-low.
    ENDCASE.
  ENDLOOP.

  " Read from your CDS / DB view
  SELECT * FROM zcds_your_query_view(
                  p_date     = @lv_date,
                  p_language = @lv_lang,
                  p_uom      = @lv_uom )
    INTO TABLE @DATA(lt_results).

  " Build pipe-delimited output dynamically using RTTI
  LOOP AT lt_results INTO DATA(ls_row).
    CLEAR lv_text_item.
    DATA(lo_struct) = CAST cl_abap_structdescr(
                        cl_abap_typedescr=>describe_by_data( ls_row ) ).

    LOOP AT lo_struct->components INTO DATA(ls_comp).
      ASSIGN COMPONENT ls_comp-name OF STRUCTURE ls_row             TO FIELD-SYMBOL().
      IF  IS ASSIGNED.
        lv_string = CONV #(  ).
        IF lv_text_item IS INITIAL.
          lv_text_item = lv_string.
        ELSE.
          CONCATENATE lv_text_item lv_string            INTO lv_text_item SEPARATED BY '|'.
        ENDIF.
      ENDIF.
    ENDLOOP.

    APPEND lv_text_item TO lt_final_data.
  ENDLOOP.

  " Write output file
  GET TIME STAMP FIELD lv_timestamp.
  lv_filename = |ZREPORT_OUTPUT_{ lv_date }_{ lv_timestamp }|.

  IF lt_final_data IS NOT INITIAL.
    OPEN DATASET lv_filename FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.
    LOOP AT lt_final_data INTO DATA(ls_line).
      TRANSFER ls_line TO lv_filename.
    ENDLOOP.
    CLOSE DATASET lv_filename.
  ENDIF.

ENDMETHOD.

Step 5 — EXECUTE Method : Select Options

 

METHOD if_apj_rt_exec_object~execute.

  DATA: lt_contract_type TYPE RANGE OF tctyp,
        lt_plant         TYPE RANGE OF werks_d,
        lt_company_code  TYPE RANGE OF bukrs,
        lv_where_clause  TYPE string,
        lv_text_header   TYPE string,
        lv_text_item     TYPE string,
        lv_filename      TYPE string,
        lt_final_data    TYPE TABLE OF string,
        lt_temp          TYPE dfies_tab.

  " Build WHERE clause dynamically from select options
  LOOP AT it_parameters INTO DATA(ls_param) WHERE low IS NOT INITIAL.
    CASE ls_param-selname.
      WHEN gv_contracttype.
        APPEND VALUE #( sign   = 'I'
                        option = ls_param-option                        low    = ls_param-low                        high   = ls_param-high ) TO lt_contract_type.
        lv_where_clause = COND #(
          WHEN lv_where_clause IS INITIAL
          THEN 'contracttype IN @lt_contract_type'
          ELSE lv_where_clause && ' AND contracttype IN @lt_contract_type' ).

      WHEN gv_compcode.
        APPEND VALUE #( sign   = 'I'
                        option = ls_param-option                        low    = ls_param-low                        high   = ls_param-high ) TO lt_company_code.
        lv_where_clause = COND #(
          WHEN lv_where_clause IS INITIAL
          THEN 'companycode IN @lt_company_code'
          ELSE lv_where_clause && ' AND companycode IN @lt_company_code' ).

      WHEN gv_plant.
        APPEND VALUE #( sign   = 'I'
                        option = ls_param-option                        low    = ls_param-low                        high   = ls_param-high ) TO lt_plant.
        lv_where_clause = COND #(
          WHEN lv_where_clause IS INITIAL
          THEN 'plant IN @lt_plant'
          ELSE lv_where_clause && ' AND plant IN @lt_plant' ).
    ENDCASE.
  ENDLOOP.

  " Select using dynamic WHERE clause
  SELECT * FROM zcds_your_position_view    WHERE (lv_where_clause)
    INTO TABLE @DATA(lt_position).

  IF sy-subrc = 0.
    " Get column headers via DDIF_FIELDINFO_GET
    CALL FUNCTION 'DDIF_FIELDINFO_GET'
      EXPORTING
        tabname        = 'ZCDS_YOUR_POSITION_VIEW'
        langu          = sy-langu      TABLES
        dfies_tab      = lt_temp      EXCEPTIONS
        not_found      = 1
        internal_error = 2
        OTHERS         = 3.

    " Build header row
    DATA(lt_columns) = VALUE string_table(
      FOR ls_f IN lt_temp INDEX INTO lv_idx      WHERE ( lv_idx > 1 ) " skip MANDT
      ( ls_f-fieldname ) ).

    LOOP AT lt_columns INTO DATA(lv_col).
      lv_text_header = COND #(
        WHEN lv_text_header IS INITIAL THEN lv_col        ELSE lv_text_header && '|' && lv_col ).
    ENDLOOP.

    " Build data rows
    LOOP AT lt_position INTO DATA(ls_pos).
      " Concatenate your fields with | separator
      CONCATENATE
        ls_pos-field1  ls_pos-field2  ls_pos-field3        " ... add all required fields
        INTO lv_text_item SEPARATED BY '|'.
      APPEND lv_text_item TO lt_final_data.
    ENDLOOP.

    " Insert header at top
    INSERT lv_text_header INTO lt_final_data INDEX 1.

    " Write to application server
    CONCATENATE '/usr/sap/trans/' sy-uname '_' sy-uzeit '.xls'
      INTO lv_filename.

    IF lt_final_data IS NOT INITIAL.
      OPEN DATASET lv_filename FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.
      LOOP AT lt_final_data INTO DATA(ls_line).
        TRANSFER ls_line TO lv_filename.
      ENDLOOP.
      CLOSE DATASET lv_filename.
    ENDIF.
  ENDIF.

ENDMETHOD.

Step 6 — Create Job Catalog & Template in ADT

  1. In ADT (Eclipse) → New → Application Job Catalog Entry
    • Enter your class name (ZCL_MY_APJ_JOB)
    • Give it a Catalog Name (e.g. ZCAT_MY_JOB)
  2. New → Application Job Template
    • Reference the Catalog Name created above
    • Define default parameter values
  3. Schedule via Application Jobs Fiori app (transaction /n/SDF/JOBD or Fiori Launchpad)

Source link

Leave a Reply

Your email address will not be published. Required fields are marked *