ABAP Currency & Unit Conversion: Währung und Mengeneinheiten

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Currency & Unit Conversion in ABAP erfordert besondere Beachtung von Dezimalstellen, Wechselkursen und Umrechnungsfaktoren. Die korrekte Handhabung ist entscheidend für Finanz- und Logistikanwendungen.

Grundkonzept

ThemaBeschreibung
WährungDezimalstellen abhängig von Währung (EUR: 2, JPY: 0)
WechselkursKurse aus Tabelle TCURR
MengeneinheitUmrechnungsfaktoren in T006
Intern vs. ExternUnterschiedliche Darstellung

Beispiele

1. Währungsbetrag konvertieren

DATA: lv_amount_eur TYPE bapicurr-bapicurr VALUE '1000.00',
lv_amount_usd TYPE bapicurr-bapicurr,
lv_rate TYPE bapi1093_0.
" EUR nach USD umrechnen
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_EXTERNAL'
EXPORTING
currency = 'EUR'
amount_internal = lv_amount_eur
IMPORTING
amount_external = lv_amount_eur.
CALL FUNCTION 'BAPI_EXCHANGERATE_GETDETAIL'
EXPORTING
rate_type = 'M' " Kurstyp
from_curr = 'EUR'
to_currncy = 'USD'
date = sy-datum
IMPORTING
exch_rate = lv_rate
EXCEPTIONS
not_found = 1
OTHERS = 2.
IF sy-subrc = 0.
lv_amount_usd = lv_amount_eur * lv_rate-exch_rate.
WRITE: / '1000 EUR =', lv_amount_usd, 'USD'.
ENDIF.

2. Währungskonvertierung mit CONVERT_TO_LOCAL_CURRENCY

DATA: lv_source_amount TYPE dmbtr VALUE '1000.00',
lv_target_amount TYPE dmbtr.
CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
EXPORTING
date = sy-datum
foreign_amount = lv_source_amount
foreign_currency = 'USD'
local_currency = 'EUR'
type_of_rate = 'M'
IMPORTING
local_amount = lv_target_amount
EXCEPTIONS
no_rate_found = 1
overflow = 2
no_factors_found = 3
no_spread_found = 4
derived_2_times = 5
OTHERS = 6.
IF sy-subrc = 0.
WRITE: / '1000 USD =', lv_target_amount, 'EUR'.
ELSE.
WRITE: / 'Wechselkurs nicht gefunden'.
ENDIF.

3. Dezimalstellen nach Währung

DATA: lv_amount_eur TYPE netwr VALUE '123.45',
lv_amount_jpy TYPE netwr VALUE '12345',
lv_decimals TYPE i.
" Dezimalstellen einer Währung ermitteln
SELECT SINGLE currdec FROM tcurx
WHERE currkey = 'EUR'
INTO @lv_decimals.
IF sy-subrc <> 0.
lv_decimals = 2. " Standard
ENDIF.
WRITE: / 'EUR hat', lv_decimals, 'Dezimalstellen'.
" Für JPY (0 Dezimalstellen)
SELECT SINGLE currdec FROM tcurx
WHERE currkey = 'JPY'
INTO @lv_decimals.
" lv_decimals = 0
" Betrag entsprechend Währung formatieren
CLASS zcl_currency DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: format_amount
IMPORTING iv_amount TYPE any
iv_currency TYPE waers
RETURNING VALUE(rv_formatted) TYPE string.
ENDCLASS.
CLASS zcl_currency IMPLEMENTATION.
METHOD format_amount.
DATA: lv_decimals TYPE i.
SELECT SINGLE currdec FROM tcurx
WHERE currkey = @iv_currency
INTO @lv_decimals.
IF sy-subrc <> 0.
lv_decimals = 2.
ENDIF.
rv_formatted = |{ iv_amount DECIMALS = lv_decimals }|.
ENDMETHOD.
ENDCLASS.

4. Interne/Externe Währungsdarstellung

DATA: lv_internal TYPE bapicurr-bapicurr,
lv_external TYPE bapicurr-bapicurr.
" Intern (DB) -> Extern (Anzeige)
" Für Währungen mit weniger als 2 Dezimalstellen
lv_internal = '12345'. " JPY in DB (ohne Dezimalen)
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_EXTERNAL'
EXPORTING
currency = 'JPY'
amount_internal = lv_internal
IMPORTING
amount_external = lv_external.
" lv_external = '12345.00' für Anzeige
" Extern -> Intern (für Speicherung)
lv_external = '12345.00'.
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_INTERNAL'
EXPORTING
currency = 'JPY'
amount_external = lv_external
max_number_of_digits = 15
IMPORTING
amount_internal = lv_internal.
" lv_internal = '12345' für DB

