Tuesday, 23 September 2025

Belajar ABAP Part 13: CDS Consumption View (UI Annotation) & Unit Testing AMDP

Belajar ABAP Part 13: CDS Consumption View (UI Annotation) & Unit Testing AMDP

Belajar ABAP Part 13: CDS Consumption View (UI Annotation) & Unit Testing AMDP

Ringkasan: Di Part 13 ini lo bakal belajar dua hal krusial untuk ABAP modern:
  1. CDS Consumption Views + UI Annotation — bagaimana membuat CDS yang siap dipakai Fiori/OData dengan @UI.hints agar kolom muncul rapi di apps.
  2. Unit Testing AMDP — pattern dan contoh ABAP Unit untuk menguji AMDP (SQLScript) secara repeatable di development/CI pipeline.
Target: membuat data model yang consumable & procedure DB yang dapat diuji otomatis.

Bagian A — CDS Consumption View + UI Annotation

1) Tujuan dan kapan pakai Consumption View

Consumption View adalah layer CDS yang dioptimalkan untuk konsumsi (UI, OData, analytical). Pakai ini ketika lo ingin:

  • Expose data ke Fiori/Smart Template apps
  • Menyediakan field yang relevan di grid/detail view
  • Mengontrol metadata UI (label, ordering, search, filter)

2) Contoh sederhana: CDS Consumption View dengan UI Annotations

Contoh: kita punya basic view ZCDS_PO_BASE (PO header) dan mau bikin consumption view untuk Fiori list.


@AbapCatalog.sqlViewName: 'ZV_PO_BASE'
@EndUserText.label: 'PO Base View'
define view ZCDS_PO_BASE as select from ekko {
  key ebeln,
      bukrs,
      lifnr,
      bedat,
      netwr
}
    

Sekarang bikin consumption view dengan UI annotation:


@AbapCatalog.sqlViewName: 'ZV_PO_CONS'
@EndUserText.label: 'PO Consumption for Fiori'
@AccessControl.authorizationCheck: #CHECK
@OData.publish: true
define view ZCDS_PO_CONSUMPTION
  with parameters
    p_companycode : bukrs
  as select from ZCDS_PO_BASE
{
  key ebeln                                                      as PurchaseOrder,
  bukrs                                                          as CompanyCode,
  lifnr                                                          as Vendor,
  bedat                                                          as DocumentDate,
  netwr                                                          as NetValue,

  @UI.lineItem: [{ position: 10 }]
  @UI.selectionField: [{ position: 10 }]
  netwr                                                           as Amount
}
where bukrs = :p_companycode;
    

Penjelasan annotation penting:

  • @OData.publish: true → CDS akan expose service OData otomatis (aktifkan di /IWFND/MAINT_SERVICE).
  • @AccessControl.authorizationCheck: #CHECK → mengaktifkan pemeriksaan authorization (CDS-based ACL jika ada).
  • @UI.lineItem → menentukan kolom yang tampil di table/list (Fiori Smart Template).
  • @UI.selectionField → menentukan field yang tampil di filter bar (selection).

3) Men-deploy & expose OData service

  1. Activate CDS view di ADT (Eclipse) atau SE11 (jika tersedia).
  2. Buka /IWFND/MAINT_SERVICE → tambahkan service OData yang dibuat (service name biasanya autogen dari CDS).
  3. Test OData endpoint: https://{host}:443/sap/opu/odata/sap/{SERVICE_NAME}/ (SERVICE_NAME lihat di SDA atau di service registration).
  4. Gunakan Fiori Smart Template app (List Report) untuk konsumsi otomatis berdasarkan annotations.
Note: Untuk Fiori-ready UX, lengkapi juga annotation UI seperti @UI.headerInfo, @UI.lineItem dengan label, importance, dan @Search.searchable bila diperlukan.

4) Contoh annotation tambahan (headerInfo & search)


@UI.headerInfo: { typeName: 'PurchaseOrder', typeNamePlural: 'PurchaseOrders' }
@Search.searchable: true
@UI.lineItem: [{ position: 10, label: 'PO' }]
@UI.lineItem: [{ position: 20, label: 'Vendor' }]
define view ZCDS_PO_CONSUMPTION as select from ZCDS_PO_BASE { ... }
    

5) Tips praktis untuk CDS Consumption

  • Gunakan parameter pada consumption view bila perlu scoping (sebagai filter default).
  • Gunakan @ObjectModel.representativeKey bila key composite / perlu mapping ke entity.
  • Dokumentasikan mapping antara CDS field & requirement UI (label, format, currency).

Bagian B — Unit Testing AMDP (ABAP Managed Database Procedures)

1) Mengapa perlu unit test untuk AMDP?

AMDP mengeksekusi logic di database (SQLScript). Karena logic di-database berisiko (performa, hasil berbeda), kita butuh test automated agar:

  • Validasi correctness (aggregasi, bucketing, transform)
  • Mendeteksi regresi saat deploy
  • Mendukung CI/CD untuk ABAP/transport

2) Pendekatan testing untuk AMDP

Ada beberapa pattern:

  1. Integration-style ABAP Unit: buat test class ABAP Unit yang men-setup data di DB (insert ke table/temporary table), panggil AMDP, assert hasil.
  2. Mocking layer ABAP: bungkus pemanggilan AMDP di class ABAP non-AMDP yang bisa di-mock untuk unit test biasa (lebih mudah di-run tanpa DB dependency).
  3. HANA-side tests: tulis SQLScript test di HANA jika ada framework internal (lebih advanced).
Tip: untuk reliability, kombinasi approach #1 (integration tests di dev/CI) + #2 (pure unit tests untuk business logic wrapper) paling baik.

3) Contoh AMDP (kita pakai yang dari Part 12):


CLASS zcl_amdp_po_age DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_amdp_marker_hdb.
    CLASS-METHODS calc_po_aging
      IMPORTING iv_days1 TYPE i iv_days2 TYPE i
      RETURNING VALUE(rt_result) TYPE TABLE OF ty_po_aging.
ENDCLASS.

" (IMPLEMENTATION seperti Part 12 - menghasilkan rt_result)
    

4) Contoh ABAP Unit Test (Integration-style)

Keterangan pendek: test ini akan insert data test ke table EKKO/EKPO (atau ke table Z-temp jika prefer), lalu memanggil AMDP dan memeriksa hasil bucket.


CLASS ltc_amdp_po_age_test DEFINITION FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.

  PRIVATE SECTION.
    METHODS:
      setup FOR TESTING,
      teardown FOR TESTING,
      test_po_aging_basic FOR TESTING.

    DATA: mt_ekko TYPE TABLE OF ekko,
          mt_ekpo TYPE TABLE OF ekpo.
ENDCLASS.

