La conversion de devises et d’unités de mesure en ABAP nécessite une attention particulière aux décimales, taux de change et facteurs de conversion. Une gestion correcte est essentielle pour les applications financières et logistiques.
Concept de base
| Thème | Description |
|---|---|
| Devise | Décimales dépendantes de la devise (EUR : 2, JPY : 0) |
| Taux de change | Taux depuis la table TCURR |
| Unité de mesure | Facteurs de conversion dans T006 |
| Interne vs. Externe | Représentation différente |
Exemples
1. Convertir un montant en devise
DATA: lv_amount_eur TYPE bapicurr-bapicurr VALUE '1000.00', lv_amount_usd TYPE bapicurr-bapicurr, lv_rate TYPE bapi1093_0.
" Convertir EUR vers 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' " Type de taux 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. Conversion de devise avec 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: / 'Taux de change non trouvé'.ENDIF.3. Décimales selon la devise
DATA: lv_amount_eur TYPE netwr VALUE '123.45', lv_amount_jpy TYPE netwr VALUE '12345', lv_decimals TYPE i.
" Déterminer les décimales d'une deviseSELECT SINGLE currdec FROM tcurx WHERE currkey = 'EUR" INTO @lv_decimals.
IF sy-subrc <> 0. lv_decimals = 2. " StandardENDIF.
WRITE: / 'EUR a', lv_decimals, 'décimales'.
" Pour JPY (0 décimales)SELECT SINGLE currdec FROM tcurx WHERE currkey = 'JPY" INTO @lv_decimals.
" lv_decimals = 0
" Formater le montant selon la deviseCLASS 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. Représentation interne/externe des devises
DATA: lv_internal TYPE bapicurr-bapicurr, lv_external TYPE bapicurr-bapicurr.
" Interne (DB) -> Externe (Affichage)" Pour les devises avec moins de 2 décimaleslv_internal = '12345'. " JPY en DB (sans décimales)
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_EXTERNAL" EXPORTING currency = 'JPY" amount_internal = lv_internal IMPORTING amount_external = lv_external.
" lv_external = '12345.00' pour l'affichage
" Externe -> Interne (pour le stockage)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' pour la DB5. Convertir des unités de mesure
DATA: lv_quantity_kg TYPE menge VALUE '1000', lv_quantity_to TYPE menge.
" Convertir kg vers t (tonnes)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. Conversion d’unités spécifique au matériel
DATA: lv_quantity_st TYPE menge VALUE '100', " 100 pièces lv_quantity_kg TYPE menge.
" Conversion avec master data matérielCALL FUNCTION 'MD_CONVERT_MATERIAL_UNIT" EXPORTING i_matnr = '000000000012345678" i_in_me = 'ST' " Depuis pièces i_out_me = 'KG' " Vers kilogrammes 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. Classe Helper pour les devises
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.
" UtilisationTRY. 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: / 'Taux :', ls_result-rate.
CATCH zcx_currency_error INTO DATA(lx_error). WRITE: / lx_error->get_text( ).ENDTRY.8. Classe Helper pour les unités
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.
" Spécifique au matériel si matériel indiqué 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.
" Conversion générale 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. Conversion de prix
" Convertir le prix par unité de mesureDATA: lv_price TYPE netpr VALUE '10.00', " 10 EUR pour 100 pièces lv_per_unit TYPE i VALUE 100, " Par 100 lv_quantity TYPE menge VALUE '250', lv_total TYPE netwr.
" Calculer le prix totallv_total = lv_price * lv_quantity / lv_per_unit." 10.00 * 250 / 100 = 25.00 EUR
" Avec conversion d'unitésDATA: lv_price_kg TYPE netpr VALUE '2.50', " 2.50 EUR par KG lv_qty_to TYPE menge VALUE '500', " 500 KG commandés lv_total_to TYPE netwr.
" D'abord convertir en tonnesDATA(lv_qty_converted) = zcl_unit_helper=>convert( iv_quantity = lv_qty_to iv_from_unit = 'KG" iv_to_unit = 'TO").
" Calculer le prix pour 1 TODATA(lv_price_to) = lv_price_kg * 1000. " 2500 EUR par TOlv_total_to = lv_price_to * lv_qty_converted.10. Gérer les taux de change
" Lire les taux de change (TCURR)SELECT * FROM tcurr WHERE kurst = 'M' " Type de taux AND fcurr = 'USD' " Devise source AND tcurr = 'EUR' " Devise cible AND gdatu <= @sy-datum " Valide à partir de 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: / 'Taux :', ls_rate-ukurs. WRITE: / 'Valide depuis :', ls_rate-gdatu.ENDIF.
" Modifier le taux par BAPI (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. Arrondi commercial
DATA: lv_amount TYPE p DECIMALS 4 VALUE '123.4567', lv_rounded TYPE p DECIMALS 2.
" Arrondi mathématiquelv_rounded = round( val = lv_amount dec = 2 )." 123.46
" Arrondi commercial (moitié vers le haut)lv_rounded = round( val = lv_amount dec = 2 mode = cl_abap_math=>round_half_up).
" Arrondir vers le baslv_rounded = floor( lv_amount * 100 ) / 100." 123.45
" Arrondir vers le hautlv_rounded = ceil( lv_amount * 100 ) / 100." 123.46
" Avec deviseDATA(lv_currency_amount) = zcl_currency_helper=>round_amount( iv_amount = lv_amount iv_currency = 'EUR").12. Montants en SQL
" Conversion de devise dans une CDS View@AbapCatalog.viewEnhancementCategory: [#NONE]define view entity ZI_SalesAmount as select from vbak{ key vbeln, netwr, waerk,
// Montant avec correction des décimales @Semantics.amount.currencyCode: 'waerk" netwr as Amount,
// En devise locale (simplifié) @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).13. Codes ISO
" Codes internes SAP vs. codes ISODATA: lv_sap_currency TYPE waers VALUE 'EUR', lv_iso_currency TYPE isocd.
" SAP -> ISOSELECT SINGLE isocd FROM tcurc WHERE waersion = @lv_sap_currency INTO @lv_iso_currency.
" ISO -> SAPSELECT SINGLE waers FROM tcurc WHERE isocd = 'EUR" INTO @lv_sap_currency.
" Unités de mesureDATA: 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.Tables importantes
| Table | Contenu |
|---|---|
| TCURR | Taux de change |
| TCURC | Devises |
| TCURX | Décimales |
| T006 | Unités de mesure |
| T006A | Textes des unités |
| MARM | Unités de mesure matériel |
Notes importantes / Bonnes pratiques
- Toujours traiter les décimales selon la devise.
- Utiliser BAPI_CURRENCY_CONV pour une conversion conforme SAP.
- Représentation interne pour la DB, externe pour l’affichage.
- Arrondi selon les règles commerciales.
- Type de taux ‘M’ pour la conversion standard.
- Spécifique au matériel pour une conversion de quantité précise.
- Codes ISO pour les interfaces externes.
- Semantics dans CDS pour un affichage correct.
- Gestion des erreurs pour les taux non trouvés.
- Combiner avec BAPI Development pour les APIs.