5. Mengeneinheiten konvertieren

DATA: lv_quantity_kg TYPE menge VALUE '1000',
lv_quantity_to TYPE menge.
" kg nach t (Tonnen) umrechnen
CALL FUNCTION 'UNIT_CONVERSION_SIMPLE'
EXPORTING
input = lv_quantity_kg
unit_in = 'KG'
unit_out = 'TO'
IMPORTING
output = lv_quantity_to
EXCEPTIONS
conversion_not_found = 1
division_by_zero = 2
input_invalid = 3
output_invalid = 4
overflow = 5
type_invalid = 6
units_missing = 7
unit_in_not_found = 8
unit_out_not_found = 9
OTHERS = 10.
IF sy-subrc = 0.
WRITE: / '1000 KG =', lv_quantity_to, 'TO'. " 1 TO
ENDIF.

6. Materialspezifische Mengenumrechnung

DATA: lv_quantity_st TYPE menge VALUE '100', " 100 Stück
lv_quantity_kg TYPE menge.
" Umrechnung mit Materialstamm
CALL FUNCTION 'MD_CONVERT_MATERIAL_UNIT'
EXPORTING
i_matnr = '000000000012345678'
i_in_me = 'ST' " Von Stück
i_out_me = 'KG' " Nach Kilogramm
i_menge = lv_quantity_st
IMPORTING
e_menge = lv_quantity_kg
EXCEPTIONS
error_in_application = 1
error = 2
OTHERS = 3.
IF sy-subrc = 0.
WRITE: / '100 ST =', lv_quantity_kg, 'KG'.
ENDIF.

7. Currency Helper Klasse

CLASS zcl_currency_helper DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF ty_conversion_result,
amount TYPE dmbtr,
currency TYPE waers,
rate TYPE ukurs,
END OF ty_conversion_result.
CLASS-METHODS: convert
IMPORTING iv_amount TYPE dmbtr
iv_from_currency TYPE waers
iv_to_currency TYPE waers
iv_date TYPE sy-datum DEFAULT sy-datum
iv_rate_type TYPE kurst DEFAULT 'M'
RETURNING VALUE(rs_result) TYPE ty_conversion_result
RAISING zcx_currency_error.
CLASS-METHODS: get_decimals
IMPORTING iv_currency TYPE waers
RETURNING VALUE(rv_decimals) TYPE i.
CLASS-METHODS: round_amount
IMPORTING iv_amount TYPE dmbtr
iv_currency TYPE waers
RETURNING VALUE(rv_amount) TYPE dmbtr.
CLASS-METHODS: to_internal
IMPORTING iv_amount TYPE dmbtr
iv_currency TYPE waers
RETURNING VALUE(rv_amount) TYPE dmbtr.
CLASS-METHODS: to_external
IMPORTING iv_amount TYPE dmbtr
iv_currency TYPE waers
RETURNING VALUE(rv_amount) TYPE dmbtr.
ENDCLASS.
CLASS zcl_currency_helper IMPLEMENTATION.
METHOD convert.
IF iv_from_currency = iv_to_currency.
rs_result-amount = iv_amount.
rs_result-currency = iv_to_currency.
rs_result-rate = 1.
RETURN.
ENDIF.
CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
EXPORTING
date = iv_date
foreign_amount = iv_amount
foreign_currency = iv_from_currency
local_currency = iv_to_currency
type_of_rate = iv_rate_type
IMPORTING
local_amount = rs_result-amount
exchange_rate = rs_result-rate
EXCEPTIONS
no_rate_found = 1
OTHERS = 2.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE zcx_currency_error
EXPORTING
textid = zcx_currency_error=>rate_not_found
from_currency = iv_from_currency
to_currency = iv_to_currency.
ENDIF.
rs_result-currency = iv_to_currency.
ENDMETHOD.
METHOD get_decimals.
SELECT SINGLE currdec FROM tcurx
WHERE currkey = @iv_currency
INTO @rv_decimals.
IF sy-subrc <> 0.
rv_decimals = 2. " Standard
ENDIF.
ENDMETHOD.
METHOD round_amount.
DATA(lv_decimals) = get_decimals( iv_currency ).
rv_amount = round(
val = iv_amount
dec = lv_decimals
mode = cl_abap_math=>round_half_up
).
ENDMETHOD.
METHOD to_internal.
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_INTERNAL'
EXPORTING
currency = iv_currency
amount_external = iv_amount
max_number_of_digits = 23
IMPORTING
amount_internal = rv_amount.
ENDMETHOD.
METHOD to_external.
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_EXTERNAL'
EXPORTING
currency = iv_currency
amount_internal = iv_amount
IMPORTING
amount_external = rv_amount.
ENDMETHOD.
ENDCLASS.
" Verwendung
TRY.
DATA(ls_result) = zcl_currency_helper=>convert(
iv_amount = '1000.00'
iv_from_currency = 'USD'
iv_to_currency = 'EUR'
).
WRITE: / '1000 USD =', ls_result-amount, 'EUR'.
WRITE: / 'Kurs:', ls_result-rate.
CATCH zcx_currency_error INTO DATA(lx_error).
WRITE: / lx_error->get_text( ).
ENDTRY.

