Indu Learning Platform
Course/Unit 2 · ABAP for MDG
Lesson 2-1

Modern ABAP in one sitting

The ABAP 7.4+ syntax, object orientation and ABAP Unit you will meet in every MDG enhancement written in the last ten years.

Watch firstSAP ABAP 7.4+ New Syntax: Intro to Inline Declarations (Part 1) · Smart Logic AcademyOpen on YouTube ↗

Why a functional consultant needs to read ABAP

On a technical MDG role you write the rules you used to specify. Most MDG code is small: a validation BAdI is often 40 lines, a derivation 20. What makes it look hard is the modern syntax. MDG enhancements live in the middle two layers below: classes, and the 7.4+ expression syntax.

CDS views and RAPI_BusinessPartner, ODataABAP 7.4+ expressionsDATA(...), VALUE, FOR, REDUCEObject-oriented ABAPclasses, interfaces, BAdIsClassic ABAPreports, FMs, FORMs
Top layer is the newest. MDG BAdIs are classes that use the expression syntax.

Which syntax can you use?

NetWeaver 7.40 brought inline declarations; every S/4HANA release runs on 7.50 or later, so on S/4 MDG you can use everything here.

Classic ABAPreports, FMs, LSMW eraABAP Objectsclasses, BAdIs7.40 (2013+)inline DATA, VALUE,FOR7.50 (2015)S/4HANA baselineABAP Cloud (2022+)released APIs, cleancore
Check SAP_BASIS under System > Status before using newer syntax.

Reading data: one SELECT, step by step

The modern SELECT: commas between fields, @ before every ABAP variable, and @DATA(...) declaring the result table on the spot. Click through the steps.

Step through · 1 of 5
DATA(lv_since)inline variableSELECT fieldspartner, bu_group...WHERE ... @lv_sincehost variableINTO TABLE@DATA(lt_orgs)inline tablesy-subrc0 = rows found
CONV d( ) turns the result into a date; DATA(...) declares lv_since where it is first used.
Click Next to walk through the statement.
ABAP
DATA(lv_since) = CONV d( sy-datum - 365 ).

SELECT partner, bu_group, name_org1, crdat
  FROM but000
  WHERE type  = '2'            " 2 = organisation
    AND crdat >= @lv_since
  ORDER BY crdat DESCENDING
  INTO TABLE @DATA(lt_orgs)
  UP TO 500 ROWS.

IF sy-subrc <> 0.
  RETURN.  " nothing found
ENDIF.

Try it: write the SELECT

Try it · Fill the blanks

Complete this modern SELECT and count the rows.

SELECT partner, name_org1
  FROM but000
  WHERE crdat >= 
  INTO TABLE .

DATA(lv_count) = ( lt_orgs ).

Classic to modern

Standard SAP is full of the left side. Read both; write the right.

Classic ABAPModern ABAP 7.4+DATA lt_x TYPE TABLE OF ... +SELECT INTOREAD TABLE ... WITH KEY ...INTO lsLOOP + APPEND to build a tableLOOP + ADD to sum a valueMOVE-CORRESPONDINGCONCATENATE ... INTO lvINTO TABLE @DATA(lt_x)lt_x[ key = ... ] /line_exists( )VALUE #( FOR ... )REDUCE #( INIT ... FOR ... NEXT... )CORRESPONDING #( ... MAPPING... )|{ a } { b }| string templateinlinetable expressionconstructorfoldwith mappingtemplate
Same meaning, fewer lines.

Constructor expressions

They build a value in one statement, like a Data Services transform: rows in, rows out. A table expression such as lt_x[ lifnr = ... ] raises CX_SY_ITAB_LINE_NOT_FOUND when no row matches.

OperatorBuildsMigration analogyVALUE #( )structure or tabletarget record layoutFOR ... INrow loop inside VALUEQuery transformREDUCEone value from many rowsrecord count checkCOND / SWITCHvalue from a conditionconversion rule IFCORRESPONDINGcopy by field namesource-to-target mapping
The five operators that cover most MDG enhancement code.
Try it · Match

Match each construct to what it does.

Pick an item on the left, then its match on the right.

Let the database count

A data-quality report (partners per country with an empty postal code) is one SELECT ... GROUP BY joining BUT000, BUT020 and ADRC. Counting in the database beats reading everything and counting in ABAP.

Classic: SELECT, LOOP,COLLECT, WRITE34 linesModern: SELECT ... GROUP BY +display10 linesCDS view + analytical query8 lines
Approximate line counts for the same report (our examples, not a benchmark).

Classes and interfaces

Every MDG BAdI is a class implementing an SAP interface. Think of a class as a function group whose functions share data, and an interface as a published signature many classes can implement. Keep rules in plain classes, such as ZCL_TAX_RULE_DE implementing ZIF_TAX_RULE~IS_VALID, so they can be tested without MDG.

Function module styleGlobal data in a function groupCalled by name with CALL FUNCTIONHard to replace in a testClass + interface styleState inside an objectCalled through an interface referenceSwap in a test double easilyRequired for BAdIs and RAPvs
New MDG code should be classes. Wrap old function modules rather than copying them.

ABAP Unit: test the rule, not the screen

A test class is marked FOR TESTING, lives with the class it tests and never touches production data. Run it with Ctrl+Shift+F10 in ADT (the Eclipse editor; use it for all new code). Ask an AI assistant for tests too, and run them before trusting its code.

Redwrite a failing testGreenmake it passRefactortidy, re-runATC checkquality gate
ATC (ABAP Test Cockpit) runs static checks before transport.
ABAP
CLASS ltc_tax_rule DEFINITION FINAL FOR TESTING
  DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    METHODS short_german_id_fails FOR TESTING.
ENDCLASS.

CLASS ltc_tax_rule IMPLEMENTATION.
  METHOD short_german_id_fails.
    DATA lo_cut TYPE REF TO zif_tax_rule.
    lo_cut = NEW zcl_tax_rule_de( ).
    cl_abap_unit_assert=>assert_false(
      act = lo_cut->is_valid( iv_country = 'DE' iv_tax_number = 'DE1234' )
      msg = 'Short German VAT ID must fail' ).
  ENDMETHOD.
ENDCLASS.

Local Test Classes tab of ZCL_TAX_RULE_DE in ADT.

Try it: a short dump

Try it · What would you do?

Your report dumps with CX_SY_ITAB_LINE_NOT_FOUND on the line DATA(ls) = lt_suppliers[ lifnr = lv_lifnr ]. What is the right fix?

Remember

  • Modern SELECT: comma-separated fields, @ for ABAP variables, @DATA(...) for an inline target table.
  • VALUE, FOR, REDUCE, COND and CORRESPONDING replace most LOOP/APPEND/MOVE code; let the database count.
  • BAdIs are classes implementing interfaces; keep rules in plain classes with ABAP Unit tests.

Check yourself

In SELECT ... WHERE crdat >= @lv_since INTO TABLE @DATA(lt_orgs), what does the @ in front of lv_since mean?
You need the number of materials in a table lt_mara that have an empty gross weight. Which expression fits best?
What happens if lt_suppliers[ lifnr = '0000999999' ] finds no row?
Why keep a tax-number rule in its own class rather than directly inside the MDG BAdI method?