Tuesday, 23 September 2025

Belajar ABAP Part 14: Performance Tuning & HANA Optimization

Belajar ABAP Part 14: Performance Tuning & HANA Optimization — Tips, Tools, dan Contoh

Belajar ABAP Part 14: Performance Tuning & HANA Optimization

Ringkasan: Fokus part ini: teknik tuning ABAP + HANA untuk aplikasi S/4HANA. Intinya: push logic ke DB (code pushdown) bila relevan, hindari loop di ABAP untuk operasi data besar, gunakan CDS/AMDP dengan bijak, dan pakai alat profiling (PlanViz, ST05, SAT) untuk menemukan bottleneck. Disertai contoh before/after dan checklist tuning.

1. Prinsip Utama Tuning

  • Measure first: jangan guesstimate — profil untuk lihat hotspot.
  • Pushdown where possible: gunakan CDS/AMDP agar DB melakukan kerja berat.
  • Avoid row-by-row: minimalisasi nested loops yang baca DB di tiap iterasi.
  • Right data, right place: pilih column-store, partitioning, atau caching sesuai pola akses.

2. Tools untuk Diagnosis

ToolFungsiKapan pakai
ST05 (SQL Trace)Melacak SQL yang dijalankan oleh ABAPCari query lambat / banyak eksekusi
SAT / SE30Runtime analysis (time breakdown)Profiling performa prosedural ABAP
ST12Gabungan trace SQL+ABAP untuk root-causeInvestigasi mendalam
PlanViz (HANA)Analisa execution plan HANAOptimasi query/AMDP/SQLScript
HANA Studio / Eclipse (HANA tools)Profiling, index, partition, CDS activationDB-side tuning
ST10 / DBACOCKPIT (ST04)Analisa buffer & statistik DBOps DB/DBA checks

3. ABAP-level Best Practices

3.1 Open SQL yang efisien

  • Pakai field list explicit — SELECT matnr, mtart FROM mara bukan SELECT *.
  • Gunakan UP TO n ROWS untuk preview atau sampling.
  • Tambahkan kondisi WHERE yang mengurangi rows sebanyak mungkin (filter by key/partitions)
  • Gunakan INTO TABLE ketimbang loop + SELECT satu per satu.

3.2 FOR ALL ENTRIES vs JOIN

FOR ALL ENTRIES berguna tapi sering disalahpakai. Prinsip:

  • Gunakan hanya jika internal table tidak kosong.
  • Jangan pakai jika internal table sangat besar (ribuan baris) — lebih baik create temporary table in DB (CDS/AMDP) atau gunakan joins.


" risky: many entries => big IN-list
SELECT * FROM ekpo INTO TABLE lt_items
  FOR ALL ENTRIES IN lt_po
  WHERE ebeln = lt_po-ebeln.
    

Jika lt_po besar, buat CDS/AMDP atau insert ke Z-temp table dan JOIN di DB.

3.3 Buffering & Caching

  • Gunakan table buffering untuk small master tables (SE11 → Technical Settings → Buffering).
  • Gunakan ABAP application buffers (SET/GET) bila data jarang berubah dan dibaca sering.
  • Hati-hati: cache stale issues — set TTL/invalidasi sesuai kebutuhan.

3.4 Reduce Network Round-trips

Ambil data dalam bulk (SELECT ... INTO TABLE) daripada banyak CALL FUNCTION/remote roundtrip.

4. CDS / AMDP / Pushdown Best Practices

4.1 Gunakan CDS untuk:

  • Joins/associations dan basic aggregasi
  • Expose OData / Fiori (consumption view + annotations)
  • Parameterize queries (consumption view parameters untuk pruning)

4.2 Gunakan AMDP ketika:

  • Logika procedural/iteratif yang sulit di-express di CDS
  • Transformasi/ETL/komputasi berat di DB
  • Anda perlu kontrol SQLScript (CE functions, window functions, temporary tables)
Note: selalu cek PlanViz untuk melihat apakah CDS/AMDP benar-benar melakukan pushdown dan tidak memaksa row-by-row transfer ke ABAP layer.

5. HANA-specific Optimization

5.1 Column Store vs Row Store

HANA column-store unggul untuk scanning, aggregation, compression. Pastikan tabel besar berada di column store (default di HANA) — hindari row-store untuk analytic workloads.

5.2 Compression & Dictionary Encoding

Column-store compresses data; gunakan low-cardinality columns as dictionary keys. Compression mengurangi I/O & memory footprint.

5.3 Partitioning

Partitioning memudahkan pruning: range partition by date, hash partition by company, dll. Partitioning membantu query hanya scan partition relevan.

5.4 Use CE Functions & Window Functions

HANA menyediakan Calculation Engine (CE) functions dan window functions (ROW_NUMBER, RANK) yang sering jauh lebih cepat dibandingkan SQL naif. Gunakan SQLScript/AMDP untuk memanfaatkan fungsi ini.

5.5 Avoid UDFs / Scalar UDFs where possible

Scalar UDFs can cause performance penalties (row-context). Prefer built-in CE functions or inline expressions.

6. Profiling HANA Queries (PlanViz)

  1. Ambil SQL dari ST05 atau gunakan CDS explain plan.
  2. Load SQL ke PlanViz (HANA Studio / Eclipse) — lihat execution steps: CE joins, table scan, column access.
  3. Perhatikan expensive operators: CE_MULTI_JOIN, CE_JOIN, CE_AGGREGATE, CE_CALCULATION.
  4. Optimisasi: add predicate pushdown, change join order, add partition pruning filters.

