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
| Tema | Descripción |
|---|---|
| Moneda | Decimales dependientes de la moneda (EUR: 2, JPY: 0) |
| Tipo de cambio | Tasas de la tabla TCURR |
| Unidad de medida | Factores de conversión en T006 |
| Interno vs. Externo | Representació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 USDCALL 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 monedaSELECT SINGLE currdec FROM tcurx WHERE currkey = 'EUR' INTO @lv_decimals.
IF sy-subrc <> 0. lv_decimals = 2. " EstándarENDIF.
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 monedaCLASS 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 decimaleslv_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 BD5. 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 TOENDIF.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 materialesCALL 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.
" UsoTRY. 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 medidaDATA: 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 totallv_total = lv_price * lv_quantity / lv_per_unit." 10.00 * 250 / 100 = 25.00 EUR
" Con conversión de unidadesDATA: 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 toneladasDATA(lv_qty_converted) = zcl_unit_helper=>convert( iv_quantity = lv_qty_to iv_from_unit = 'KG' iv_to_unit = 'TO').
" Calcular precio por 1 TODATA(lv_price_to) = lv_price_kg * 1000. " 2500 EUR por TOlv_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áticolv_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 abajolv_rounded = floor( lv_amount * 100 ) / 100." 123.45
" Redondear hacia arribalv_rounded = ceil( lv_amount * 100 ) / 100." 123.46
" Con monedaDATA(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 SQLSELECT 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 ISODATA: lv_sap_currency TYPE waers VALUE 'EUR', lv_iso_currency TYPE isocd.
" SAP -> ISOSELECT SINGLE isocd FROM tcurc WHERE waers = @lv_sap_currency INTO @lv_iso_currency.
" ISO -> SAPSELECT SINGLE waers FROM tcurc WHERE isocd = 'EUR' INTO @lv_sap_currency.
" Unidades de medidaDATA: 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
| Tabla | Contenido |
|---|---|
| TCURR | Tipos de cambio |
| TCURC | Monedas |
| TCURX | Decimales |
| T006 | Unidades de medida |
| T006A | Textos de unidades |
| MARM | Unidades 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.