ABAP Currency & Unit Conversion: Moneda y unidades de medida

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Currency & Unit Conversion en ABAP requiere especial atención a los decimales, tipos de cambio y factores de conversión. El manejo correcto es crucial para aplicaciones financieras y logísticas.

Concepto básico

TemaDescripción
MonedaDecimales dependientes de la moneda (EUR: 2, JPY: 0)
Tipo de cambioTasas de la tabla TCURR
Unidad de medidaFactores de conversión en T006
Interno vs. ExternoRepresentación diferente

Ejemplos

1. Convertir importe de moneda

DATA: lv_amount_eur TYPE bapicurr-bapicurr VALUE '1000.00',
lv_amount_usd TYPE bapicurr-bapicurr,
lv_rate TYPE bapi1093_0.
" Convertir EUR a USD
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' " Tipo de tasa
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. Conversión de moneda con 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: / 'Tipo de cambio no encontrado'.
ENDIF.

3. Decimales según moneda

DATA: lv_amount_eur TYPE netwr VALUE '123.45',
lv_amount_jpy TYPE netwr VALUE '12345',
lv_decimals TYPE i.
" Determinar decimales de una moneda
SELECT SINGLE currdec FROM tcurx
WHERE currkey = 'EUR'
INTO @lv_decimals.
IF sy-subrc <> 0.
lv_decimals = 2. " Estándar
ENDIF.
WRITE: / 'EUR tiene', lv_decimals, 'decimales'.
" Para JPY (0 decimales)
SELECT SINGLE currdec FROM tcurx
WHERE currkey = 'JPY'
INTO @lv_decimals.
" lv_decimals = 0
" Formatear importe según moneda
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. Representación de moneda interna/externa

DATA: lv_internal TYPE bapicurr-bapicurr,
lv_external TYPE bapicurr-bapicurr.
" Interna (BD) -> Externa (visualización)
" Para monedas con menos de 2 decimales
lv_internal = '12345'. " JPY en BD (sin decimales)
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_EXTERNAL'
EXPORTING
currency = 'JPY'
amount_internal = lv_internal
IMPORTING
amount_external = lv_external.
" lv_external = '12345.00' para visualización
" Externa -> Interna (para almacenamiento)
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' para BD

5. Convertir unidades de medida

DATA: lv_quantity_kg TYPE menge VALUE '1000',
lv_quantity_to TYPE menge.
" Convertir kg a t (toneladas)
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. Conversión de cantidad específica de material

DATA: lv_quantity_st TYPE menge VALUE '100', " 100 piezas
lv_quantity_kg TYPE menge.
" Conversión con maestro de materiales
CALL FUNCTION 'MD_CONVERT_MATERIAL_UNIT'
EXPORTING
i_matnr = '000000000012345678'
i_in_me = 'ST' " Desde piezas
i_out_me = 'KG' " A kilogramos
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. Clase helper de moneda

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. " Estándar
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.
" Uso
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: / 'Tasa:', ls_result-rate.
CATCH zcx_currency_error INTO DATA(lx_error).
WRITE: / lx_error->get_text( ).
ENDTRY.

8. Clase helper de unidades

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.
" Específico de material si se indica material
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.
" Conversión general
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. Conversión de precios

" Convertir precio por unidad de medida
DATA: lv_price TYPE netpr VALUE '10.00', " 10 EUR por 100 piezas
lv_per_unit TYPE i VALUE 100, " Por 100
lv_quantity TYPE menge VALUE '250',
lv_total TYPE netwr.
" Calcular precio total
lv_total = lv_price * lv_quantity / lv_per_unit.
" 10.00 * 250 / 100 = 25.00 EUR
" Con conversión de unidades
DATA: lv_price_kg TYPE netpr VALUE '2.50', " 2.50 EUR por KG
lv_qty_to TYPE menge VALUE '500', " 500 KG pedidos
lv_total_to TYPE netwr.
" Primero convertir a toneladas
DATA(lv_qty_converted) = zcl_unit_helper=>convert(
iv_quantity = lv_qty_to
iv_from_unit = 'KG'
iv_to_unit = 'TO'
).
" Calcular precio por 1 TO
DATA(lv_price_to) = lv_price_kg * 1000. " 2500 EUR por TO
lv_total_to = lv_price_to * lv_qty_converted.

10. Redondeo comercial

DATA: lv_amount TYPE p DECIMALS 4 VALUE '123.4567',
lv_rounded TYPE p DECIMALS 2.
" Redondeo matemático
lv_rounded = round( val = lv_amount dec = 2 ).
" 123.46
" Redondeo comercial (mitad hacia arriba)
lv_rounded = round(
val = lv_amount
dec = 2
mode = cl_abap_math=>round_half_up
).
" Redondear hacia abajo
lv_rounded = floor( lv_amount * 100 ) / 100.
" 123.45
" Redondear hacia arriba
lv_rounded = ceil( lv_amount * 100 ) / 100.
" 123.46
" Con moneda
DATA(lv_currency_amount) = zcl_currency_helper=>round_amount(
iv_amount = lv_amount
iv_currency = 'EUR'
).

11. Importes en SQL

" Conversión de moneda en CDS View
@AbapCatalog.viewEnhancementCategory: [#NONE]
define view entity ZI_SalesAmount as select from vbak
{
key vbeln,
netwr,
waerk,
// Importe con corrección de decimales
@Semantics.amount.currencyCode: 'waerk'
netwr as Amount,
// En moneda local (simplificado)
@Semantics.amount.currencyCode: 'EUR'
cast(netwr as abap.curr(15,2)) as AmountEUR
}
" En Open SQL
SELECT vbeln, netwr, waerk,
CURR_TO_DECFLOAT_AMOUNT( netwr, waerk ) AS amount_decimal
FROM vbak
INTO TABLE @DATA(lt_sales).

12. Códigos ISO

" Códigos internos SAP vs. códigos ISO
DATA: lv_sap_currency TYPE waers VALUE 'EUR',
lv_iso_currency TYPE isocd.
" SAP -> ISO
SELECT SINGLE isocd FROM tcurc
WHERE waers = @lv_sap_currency
INTO @lv_iso_currency.
" ISO -> SAP
SELECT SINGLE waers FROM tcurc
WHERE isocd = 'EUR'
INTO @lv_sap_currency.
" Unidades de medida
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.

Tablas importantes

TablaContenido
TCURRTipos de cambio
TCURCMonedas
TCURXDecimales
T006Unidades de medida
T006ATextos de unidades
MARMUnidades de medida de material

Notas importantes / Mejores prácticas

  • Decimales siempre tratar dependiendo de la moneda.
  • BAPI_CURRENCY_CONV para conversión conforme a SAP.
  • Representación interna para BD, externa para visualización.
  • Redondeo según reglas comerciales.
  • Tipo de tasa ‘M’ para conversión estándar.
  • Específico de material para conversión precisa de cantidades.
  • Códigos ISO para interfaces externas.
  • Semantics en CDS para representación correcta.
  • Manejo de errores para tasas no encontradas.
  • Combine con Desarrollo BAPI para APIs.