CLASS ltc_amdp_po_age_test IMPLEMENTATION.

  METHOD setup.
    " Prepare test data: insert sample PO header & item
    " NOTE: gunakan RFC/user dengan privilege; atau gunakan Z-table test isolation
    CLEAR mt_ekko.
    CLEAR mt_ekpo.

    DATA(ls_ekko) = VALUE ekko( ebeln = 'ZTEST0001' bukrs = '1000' lifnr = '000001' bedat = '20250901' ).
    APPEND ls_ekko TO mt_ekko.

    DATA(ls_ekpo) = VALUE ekpo( ebeln = 'ZTEST0001' ebelp = '00010' netwr = '500000' ).
    APPEND ls_ekpo TO mt_ekpo.

    " Insert to DB (use transportable test data table or temporary Z-table recommended)
    INSERT ekko FROM TABLE mt_ekko.
    INSERT ekpo FROM TABLE mt_ekpo.
  ENDMETHOD.

  METHOD teardown.
    " Clean up test data
    DELETE FROM ekpo WHERE ebeln = 'ZTEST0001'.
    DELETE FROM ekko WHERE ebeln = 'ZTEST0001'.
  ENDMETHOD.

  METHOD test_po_aging_basic.
    " Call AMDP
    DATA(rt_result) = zcl_amdp_po_age=>calc_po_aging( iv_days1 = 30 iv_days2 = 60 ).

    " Assert that result contains expected bucket (e.g., '0-30' or other depending bedat)
    cl_abap_unit_assert=>assert_not_initial( act = rt_result ).
    " find row for bucket '0-30'
    READ TABLE rt_result INTO DATA(ls_row) WITH KEY bucket = '0-30'.
    cl_abap_unit_assert=>assert_true( act = sy-subrc = 0 ).
    cl_abap_unit_assert=>assert_true( act = ls_row-po_count >= 1 ).
  ENDMETHOD.

ENDCLASS.
    

Catatan penting tentang contoh di atas:

  • Penggunaan EKKO/EKPO langsung di DEV bisa berbahaya → lebih aman pakai Z-table test atau transactionally isolated test data.
  • Pastikan user yang menjalankan test punya privilege INSERT/DELETE pada table target (biasanya di DEV/CI saja).
  • Gunakan TRANSPORTABLE atau test data reset agar test idempotent (selalu bisa di-run ulang tanpa efek samping).

5) Teknik Mocking / Wrapper untuk AMDP

Untuk unit test murni (tanpa DB), bungkus pemanggilan AMDP ke class non-AMDP (interface). Di unit test, mock class wrapper tersebut untuk mengembalikan hasil yang diharapkan.


INTERFACE if_po_aging_service.
  METHODS calc_aging IMPORTING iv_d1 TYPE i iv_d2 TYPE i RETURNING VALUE(rt) TYPE TABLE OF ty_po_aging.
ENDINTERFACE.

" Prod implementation: wrapper yang memanggil AMDP
CLASS zcl_po_aging_service DEFINITION.
  PUBLIC SECTION.
    INTERFACES if_po_aging_service.
ENDCLASS.

CLASS zcl_po_aging_service IMPLEMENTATION.
  METHOD if_po_aging_service~calc_aging.
    rt = zcl_amdp_po_age=>calc_po_aging( iv_days1 = iv_d1 iv_days2 = iv_d2 ).
  ENDMETHOD.
ENDCLASS.

" Di unit test, buat fake/mock class (atau local test double) yang implement interface dan return static data.
    

6) Best Practices untuk Testing AMDP

  • Prefer test data di Z-tables khusus test agar tidak mengotori master/transactional table.
  • Jalankan integration tests di DEV/CI environment, bukan di sandbox/production.
  • Gunakan TEARDOWN untuk selalu bersih-bersih data test (DELETE by unique key).
  • Document precondition (HANA version, schema privileges, necessary objects).
  • Gunakan mocking wrapper bila perlu rapid unit testing.

Kesimpulan — Part 13

- CDS Consumption Views + UI annotations membuat model siap konsumsi Fiori/OData dengan sedikit usaha. - AMDP harus diuji: buat integration ABAP Unit tests (setup data → panggil AMDP → assert) dan gunakan wrapper/mocking untuk pure unit tests jika ingin tanpa DB dependency. - Kombinasi CDS (untuk modelling/exposure) + AMDP (untuk logic DB-heavy) + test automation = strategy yang kuat untuk aplikasi S/4HANA yang reliable dan performa tinggi.

👉 Lanjut ke: Belajar ABAP Part 14: Performance Tuning & HANA Optimization


Catatan: snippet di atas disederhanakan untuk pembelajaran. Untuk produksi, sesuaikan naming convention, object transport, dan policy keamanan (authorizations). Pastikan selalu validasi SQLScript functions pada HANA versi target.

Belajar ABAP Part 12: CDS View & AMDP

Belajar ABAP Part 12: CDS View & AMDP — Core Data Services dan ABAP Managed DB Procedures

Belajar ABAP Part 12: CDS View & AMDP — Core Data Services dan ABAP Managed DB Procedures

Ringkasan: CDS View dan AMDP adalah komponen kunci ABAP modern (S/4HANA) untuk menerapkan code pushdown. - CDS digunakan untuk modelling data di DB (view, associations, annotation, expose OData). - AMDP (ABAP Managed Database Procedures) memungkinkan menulis prosedur SQLScript yang dieksekusi langsung di HANA. Gunakan CDS untuk data modelling & exposure, dan AMDP saat diperlukan logic DB-procedural/berat.

1. Mengapa CDS & AMDP penting?

S/4HANA mendorong code pushdown — memindahkan pemrosesan berat dari ABAP stack ke database (HANA). Ini meningkatkan performa (in-memory processing, parallelization). CDS menyediakan cara semantik memodel data; AMDP mengeksekusi logic procedural di DB.

2. Apa itu CDS View?

Core Data Services (CDS) adalah bahasa deklaratif untuk membuat view level database dengan semantik bisnis. CDS mendukung annotation untuk authorization, OData exposure, dan UI hints (Fiori).

Contoh CDS View (basic)


@AbapCatalog.sqlViewName: 'ZV_MARA_MAT'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'CDS View Material Master'
define view ZCDS_MATERIAL as select from mara {
  key matnr,
      ersda,
      matkl,
      mtart
}
    

Penjelasan:

  • @AbapCatalog.sqlViewName → nama SQL view fisik di DB (maks 16 char).
  • @EndUserText.label → label untuk dokumentasi / UI.
  • key → kolom kunci pada CDS (required untuk beberapa use-case).

3. Association di CDS (pengganti JOIN manual)

Association memungkinkan mendefinisikan relasi antara entity/view sehingga bisa navigasi secara deklaratif.


@AbapCatalog.sqlViewName: 'ZV_PO_HDR'
define view ZCDS_PO as select from ekko
  association [0..*] to ekpo as _items on $projection.ebeln = _items.ebeln
{
  key ebeln,
      bukrs,
      lifnr,
      _items
}
    

Cara pakai association di query:


select from ZCDS_PO as po
  inner join po._items as item
  on po.ebeln = item.ebeln
  { po.ebeln, item.matnr } 
  where po.bukrs = '1000';
      

4. Jenis CDS View

  • Basic View — representasi satu tabel atau view dasar.
  • Composite View — gabungan dari basic view via join/association.
  • Consumption View — view yang dioptimalkan untuk konsumsi (Fiori, OData, analytical), berisi annotation khusus consumption.

Contoh Consumption View (OData exposure)


@AbapCatalog.sqlViewName: 'ZV_PO_CONS'
@OData.publish: true
@EndUserText.label: 'PO Consumption View for Fiori'
define view ZCDS_PO_CONSUMPTION as select from ZCDS_PO {
  key ebeln         as PurchaseOrder,
      lifnr         as Vendor,
      bukrs         as CompanyCode,
      _items         /* association: will be consumed */
}
    

@OData.publish: true membuat OData service otomatis tersedia (aktifkan service di /IWFND/MAINT_SERVICE).