8. Unit Helper Klasse

CLASS zcl_unit_helper DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: convert
IMPORTING iv_quantity TYPE menge
iv_from_unit TYPE meins
iv_to_unit TYPE meins
iv_matnr TYPE matnr OPTIONAL
RETURNING VALUE(rv_quantity) TYPE menge
RAISING zcx_unit_error.
CLASS-METHODS: get_base_unit
IMPORTING iv_matnr TYPE matnr
RETURNING VALUE(rv_unit) TYPE meins.
CLASS-METHODS: get_conversion_factor
IMPORTING iv_from_unit TYPE meins
iv_to_unit TYPE meins
RETURNING VALUE(rv_factor) TYPE f.
ENDCLASS.
CLASS zcl_unit_helper IMPLEMENTATION.
METHOD convert.
IF iv_from_unit = iv_to_unit.
rv_quantity = iv_quantity.
RETURN.
ENDIF.
" Materialspezifisch wenn Material angegeben
IF iv_matnr IS NOT INITIAL.
CALL FUNCTION 'MD_CONVERT_MATERIAL_UNIT'
EXPORTING
i_matnr = iv_matnr
i_in_me = iv_from_unit
i_out_me = iv_to_unit
i_menge = iv_quantity
IMPORTING
e_menge = rv_quantity
EXCEPTIONS
error_in_application = 1
OTHERS = 2.
IF sy-subrc = 0.
RETURN.
ENDIF.
ENDIF.
" Allgemeine Umrechnung
CALL FUNCTION 'UNIT_CONVERSION_SIMPLE'
EXPORTING
input = iv_quantity
unit_in = iv_from_unit
unit_out = iv_to_unit
IMPORTING
output = rv_quantity
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE zcx_unit_error
EXPORTING
textid = zcx_unit_error=>conversion_failed
from_unit = iv_from_unit
to_unit = iv_to_unit.
ENDIF.
ENDMETHOD.
METHOD get_base_unit.
SELECT SINGLE meins FROM mara
WHERE matnr = @iv_matnr
INTO @rv_unit.
ENDMETHOD.
METHOD get_conversion_factor.
DATA: lv_output TYPE menge.
CALL FUNCTION 'UNIT_CONVERSION_SIMPLE'
EXPORTING
input = CONV menge( 1 )
unit_in = iv_from_unit
unit_out = iv_to_unit
IMPORTING
output = lv_output
EXCEPTIONS
OTHERS = 1.
IF sy-subrc = 0.
rv_factor = lv_output.
ELSE.
rv_factor = 1.
ENDIF.
ENDMETHOD.
ENDCLASS.

9. Preisumrechnung

" Preis pro Mengeneinheit umrechnen
DATA: lv_price TYPE netpr VALUE '10.00', " 10 EUR pro 100 ST
lv_per_unit TYPE i VALUE 100, " Pro 100
lv_quantity TYPE menge VALUE '250',
lv_total TYPE netwr.
" Gesamtpreis berechnen
lv_total = lv_price * lv_quantity / lv_per_unit.
" 10.00 * 250 / 100 = 25.00 EUR
" Mit Einheitenumrechnung
DATA: lv_price_kg TYPE netpr VALUE '2.50', " 2.50 EUR pro KG
lv_qty_to TYPE menge VALUE '500', " 500 KG bestellt
lv_total_to TYPE netwr.
" Erst in Tonnen umrechnen
DATA(lv_qty_converted) = zcl_unit_helper=>convert(
iv_quantity = lv_qty_to
iv_from_unit = 'KG'
iv_to_unit = 'TO'
).
" Preis für 1 TO berechnen
DATA(lv_price_to) = lv_price_kg * 1000. " 2500 EUR pro TO
lv_total_to = lv_price_to * lv_qty_converted.