7. Before / After Example: Replace Loop + Select with Single SQL (ABAP)

Before (slow — many DB roundtrips):


LOOP AT lt_po INTO ls_po.
  SELECT * FROM ekpo INTO TABLE lt_items WHERE ebeln = ls_po-ebeln.
  " process lt_items
ENDLOOP.
    

After (fast — single DB call using JOIN or FOR ALL ENTRIES carefully):


" Option A: single join (preferred)
SELECT e~ebeln, e~ebelp, e~matnr, h~lifnr
  FROM ekpo AS e
  INNER JOIN ekko AS h ON e~ebeln = h~ebeln
  INTO TABLE lt_result
  FOR ALL ENTRIES IN lt_po
  WHERE e~ebeln = lt_po-ebeln.

" Option B: single select with IN-list (if lt_po small)
SELECT * FROM ekpo INTO TABLE lt_items WHERE ebeln IN @lt_po_ebeln.
    
Tip: prefer JOIN or CDS view that returns already-joined data. If using FOR ALL ENTRIES, ensure lt_po is not initial and de-duplicated.

8. Example: CDS Pushdown vs ABAP Aggregation

Prefer CDS aggregation:


-- CDS
@AbapCatalog.sqlViewName: 'ZV_SALES_SUM'
define view ZCDS_SALES_SUM as select from vbak {
  key kunnr,
  sum( netwr ) as total_sales
}
group by kunnr;
    

Instead of pulling rows then aggregate in ABAP (slow).

9. Index & Statistics

  • HANA tidak bergantung heavy pada secondary indexes seperti DB row-stores — but still check if specific secondary indexes help for point queries.
  • Pastikan table statistics up-to-date (DBA job) agar optimizer bisa memilih plan terbaik.

10. Locking & Concurrency

- Minimalkan long-running transactions. - Untuk batch jobs, gunakan smaller commit intervals (COMMIT WORK) tapi hati-hati dengan partial state. - Pelajari isolation levels jika reading while writing heavy workloads (consistent snapshot behavior in HANA).

11. Operational Checks (Checklist cepat)

  1. Profiling: jalankan ST05 / SAT untuk mendeteksi hotspot.
  2. Ambil SQL & buka PlanViz — cek apakah pushdown terjadi.
  3. Pastikan CDS/AMDP menggunakan parameters untuk pruning.
  4. Ganti nested loops yang men-trigger DB call dengan single SELECT / JOIN / CDS.
  5. Gunakan AMDP untuk heavy aggregations / CE functions.
  6. Periksa partitioning, compression, dan column-store placement.
  7. Perbarui statistik DB; cek HANA alerts/caches.
  8. Uji di environment yang mirip production (volume testing).

12. Common Anti-Patterns

  • SELECT * di aplikasi produksi.
  • Loop { SELECT ... } — row-by-row DB call (N+1 problem).
  • FOR ALL ENTRIES dengan tabel kosong (causes full scan) atau dengan very large table.
  • Scalar UDFs that execute per-row on DB side.
  • Long-running transactions holding locks during heavy writes + reads.

13. Quick Reference: Commands & Tools

  • ST05 — start/stop SQL trace, analyze slow statements
  • SAT / SE30 — runtime analysis for ABAP
  • ST12 — combined ABAP + SQL trace
  • PlanViz — analyze HANA execution plans
  • /HANA Studio / Eclipse — HANA trace, index, partition management
  • DBACOCKPIT / ST04 — DB level stats and space usage

14. Contoh Kasus Nyata (Before / After) — PO Aging

Masalah: report PO aging awalnya membaca semua EKPO & menghitung di ABAP → sangat lambat.

Before (ABAP aggregation):


SELECT * FROM ekko INTO TABLE lt_ekko WHERE bukrs = '1000'.

LOOP AT lt_ekko INTO ls_ekko.
  SELECT SUM( netwr ) INTO lv_sum FROM ekpo WHERE ebeln = ls_ekko-ebeln.
  " calculate aging in ABAP
ENDLOOP.
    

After (AMDP pushdown): gunakan AMDP/SQLScript to compute sums and buckets in DB then return small result set:


" AMDP returns aggregated buckets (small table)
DATA(lt_buckets) = zcl_amdp_po_age=>calc_po_aging( iv_days1 = 30 iv_days2 = 60 ).
    

Hasil: transfer data jauh lebih sedikit, CPU kerja HANA optimized for aggregations, report 10x+ faster tergantung volume.

15. Penutup & Praktik Rutin

Performance tuning adalah siklus: measure → optimize → verify → monitor. Buat baseline sebelum optimasi, dokumentasikan perubahan, dan lakukan regresi/performa test setelah deploy. Kolaborasi tim dev + DBA + BASIS + infra sangat penting buat hasil optimal.

👉 Lanjut ke: Belajar ABAP Part 15: Performance Tuning Lanjutan


Catatan: contoh-contoh di sini disederhanakan supaya mudah paham. Untuk optimasi production, selalu lakukan load testing di environment yang mirip production dan libatkan DBA HANA untuk rekomendasi level DB.

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)

SAP ABAP - Upload Count PID dengan BAPI

Upload Count PID dengan BAPI di ABAP — Contoh Program YMMC100 Upload Count PID dengan BAPI di ABAP — Progr...