5. Annotation Penting di CDS

  • @AccessControl.authorizationCheck — authorization di level CDS (#CHECK / #NOT_REQUIRED).
  • @Search.searchable — menandai field agar bisa dicari (Fiori).
  • @UI.lineItem, @UI.selectionField — hints untuk Fiori UI.
  • @ObjectModel — behaviour seperti aggregations, composition, dll.

6. Apa itu AMDP?

AMDP (ABAP Managed Database Procedures) memungkinkan developer menulis procedures/fungsi yang dieksekusi di DB (HANA) menggunakan SQLScript. Implementasinya berada di method ABAP class dengan keyword khusus `BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT`.

Contoh AMDP Class (simple)


CLASS zcl_amdp_sales DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES: if_amdp_marker_hdb.
    CLASS-METHODS get_top_customers
      IMPORTING iv_year TYPE gjahr
      RETURNING VALUE(rt_result) TYPE TABLE OF ty_cust_sales.
ENDCLASS.

CLASS zcl_amdp_sales IMPLEMENTATION.
  METHOD get_top_customers BY DATABASE PROCEDURE FOR HDB
    LANGUAGE SQLSCRIPT
    OPTIONS READ-ONLY
    USING vbak vbap kna1.

    rt_result =
      SELECT v~kunnr   AS kunnr,
             k~name1   AS name1,
             SUM( v~netwr ) AS total_sales
      FROM vbak AS v
      JOIN kna1 AS k ON v.kunnr = k.kunnr
      WHERE v.gjahr = :iv_year
      GROUP BY v.kunnr, k.name1
      ORDER BY total_sales DESC
      LIMIT 10;

  ENDMETHOD.
ENDCLASS.
    

Penjelasan singkat:

  • if_amdp_marker_hdb — marker interface untuk HANA AMDP.
  • BY DATABASE PROCEDURE FOR HDB — menandakan body method adalah SQLScript yang dijalankan di HANA.
  • USING — deklarasi tabel yang dipakai (optimisasi dependency).
  • Variabel ABAP di SQLScript dipanggil dengan prefix : (contoh :iv_year).

7. Memanggil AMDP dari ABAP


DATA(lt_top) = zcl_amdp_sales=>get_top_customers( iv_year = '2024' ).

LOOP AT lt_top INTO DATA(ls_row).
  WRITE: / ls_row-kunnr, ls_row-name1, ls_row-total_sales.
ENDLOOP.
    

8. Real Case: CDS + AMDP untuk Report PO Aging

Use-case: ingin laporan aging purchase order — hitung jumlah PO dan total value grouped by aging bucket. CDS bisa dipakai untuk struktur dasar, tapi jika logika bucketing kompleks atau heavy aggregation, gunakan AMDP.

CDS untuk PO dasar


@AbapCatalog.sqlViewName: 'ZV_PO_BASE'
@EndUserText.label: 'PO Base View'
define view ZCDS_PO_BASE as select from ekko {
  key ebeln,
      bukrs,
      lifnr,
      aedat,   -- last change date
      bedat    -- document date
}
    

AMDP untuk hitung aging buckets (SQLScript)


CLASS zcl_amdp_po_age DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_amdp_marker_hdb.
    CLASS-METHODS calc_po_aging
      IMPORTING iv_days1 TYPE i iv_days2 TYPE i
      RETURNING VALUE(rt_result) TYPE TABLE OF ty_po_aging.
ENDCLASS.

CLASS zcl_amdp_po_age IMPLEMENTATION.
  METHOD calc_po_aging BY DATABASE PROCEDURE FOR HDB
    LANGUAGE SQLSCRIPT
    OPTIONS READ-ONLY
    USING ekko ekpo.

    -- contoh sederhana: bucket 0-30,31-60,>60 berdasarkan bedat
    rt_result =
      SELECT bucket,
             COUNT(*) AS po_count,
             SUM(total_value) AS po_total
      FROM (
        SELECT e.ebeln,
               e.bedat,
               ( SELECT SUM( p.netwr ) FROM ekpo AS p WHERE p.ebeln = e.ebeln ) AS total_value,
               CASE
                 WHEN DATEDIFF( day, e.bedat, CURRENT_DATE ) <= :iv_days1 THEN '0-30'
                 WHEN DATEDIFF( day, e.bedat, CURRENT_DATE ) <= :iv_days2 THEN '31-60'
                 ELSE '>60'
               END AS bucket
        FROM ekko AS e
      ) AS sub
      GROUP BY bucket;

  ENDMETHOD.
ENDCLASS.
    

Catatan: fungsi tanggal & DATEDIFF tergantung versi SQLScript/HANA; sesuaikan dengan environment kalian.

9. Perbandingan Singkat CDS vs AMDP

AspekCDS ViewAMDP
Tujuan Data modelling, expose (OData, Fiori), semantik Prosedur/algoritma kompleks & perhitungan heavy di DB
Kemudahan Declarative, mudah dipakai & direuse Butuh knowledge SQLScript, lebih teknis
Performansi Sangat baik untuk join/aggregasi DB Sangat baik untuk logic procedural & ETL di DB
Integrasi Mudah expose OData, Fiori Bisa dipanggil dari ABAP / batch, kurang langsung expose OData

10. Best Practice

  • Start with CDS: pakai CDS untuk modelling & expose API ke UI.
  • Pakai AMDP hanya saat CDS tidak memadai (complex procedural logic, performance-critical).
  • Gunakan @AccessControl & authorization di CDS untuk security.
  • Tulis unit/integration tests untuk AMDP (test data di HANA).
  • Dokumentasikan dependency (USING clause) pada AMDP untuk maintainability.
  • Profiling: gunakan HANA PlanViz untuk optimisasi query/AMDP.

11. Troubleshooting & Tips

  • CDS not activated: activate CDS view di ADT/SE11 dan periksa error compiler.
  • OData not showing: check @OData.publish dan activate service di /IWFND/MAINT_SERVICE.
  • AMDP syntax error: edit body SQLScript di ADT, cek HANA SQLScript reference.
  • Performance issues: profile execution plan di HANA Studio / Eclipse DB explorer.

12. Kesimpulan

CDS & AMDP adalah dua alat penting di ABAP modern: CDS memberikan layer semantik & exposure untuk data, sementara AMDP menyediakan kekuatan SQLScript untuk logika yang lebih procedural atau compute-heavy. Kombinasikan keduanya sesuai kebutuhan — mulai dari modelling di CDS, dan turun ke AMDP bila perlu.

👉 Lanjut ke: Belajar ABAP Part 13: CDS Consumption View (UI Annotation) & Unit Testing AMDP


Catatan: contoh-contoh SQLScript/SQL function (DATEDIFF, CURRENT_DATE, dsb.) dapat berbeda antar HANA versi. Selalu cek dokumentasi HANA SQLScript di environment kalian.

Belajar ABAP Part 11: Smartforms

Belajar ABAP Part 11: Smartforms — Buat Form Cetak & Contoh Real Case (PO → PDF → Email)

Belajar ABAP Part 11: Smartforms — Buat Form Cetak & Contoh Real Case (PO → PDF → Email)

Ringkasan: Smartforms adalah tool SAP untuk membuat dokumen cetak (invoice, PO, delivery note) dengan UI desain tanpa banyak coding. Di artikel ini lo bakal belajar konsep Smartforms, komponennya, cara bind data, memanggil Smartform dari ABAP, convert OTF → PDF, dan contoh lengkap: mencetak Purchase Order (EKKO/EKPO) — simpan ke PDF & kirim via email (CL_BCS). Disertai best practice.

1. Apa itu Smartforms?

Smartforms adalah tool SAP yang menggantikan SAPscript untuk mendesain dokumen cetak. Keunggulan Smartforms:

  • Designer visual (pages, windows, nodes)
  • Mendukung table node untuk data berulang
  • Menghasilkan function module runtime otomatis
  • Mudah diekspor ke PDF/Spool

2. Komponen Utama Smartforms

Komponen penting yang harus diketahui:

  • Form Interface: deklarasi IMPORT PARAMETERS, TABLES, dan EXPORT.
  • Global Definitions: definisi struktur/variable global untuk form.
  • Pages & Windows: layout halaman (Main Window untuk body, Secondary Window untuk header/footer).
  • Nodes: Text Node, Table Node, Graphic Node.
  • Smart Styles: (SMARTSTYLE) untuk konsistensi font & style.

3. Alur Pembuatan Smartform (High level)

  1. Buka T-Code SMARTFORMS → Buat Smartform baru (mis. ZSMARTFORM_PO).
  2. Definisikan Form Interface (IMPORT params: PO number, TABLES: items table).
  3. Desain Pages → Window → letakkan Text Node (header) dan Table Node (items).
  4. Map field (binding): mis. di Text Node pakai &EKKO-EBELN&, di Table Node pakai &EKPO-MATNR&.
  5. Simpan & Activate form → generate function module runtime (gunakan SSF_FUNCTION_MODULE_NAME untuk dapatkan nama FM).
  6. Buat ABAP driver program untuk memanggil FM, convert OTF → PDF, dan/atau kirim email.
Note: Smartforms adalah dokumentasi/layout. Letakkan logic dan data retrieval di ABAP driver program; hindari coding kompleks di Smartform agar mudah dipelihara.

4. Contoh: Struktur Form Interface untuk PO

Misal kita definisikan interface Smartform ZSMARTFORM_PO seperti ini:


Import parameters:
  iv_ebeln      TYPE ekko-ebeln

Tables:
  it_ekpo       TYPE ekpo             " item table
  it_ekko       TYPE ekko OPTIONAL    " optional header (jika mau)
    

Di design, buat:

  • Header window: tampilkan data EKKO (EBELN, LIFNR, EKKO-BSART)
  • Main window / Table node: looping it_ekpo → tampilkan EKPO-EBELP, MATNR, MENGE, NETPR, nilai baris
  • Footer: subtotal, currency, signature

5. Binding Field di Smartform

Contoh dalam Text Node: &IT_EKKO-LIFNR& atau &IV_EBELN& (tergantung interface). Dalam Table Node gunakan field table: &IT_EKPO-MATNR&, &IT_EKPO-MENGE&, dst.

6. Memanggil Smartform dari ABAP — Driver Program (Contoh Lengkap)

Berikut contoh driver program lengkap untuk:

  1. Mengambil data PO header & items (EKKO/EKPO)
  2. Mengenerate Smartform menjadi OTF
  3. Mengonversi OTF → PDF (xstring)
  4. Menyimpan PDF ke folder aplikasi (opsional) dan mengirim email lampiran via CL_BCS

REPORT zsf_po_print_demo.

PARAMETERS: p_ebeln TYPE ekko-ebeln OBLIGATORY.

DATA: lv_formname   TYPE rs38l_fnam,
      lv_fm_name    TYPE rs38l_fnam,
      lt_ekpo       TYPE TABLE OF ekpo,
      ls_ekpo       TYPE ekpo,
      ls_ekko       TYPE ekko.

" 1. Ambil data header & items
SELECT SINGLE * FROM ekko INTO ls_ekko WHERE ebeln = p_ebeln.
IF sy-subrc <> 0.
  MESSAGE 'PO not found' TYPE 'E'.
  EXIT.
ENDIF.

SELECT * FROM ekpo INTO TABLE lt_ekpo WHERE ebeln = p_ebeln ORDER BY ebelp.

" 2. Dapatkan function module runtime Smartform
CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
  EXPORTING
    formname = 'ZSMARTFORM_PO'
  IMPORTING
    fm_name  = lv_fm_name
  EXCEPTIONS
    OTHERS   = 1.
IF sy-subrc <> 0.
  MESSAGE 'Smartform not found or error' TYPE 'E'.
  EXIT.
ENDIF.

" 3. Panggil function module Smartform untuk menghasilkan OTF data
DATA: ls_control    TYPE ssfctrlop,
      ls_output     TYPE ssfcompop,
      ls_job_output TYPE ssfcompop.

ls_control-no_dialog = 'X'. " supress dialog
ls_output-preview   = 'X'. " preview (opsional)

CALL FUNCTION lv_fm_name
  EXPORTING
    control_parameters = ls_control
    output_options     = ls_output
    iv_ebeln           = p_ebeln
  TABLES
    it_ekko            = VALUE #( ( ls_ekko ) )
    it_ekpo            = lt_ekpo
  IMPORTING
    job_output_info    = ls_job_output
  EXCEPTIONS
    OTHERS             = 1.

" 4. Ambil OTF dan convert ke PDF (xstring)
DATA: lt_otf    TYPE STANDARD TABLE OF itcoo, " tipe OTF (implementasi system bisa berbeda)
      lv_pdf    TYPE xstring.

" Banyak runtime FM mengembalikan OTF di job_output_info-otfdata
lt_otf = ls_job_output-otfdata. " pastikan struktur ini ada di sistem lo

CALL FUNCTION 'CONVERT_OTF'
  EXPORTING
    format  = 'PDF'
  IMPORTING
    bin_file = lv_pdf
  TABLES
    otf      = lt_otf
  EXCEPTIONS
    OTHERS   = 1.

IF sy-subrc <> 0.
  MESSAGE 'Error converting OTF to PDF' TYPE 'E'.
  EXIT.
ENDIF.

" 5. (Opsional) Simpan PDF ke application server / local file
" contoh menyimpan xstring ke server file (sapis)
DATA: lv_filename TYPE string VALUE '/tmp/PO_' && p_ebeln && '.pdf'.

CALL FUNCTION 'EPS_GET_DIRECTORY_LISTING' " (placeholder — actual FS write method depends on landscape)
  EXPORTING
    directory = '/tmp'
  TABLES
    filelist  = DATA(lt_dummy).

" Untuk menyimpan di application server gunakan OPEN DATASET / TRANSFER, atau untuk frontend gunakan GUI_DOWNLOAD.
" Contoh simpan ke frontend local PC:
CALL METHOD cl_gui_frontend_services=>gui_download
  EXPORTING
    filename = lv_filename
    filetype = 'BIN'
  CHANGING
    data_tab = DATA(lt_bin_tab)
  EXCEPTIONS
    OTHERS = 1.

" 6. Kirim PDF via email (CL_BCS) — menggunakan xstring
DATA: lo_send_request TYPE REF TO cl_bcs,
      lo_document     TYPE REF TO cl_document_bcs,
      lo_sender       TYPE REF TO if_recipient_bcs,
      lv_subject      TYPE so_obj_des VALUE |PO { p_ebeln }|,
      lv_recipient    TYPE adr6-smtp_addr VALUE 'receipient@example.com'.

" Buat send request
lo_send_request = cl_bcs=>create_persistent( ).

" Buat document (MIME) dari xstring PDF
lo_document = cl_document_bcs=>create_document(
                i_type    = 'PDF'
                i_text    = |Purchase Order { p_ebeln }|
                i_subject = lv_subject
                i_content = lv_pdf ).

" Tambah attachment (nama file)
cl_bcs=>set_document( lo_document ).
lo_send_request->set_document( lo_document ).

" Tambah recipient
lo_send_request->add_recipient( iv_address = lv_recipient ).

" Kirim
lo_send_request->send( i_with_error_screen = 'X' ).
COMMIT WORK.
    

Catatan penting tentang driver di atas:

  • Nama parameter FM yang dihasilkan oleh Smartform bisa berbeda. Selalu cek signature FM hasil generate (SE37) atau gunakan SSF_FUNCTION_MODULE_NAME untuk mendapatkan nama FM.
  • Beberapa runtime FM mengembalikan OTF di field berbeda; contoh di atas memakai job_output_info-otfdata. Jika struktur berbeda di sistem lo, cek output FM dan ambil tabel OTF yang benar.
  • Penyimpanan file dan download ke frontend tergantung landscape (application server vs frontend). Untuk user local gunakan CL_GUI_FRONTEND_SERVICES=>GUI_DOWNLOAD dengan table binari hasil convert.
  • Pengiriman email via CL_BCS memerlukan konfigurasi SAPconnect (SCOT) agar email bisa terkirim ke SMTP.

7. Export ke PDF & Pengiriman (Rangkuman teknis)

  1. Panggil FM Smartform (non-dialog) dengan control_parameters-no_dialog = 'X'.
  2. Ambil OTF output dari FM (job_output_info atau parameter otf).
  3. Gunakan CONVERT_OTF untuk menghasilkan xstring PDF (bin_file).
  4. Untuk kirim email, buat document BCS dengan content type 'PDF' dan lampirkan xstring.

8. Best Practice Smartforms

  • Pisahkan logic di ABAP driver program — Smartform fokus pada layout.
  • Gunakan SMARTSTYLE untuk konsistensi font/paragraph.
  • Hindari SELECT N+1: ambil semua data header & item sekaligus (bulk select).
  • Gunakan table node dengan header/footer subtotal bila perlu.
  • Uji printing di DEV/QA environment dengan berbagai data (short/long text, special chars, multi-currency).
  • Dokumentasikan setiap Smartform (purpose, owner, transport request).

9. Troubleshooting Umum

  • Blank page / missing fields: pastikan binding field sesuai nama parameter/tables yang dikirim dari ABAP.
  • Error FM signature: periksa FM runtime yang dihasilkan (SE37) untuk melihat parameter input/output.
  • OTF empty: periksa control_parameters & output_options saat panggil FM.
  • Email tidak terkirim: cek konfigurasi SCOT dan authorizations untuk user yang menjalankan pengiriman.

10. Contoh Real Case Tambahan — Menambahkan Logo & Bahasa Dinamis

- Logo: di Smartforms gunakan Graphic Node (upload image di SE78) lalu panggil di layout. - Bahasa Dinamis: kirim kode bahasa sebagai import parameter (iv_lang) lalu pakai kondisi/texte translation di Smartforms.

Tip: untuk dokumen resmi (faktur), selalu cek persyaratan format lokal (nomor faktur, detail PPN) dan konsultasi tim accounting sebelum deploy.

Kesimpulan

Smartforms adalah solusi teruji untuk membuat dokumen cetak di SAP. Workflow umumnya: desain Smartform → generate FM → panggil dari ABAP (driver) → konversi OTF→PDF → (opsional) kirim email. Fokuskan logika pada ABAP, gunakan Smartform untuk layout, dan selalu uji output di environment yang mirip production.

👉 Lanjut ke: Belajar ABAP Part 12: Enhancement & BADI (lanjutan) / Debugging Smartforms

Belajar ABAP Part 10: OOP Concepts in ABAP (Class, Inheritance, Polymorphism, Exception, Design Pattern)

Belajar ABAP Part 10: OOP Concepts in ABAP (Class, Inheritance, Polymorphism, Exception, Design Pattern)

Belajar ABAP Part 10: OOP Concepts in ABAP

Ringkasan: Pada bagian ini kita belajar Object-Oriented Programming (OOP) di ABAP: mulai dari class & object, visibility (PUBLIC, PROTECTED, PRIVATE), constructor, inheritance, polymorphism, hingga exception class. Disertai pula contoh design pattern populer seperti Factory dan Strategy yang sering dipakai dalam development SAP ABAP modern.

1. Pengenalan OOP di ABAP

ABAP mendukung OOP sejak SAP Basis 4.6C. Dengan paradigma ini, developer dapat membuat kode yang lebih terstruktur, reusable, dan maintainable. Konsep utama OOP di ABAP meliputi encapsulation, inheritance, polymorphism, dan abstraction.

2. Class & Object

Class adalah blueprint, sedangkan object adalah instance dari class tersebut. Public method dapat dipanggil dari luar, sementara private hanya bisa diakses dari dalam class.


CLASS zcl_hello DEFINITION.
  PUBLIC SECTION.
    METHODS: say_hello.
ENDCLASS.

CLASS zcl_hello IMPLEMENTATION.
  METHOD say_hello.
    WRITE: / 'Hello ABAP OOP!'.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(lo_hello) = NEW zcl_hello( ).
  lo_hello->say_hello( ).
  

3. Inheritance

Inheritance memungkinkan sebuah class untuk mewarisi properti dan method dari class lain. Jika method di class parent tidak sesuai, kita bisa melakukan REDEFINITION di child class.


CLASS zcl_animal DEFINITION.
  PUBLIC SECTION.
    METHODS: speak.
ENDCLASS.

CLASS zcl_animal IMPLEMENTATION.
  METHOD speak.
    WRITE: / 'Animal sound'.
  ENDMETHOD.
ENDCLASS.

CLASS zcl_dog DEFINITION INHERITING FROM zcl_animal.
  PUBLIC SECTION.
    METHODS: speak REDEFINITION.
ENDCLASS.

CLASS zcl_dog IMPLEMENTATION.
  METHOD speak.
    WRITE: / 'Woof! Woof!'.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(lo_pet) = NEW zcl_dog( ).
  lo_pet->speak( ).
  

4. Polymorphism

Polymorphism memungkinkan object yang berbeda memberikan respon berbeda terhadap method yang sama. Dengan cara ini, kode lebih fleksibel tanpa perlu banyak IF/CASE.


CLASS zcl_cat DEFINITION INHERITING FROM zcl_animal.
  PUBLIC SECTION.
    METHODS: speak REDEFINITION.
ENDCLASS.

CLASS zcl_cat IMPLEMENTATION.
  METHOD speak.
    WRITE: / 'Meow!'.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(lo_animal) TYPE REF TO zcl_animal.
  lo_animal = NEW zcl_dog( ).
  lo_animal->speak( ). "Output: Woof!

  lo_animal = NEW zcl_cat( ).
  lo_animal->speak( ). "Output: Meow!
  

5. Exception Class

Untuk menangani error, ABAP menyediakan exception class. Dengan cara ini, error dapat ditangani lebih terstruktur dibandingkan sekadar IF condition.


CLASS zcx_invalid_input DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.

CLASS zcl_calc DEFINITION.
  PUBLIC SECTION.
    METHODS divide IMPORTING iv_a TYPE i iv_b TYPE i
                   RETURNING VALUE(rv_res) TYPE f
                   RAISING zcx_invalid_input.
ENDCLASS.

CLASS zcl_calc IMPLEMENTATION.
  METHOD divide.
    IF iv_b = 0.
      RAISE EXCEPTION TYPE zcx_invalid_input.
    ENDIF.
    rv_res = iv_a / iv_b.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  TRY.
      DATA(lo_calc) = NEW zcl_calc( ).
      WRITE: / lo_calc->divide( 10, 0 ).
    CATCH zcx_invalid_input.
      WRITE: / 'Error: Division by zero!'.
  ENDTRY.
  

6. Real Case: Sales Order Object

Implementasi nyata: membuat object untuk Sales Order. Data disimpan dalam atribut private dan hanya diakses via method.


CLASS zcl_sales_order DEFINITION.
  PUBLIC SECTION.
    METHODS: constructor IMPORTING iv_so TYPE vbeln,
             display,
             get_total RETURNING VALUE(rv_total) TYPE netwr.
  PRIVATE SECTION.
    DATA: gv_so TYPE vbeln,
          gv_total TYPE netwr.
ENDCLASS.

CLASS zcl_sales_order IMPLEMENTATION.
  METHOD constructor.
    gv_so = iv_so.
    " Simulasi fetch SO dari DB
    gv_total = 10000.
  ENDMETHOD.

  METHOD display.
    WRITE: / 'SO:', gv_so, 'Total:', gv_total.
  ENDMETHOD.

  METHOD get_total.
    rv_total = gv_total.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(lo_so) = NEW zcl_sales_order( '4500001234' ).
  lo_so->display( ).
  

7. Design Pattern: Factory

Factory Pattern memisahkan logika pembuatan object. Caller cukup meminta object berdasarkan tipe, tanpa tahu implementasi detailnya.


INTERFACE if_payment_method.
  METHODS pay IMPORTING iv_amount TYPE i.
ENDINTERFACE.

CLASS zcl_payment_cash DEFINITION.
  PUBLIC SECTION.
    INTERFACES if_payment_method.
ENDCLASS.

CLASS zcl_payment_cash IMPLEMENTATION.
  METHOD if_payment_method~pay.
    WRITE: / 'Paid in CASH:', iv_amount.
  ENDMETHOD.
ENDCLASS.

CLASS zcl_payment_card DEFINITION.
  PUBLIC SECTION.
    INTERFACES if_payment_method.
ENDCLASS.

CLASS zcl_payment_card IMPLEMENTATION.
  METHOD if_payment_method~pay.
    WRITE: / 'Paid with CARD:', iv_amount.
  ENDMETHOD.
ENDCLASS.

CLASS zcl_payment_factory DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS get_payment
      IMPORTING iv_type TYPE string
      RETURNING VALUE(ro_pay) TYPE REF TO if_payment_method.
ENDCLASS.

CLASS zcl_payment_factory IMPLEMENTATION.
  METHOD get_payment.
    CASE iv_type.
      WHEN 'CASH'. CREATE OBJECT ro_pay TYPE zcl_payment_cash.
      WHEN 'CARD'. CREATE OBJECT ro_pay TYPE zcl_payment_card.
      WHEN OTHERS. ro_pay = NULL.
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(lo_pay) = zcl_payment_factory=>get_payment( 'CARD' ).
  IF lo_pay IS BOUND.
    lo_pay->pay( 500000 ).
  ENDIF.
  

8. Design Pattern: Strategy

Strategy Pattern memungkinkan algoritma dipilih secara fleksibel di runtime. Contoh: perhitungan diskon bisa berbeda untuk pelanggan biasa vs VIP.


INTERFACE if_discount_strategy.
  METHODS calc_discount IMPORTING iv_amount TYPE p DECIMALS 2
                        RETURNING VALUE(rv_disc) TYPE p DECIMALS 2.
ENDINTERFACE.

CLASS zcl_disc_normal DEFINITION.
  PUBLIC SECTION.
    INTERFACES if_discount_strategy.
ENDCLASS.

CLASS zcl_disc_normal IMPLEMENTATION.
  METHOD if_discount_strategy~calc_discount.
    rv_disc = iv_amount * 0.05.
  ENDMETHOD.
ENDCLASS.

CLASS zcl_disc_vip DEFINITION.
  PUBLIC SECTION.
    INTERFACES if_discount_strategy.
ENDCLASS.

CLASS zcl_disc_vip IMPLEMENTATION.
  METHOD if_discount_strategy~calc_discount.
    rv_disc = iv_amount * 0.15.
  ENDMETHOD.
ENDCLASS.

CLASS zcl_discount_context DEFINITION.
  PUBLIC SECTION.
    METHODS: constructor IMPORTING io_strat TYPE REF TO if_discount_strategy,
             get_discount IMPORTING iv_amount TYPE p DECIMALS 2
                          RETURNING VALUE(rv_disc) TYPE p DECIMALS 2.
  PRIVATE SECTION.
    DATA: mo_strategy TYPE REF TO if_discount_strategy.
ENDCLASS.

CLASS zcl_discount_context IMPLEMENTATION.
  METHOD constructor.
    mo_strategy = io_strat.
  ENDMETHOD.

  METHOD get_discount.
    rv_disc = mo_strategy->calc_discount( iv_amount ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(lo_vip) = NEW zcl_discount_context( NEW zcl_disc_vip( ) ).
  WRITE: / 'VIP disc for 1,000,000 =', lo_vip->get_discount( 1000000 ).

  DATA(lo_norm) = NEW zcl_discount_context( NEW zcl_disc_normal( ) ).
  WRITE: / 'Normal disc for 1,000,000 =', lo_norm->get_discount( 1000000 ).
  

9. Kesimpulan

OOP di ABAP memberi struktur kuat untuk development modern. Dengan class, inheritance, polymorphism, dan exception handling, program menjadi lebih terstruktur. Ditambah lagi dengan design pattern seperti Factory dan Strategy, kita bisa membangun sistem yang lebih fleksibel, scalable, dan maintainable.

Belajar ABAP Part 9: ALV (ABAP List Viewer) — Tutorial dan Contoh Lengkap

Belajar ABAP Part 9: ALV (ABAP List Viewer) — Tutorial & Contoh Lengkap

Belajar ABAP Part 9: ALV (ABAP List Viewer)

ALV (ABAP List Viewer) adalah toolkit standar SAP untuk menampilkan data tabel secara rapi dan interaktif — lengkap dengan fitur sorting, filtering, layout, export, dan event handling. Di artikel ini kita bahas tipe ALV yang sering dipakai, contoh kode, cara menambahkan tombol custom, field catalog manual, dan demo embed-ready.

Field Catalog di ALV

Field catalog berfungsi sebagai “peta” kolom ALV: menentukan label, panjang kolom, alignment, apakah kolom bisa disortir, dll. Field catalog ini wajib di REUSE_ALV_GRID_DISPLAY dan CL_GUI_ALV_GRID, sedangkan CL_SALV_TABLE otomatis membangun dari struktur.

Contoh Lengkap Field Catalog Manual


REPORT z_alv_fieldcat.

TABLES: mara.

DATA: gt_fieldcat TYPE lvc_t_fcat,
      gs_fieldcat TYPE lvc_s_fcat,
      gt_data     TYPE TABLE OF mara.

START-OF-SELECTION.
  SELECT * FROM mara INTO TABLE gt_data UP TO 20 ROWS.

  CLEAR gs_fieldcat.
  gs_fieldcat-fieldname = 'MATNR'.
  gs_fieldcat-coltext   = 'Material'.
  gs_fieldcat-outputlen = 18.
  gs_fieldcat-key       = abap_true.
  APPEND gs_fieldcat TO gt_fieldcat.

  CLEAR gs_fieldcat.
  gs_fieldcat-fieldname = 'MTART'.
  gs_fieldcat-coltext   = 'Material Type'.
  gs_fieldcat-outputlen = 10.
  APPEND gs_fieldcat TO gt_fieldcat.

  CLEAR gs_fieldcat.
  gs_fieldcat-fieldname = 'MATKL'.
  gs_fieldcat-coltext   = 'Material Group'.
  gs_fieldcat-outputlen = 12.
  APPEND gs_fieldcat TO gt_fieldcat.

  " ALV grid display dengan field catalog manual
  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
    EXPORTING
      it_fieldcat = gt_fieldcat
    TABLES
      t_outtab    = gt_data.
    

Demo ALV Embed-Ready

Supaya pembaca bisa coba step by step, berikut demo ALV ditampilkan dengan kombinasi <details> di Blogspot. Klik untuk expand:

🔽 Demo ALV dengan CL_SALV_TABLE (Klik untuk expand)

Preview: Program ini menampilkan data MARA ke ALV dengan CL_SALV_TABLE.


REPORT z_demo_alv_salv.

DATA: lt_data TYPE TABLE OF mara,
      lo_alv  TYPE REF TO cl_salv_table.

START-OF-SELECTION.
  SELECT * FROM mara INTO TABLE lt_data UP TO 30 ROWS.

  cl_salv_table=>factory(
    IMPORTING r_salv_table = lo_alv
    CHANGING  t_table      = lt_data ).

  lo_alv->display( ).
      
🔽 Demo ALV dengan Field Catalog + REUSE_ALV_GRID_DISPLAY

Preview: Program ini menampilkan MARA dengan custom kolom (MATNR, MTART, MATKL).


REPORT z_demo_alv_fcat.

TYPES: BEGIN OF ty_data,
         matnr TYPE mara-matnr,
         mtart TYPE mara-mtart,
         matkl TYPE mara-matkl,
       END OF ty_data.

DATA: gt_data TYPE TABLE OF ty_data,
      gt_fieldcat TYPE lvc_t_fcat,
      gs_fieldcat TYPE lvc_s_fcat.

START-OF-SELECTION.
  SELECT matnr mtart matkl
    FROM mara
    INTO TABLE gt_data
    UP TO 50 ROWS.

  CLEAR gs_fieldcat.
  gs_fieldcat-fieldname = 'MATNR'.
  gs_fieldcat-coltext   = 'Material'.
  APPEND gs_fieldcat TO gt_fieldcat.

  CLEAR gs_fieldcat.
  gs_fieldcat-fieldname = 'MTART'.
  gs_fieldcat-coltext   = 'Material Type'.
  APPEND gs_fieldcat TO gt_fieldcat.

  CLEAR gs_fieldcat.
  gs_fieldcat-fieldname = 'MATKL'.
  gs_fieldcat-coltext   = 'Material Group'.
  APPEND gs_fieldcat TO gt_fieldcat.

  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
    EXPORTING
      it_fieldcat = gt_fieldcat
    TABLES
      t_outtab    = gt_data.
      

Kesimpulan

Dengan Field Catalog, kita bisa kontrol penuh tampilan ALV. Sementara itu, demo embed-ready memudahkan pembaca coba langsung script pendek. Untuk program baru, gunakan CL_SALV_TABLE bila kebutuhan sederhana; gunakan Field Catalog + ALV Grid bila butuh customisasi lanjut.

👉 Lanjut ke: Belajar ABAP Part 10: OOP di ABAP — Class, Object, Inheritance, Exception

Belajar ABAP Part 8: Enhancement & BADI

Belajar ABAP Part 8: Enhancement & BADI — Customisasi Tanpa Modify SAP

Belajar ABAP Part 8: Enhancement & BADI — Customisasi Tanpa Modify SAP

Enhancement & BADI adalah cara menambahkan logic/custom behavior tanpa mengubah source code SAP standar. Hal ini sangat penting agar sistem tetap upgrade safe.

Apa itu Enhancement?

Enhancement memungkinkan menancapkan kode tambahan pada titik tertentu di program SAP.

  • User Exit — function modules siap pakai.
  • Customer Exit — menambah field/logic via CMOD.
  • Implicit Enhancement — otomatis tersedia di awal/akhir program.
  • Explicit Enhancement — titik yang ditentukan oleh SAP developer.

Apa itu BADI?

BADI adalah framework enhancement berbasis OOP. Bisa multiple implementation, scalable, cocok di S/4HANA.

Contoh Implicit Enhancement


ENHANCEMENT 1 ZENH_MIGO_LOG.  
  WRITE: / 'MIGO Posting date updated by user:', sy-uname.
ENDENHANCEMENT.
    

Contoh Implementasi BADI


METHOD if_ex_me_process_po_cust~process_item.

  DATA: ls_item TYPE mepoitem.

  CALL METHOD im_item->get_data
    RECEIVING re_data = ls_item.

  IF ls_item-matnr IS INITIAL.
    MESSAGE e001(zmsg) WITH 'Material must be filled'.
  ENDIF.

ENDMETHOD.
    

Step by Step Implementasi BADI

  1. Buka transaksi SE18 untuk melihat definisi BADI.
  2. Pelajari interface & methods yang tersedia.
  3. Buat implementasi baru di SE19.
  4. Isi logic di method yang relevan sesuai kebutuhan bisnis.
  5. Aktifkan & transport agar bisa digunakan di sistem lain.

Real Case: Validasi Nilai PO Minimal


METHOD if_ex_me_process_po_cust~process_header.

  DATA: ls_header TYPE mepoheader,
        lt_items  TYPE mepoitem_tab,
        lv_total  TYPE ekpo-netwr.

  CALL METHOD im_header->get_data RECEIVING re_data = ls_header.
  CALL METHOD im_header->get_items RECEIVING re_items = lt_items.

  LOOP AT lt_items INTO DATA(ls_item).
    lv_total = lv_total + ls_item-netwr.
  ENDLOOP.

  IF lv_total < 1000000.
    MESSAGE e001(zmsg) WITH 'PO value must be at least 1,000,000 IDR'.
  ENDIF.

ENDMETHOD.
    

ABAP Unit Test untuk BADI


CLASS ltc_badi_me_process_po_cust DEFINITION FINAL FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.

  PRIVATE SECTION.
    DATA: mo_badi TYPE REF TO zcl_impl_me_process_po.

    METHODS: setup.
    METHODS: test_valid_po FOR TESTING.
    METHODS: test_invalid_po FOR TESTING.
ENDCLASS.

CLASS ltc_badi_me_process_po_cust IMPLEMENTATION.

  METHOD setup.
    CREATE OBJECT mo_badi.
  ENDMETHOD.

  METHOD test_valid_po.
    " PO 1.500.000 harus lolos
    DATA: lt_items TYPE mepoitem_tab,
          ls_item TYPE mepoitem,
          ls_header TYPE mepoheader.

    ls_item-netwr = 1500000.
    APPEND ls_item TO lt_items.

    TRY.
        mo_badi->if_ex_me_process_po_cust~process_header(
          im_header = NEW cl_po_header( ls_header )
        ).
        cl_abap_unit_assert=>assert_true( act = abap_true ).
      CATCH cx_root INTO DATA(lx).
        cl_abap_unit_assert=>fail( msg = lx->get_text( ) ).
    ENDTRY.
  ENDMETHOD.

  METHOD test_invalid_po.
    " PO 500.000 harus gagal
    DATA: lt_items TYPE mepoitem_tab,
          ls_item TYPE mepoitem,
          ls_header TYPE mepoheader.

    ls_item-netwr = 500000.
    APPEND ls_item TO lt_items.

    TRY.
        mo_badi->if_ex_me_process_po_cust~process_header(
          im_header = NEW cl_po_header( ls_header )
        ).
        cl_abap_unit_assert=>fail( msg = 'Invalid PO should not pass' ).
      CATCH cx_sy_message_illegal_text INTO DATA(lx).
        cl_abap_unit_assert=>assert_true( act = abap_true ).
    ENDTRY.
  ENDMETHOD.

ENDCLASS.
    

Kesimpulan

- Enhancement & BADI memungkinkan customisasi tanpa modify SAP standard.
- Gunakan BADI untuk sistem modern (S/4HANA).
- Tambahkan unit test agar logic bisa diuji otomatis.
- Dokumentasikan & gunakan transport request agar terkontrol.

👉 Lanjut ke: Belajar ABAP Part 9: ALV (ABAP List Viewer)

Belajar ABAP: Data Dictionary (DDIC) untuk Pemula

Belajar ABAP: Data Dictionary (DDIC) untuk Pemula

Belajar ABAP: Data Dictionary (DDIC) untuk Pemula

Sebelum melanjutkan ke level advance, penting bagi pemula memahami Data Dictionary (DDIC) di SAP ABAP. Data Dictionary adalah tempat di mana semua definisi struktur data disimpan dan dikelola, sehingga developer tidak perlu mendefinisikan ulang data secara manual di program.

Apa itu Data Dictionary?

Data Dictionary di ABAP berfungsi untuk mendefinisikan, mengelola, dan memelihara objek data di SAP. Semua tabel database, field, data element, domain, view, dan search help dikelola di sini.

Fungsi Utama Data Dictionary

  • Definisi tabel database (transparent, cluster, pooled).
  • Mendefinisikan domain (tipe data dan nilai valid).
  • Mendefinisikan data element (field dengan semantic meaning).
  • Membuat view untuk kombinasi tabel.
  • Membuat search help (F4 Help).
  • Memastikan konsistensi data di seluruh sistem.

Jenis Objek di Data Dictionary

  1. Domain → menentukan tipe data teknis (char, num, length, value range).
  2. Data Element → field dengan definisi semantik (misalnya Material Number, Customer ID).
  3. Table → menyimpan data di database (transparent, pooled, cluster).
  4. View → representasi virtual dari satu atau lebih tabel.
  5. Search Help → menyediakan fasilitas pencarian (F4 help).

Hubungan Domain, Data Element, dan Table

Supaya lebih jelas, berikut ilustrasi alur Data Dictionary di ABAP:

+---------+        +--------------+        +------------+
| Domain  | -----> | Data Element | -----> |   Table    |
+---------+        +--------------+        +------------+

Contoh:
Domain: ZSTU_ID (NUMC 5)
   ↓
Data Element: ZSTU_ID (Student ID)
   ↓
Table: ZSTUDENT (Field: ID menggunakan Data Element ZSTU_ID)

Atau bisa juga digambarkan dalam bentuk tabel hierarki:

Domain Data Element Table Field
ZSTU_ID
(NUMC 5)
ZSTU_ID
(Student ID)
ID (Primary Key)
ZSTU_NAME
(CHAR 50)
ZSTU_NAME
(Student Name)
NAME
ZSTU_AGE
(NUMC 2)
ZSTU_AGE
(Age)
AGE
ZSTU_CLASS
(CHAR 10)
ZSTU_CLASS
(Class Code)
CLASS

Contoh Membuat Tabel di Data Dictionary

Misalnya kita ingin membuat tabel ZSTUDENT untuk menyimpan data mahasiswa:


Tabel: ZSTUDENT
-----------------------------------------
Field        Data Element   Domain   Key
-----------------------------------------
ID           ZSTU_ID        NUMC(5)  X
NAME         ZSTU_NAME      CHAR(50)
AGE          ZSTU_AGE       NUMC(2)
CLASS        ZSTU_CLASS     CHAR(10)

Contoh Query ABAP dengan Tabel ZSTUDENT


REPORT zddic_example.

DATA: lt_student TYPE TABLE OF zstudent,
      ls_student TYPE zstudent.

" Ambil semua data mahasiswa
SELECT * FROM zstudent INTO TABLE lt_student.

" Tampilkan hasil
LOOP AT lt_student INTO ls_student.
  WRITE: / ls_student-id, ls_student-name, ls_student-age, ls_student-class.
ENDLOOP.

Perbandingan Tipe Table di Data Dictionary

Di SAP ABAP, ada tiga jenis tabel utama di Data Dictionary. Berikut perbandingannya:

Jenis Tabel Deskripsi Contoh Kapan Digunakan
Transparent Table 1:1 dengan tabel database fisik. Nama tabel di SAP sama dengan tabel di database. Digunakan untuk menyimpan data aplikasi. ZSTUDENT, MARA, BKPF - Data master (Material, Vendor, Customer).
- Data transaksi (Document Header, Item).
Pooled Table Banyak tabel logis di SAP digabung ke dalam satu tabel fisik di database. Cocok untuk tabel kecil dengan data administratif. ATAB, TSTC - Data konfigurasi atau kontrol.
- Tabel internal SAP yang tidak besar.
Cluster Table Beberapa tabel logis disimpan dalam satu tabel fisik berbasis cluster. Data disimpan dalam bentuk compressed. BSEG (Accounting Document Segment) - Data dengan volume besar.
- Data dengan hubungan kompleks (misalnya FI/CO).

Note: Di SAP versi terbaru (S/4HANA), Pooled dan Cluster table sudah tidak direkomendasikan lagi. Mayoritas objek disimpan sebagai Transparent Table agar lebih efisien dan kompatibel dengan HANA Database.

Keuntungan Menggunakan Data Dictionary

  • Semua developer pakai definisi data yang sama → konsistensi.
  • Mengurangi redudansi kode.
  • Integrasi otomatis dengan Open SQL.
  • Dapat digunakan di semua program ABAP.

Kesimpulan

Data Dictionary adalah pondasi utama di SAP ABAP. Tanpa pemahaman DDIC, developer akan kesulitan membuat tabel, field, maupun struktur data yang reusable di berbagai program.

👉 Lanjut ke: Belajar ABAP Part 8: Enhancement & BADI

Belajar SAP ABAP RAP

  Belajar SAP ABAP RAP: Pengenalan dan Konsep Dasar Restful ABAP Programming Model Kalau kamu seorang ABAPer yang mulai terjun ke dunia SAP...