10. Wechselkurs pflegen

" Wechselkurse lesen (TCURR)
SELECT * FROM tcurr
WHERE kurst = 'M' " Kurstyp
AND fcurr = 'USD' " Ausgangswährung
AND tcurr = 'EUR' " Zielwährung
AND gdatu <= @sy-datum " Gültig ab
ORDER BY gdatu DESCENDING
INTO TABLE @DATA(lt_rates)
UP TO 1 ROWS.
IF lt_rates IS NOT INITIAL.
DATA(ls_rate) = lt_rates[ 1 ].
WRITE: / 'Kurs:', ls_rate-ukurs.
WRITE: / 'Gültig ab:', ls_rate-gdatu.
ENDIF.
" Kurs per BAPI ändern (Customizing)
DATA: lt_return TYPE TABLE OF bapiret2.
CALL FUNCTION 'BAPI_EXCHRATE_CREATEMULTIPLE'
TABLES
exchrate_list = lt_rates_new
return = lt_return.
IF NOT line_exists( lt_return[ type = 'E' ] ).
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
ENDIF.

11. Kaufmännische Rundung

DATA: lv_amount TYPE p DECIMALS 4 VALUE '123.4567',
lv_rounded TYPE p DECIMALS 2.
" Mathematische Rundung
lv_rounded = round( val = lv_amount dec = 2 ).
" 123.46
" Kaufmännische Rundung (halbe auf)
lv_rounded = round(
val = lv_amount
dec = 2
mode = cl_abap_math=>round_half_up
).
" Abrunden
lv_rounded = floor( lv_amount * 100 ) / 100.
" 123.45
" Aufrunden
lv_rounded = ceil( lv_amount * 100 ) / 100.
" 123.46
" Mit Währung
DATA(lv_currency_amount) = zcl_currency_helper=>round_amount(
iv_amount = lv_amount
iv_currency = 'EUR'
).

12. Beträge in SQL

" Währungsumrechnung in CDS View
@AbapCatalog.viewEnhancementCategory: [#NONE]
define view entity ZI_SalesAmount as select from vbak
{
key vbeln,
netwr,
waerk,
// Betrag mit Dezimalkorrektur
@Semantics.amount.currencyCode: 'waerk'
netwr as Amount,
// In Hauswährung (vereinfacht)
@Semantics.amount.currencyCode: 'EUR'
cast(netwr as abap.curr(15,2)) as AmountEUR
}
" In Open SQL
SELECT vbeln, netwr, waerk,
CURR_TO_DECFLOAT_AMOUNT( netwr, waerk ) AS amount_decimal
FROM vbak
INTO TABLE @DATA(lt_sales).

13. ISO-Codes

" SAP-interne Codes vs. ISO-Codes
DATA: lv_sap_currency TYPE waers VALUE 'EUR',
lv_iso_currency TYPE isocd.
" SAP -> ISO
SELECT SINGLE isocd FROM tcurc
WHERE waession = @lv_sap_currency
INTO @lv_iso_currency.
" ISO -> SAP
SELECT SINGLE waers FROM tcurc
WHERE isocd = 'EUR'
INTO @lv_sap_currency.
" Mengeneinheiten
DATA: lv_sap_unit TYPE meins VALUE 'KG',
lv_iso_unit TYPE isocd_unit.
SELECT SINGLE isocode FROM t006
WHERE msehi = @lv_sap_unit
INTO @lv_iso_unit.

Wichtige Tabellen

TabelleInhalt
TCURRWechselkurse
TCURCWährungen
TCURXDezimalstellen
T006Mengeneinheiten
T006AEinheitentexte
MARMMaterialmengeneinheiten

Wichtige Hinweise / Best Practice

  • Dezimalstellen immer währungsabhängig behandeln.
  • BAPI_CURRENCY_CONV für SAP-konforme Konvertierung.
  • Interne Darstellung für DB, externe für Anzeige.
  • Rundung nach kaufmännischen Regeln.
  • Kurstyp ‘M’ für Standard-Umrechnung.
  • Materialspezifisch für genaue Mengenumrechnung.
  • ISO-Codes für externe Schnittstellen.
  • Semantics in CDS für korrekte Darstellung.
  • Fehlerbehandlung bei nicht gefundenen Kursen.
  • Kombinieren Sie mit BAPI Development für APIs.