Skip to Content
Technical Articles

Amount to words in S4HCloud

This blog post will help you in implementing a similar functionality of SPELL_AMOUNT function module, that exists for on-premise solutions, as a custom reusable functionality on S4HCloud. The solution mentioned in this post will convert decimal numbers as well but currently only in English. The feature could be enhanced accordingly for other languages by putting some extra logic.

Introduction

It is very common for the customers using SAP to come up with custom forms, which might need the functionality of showing amounts in words rather than in numbers.

SAP On-premise solution to this requirement is the usage of the function module SPELL_AMOUNT; but if the same requirement is asked by the customer implementing S4HCloud, it causes a concern, as there is no re-usable component readily available as of the version SAP S/4HANA Cloud 2011 unless a certain pre-defined template is used, which internally uses the functionality of the function module SPELL_AMOUNT.

It was a surprise to me that a standard re-usable functionality to convert amount to plain words was missing in S4HCloud, in order to enable developers use it in custom developments. While trying to search for solutions, I came across a blog post written by Prasanth Padmanabhan Menon which helped me to quite an extent to get the solution for the requirement. The blog however was addressing the requirement partly, by not dealing with decimals within the amount.

Solution

Kindly follow all the steps as explained in the blog post, and ensure to perform the following changes in the mentioned two sections:

Section: “Creation of Custom Reusable Element”

Maintain an additional optional changing parameter IV_LEVEL to method NUM2WORDS. IV_LEVEL should be defined as a parameter of type Number, length 10 with two decimal places. This parameter would be used by the logic internally.

Additional%20Parameter

Additional Parameter

Once you create a draft, kindly use the following code instead of the code mentioned in the blog post:

TYPES: BEGIN OF str_d,
             num   TYPE i,
             word1 TYPE string,
             word2 TYPE string,
           END OF str_d.

    DATA: ls_h TYPE str_d,
          ls_k TYPE str_d,
          ls_m TYPE str_d,
          ls_b TYPE str_d,
          ls_t TYPE str_d,
          ls_o TYPE str_d.

    DATA lv_int TYPE i.
    DATA lv_int1 TYPE i.
    DATA lv_int2 TYPE i.
    DATA lv_dec_s TYPE string.
    DATA lv_dec   TYPE i.
    DATA lv_wholenum TYPE i.
    DATA lv_inp1 TYPE string.
    DATA lv_inp2 TYPE string.
    DATA lv_dec_words type c length 255.

    IF iv_num IS INITIAL.
        RETURN.
    ENDIF.

    ls_h-num = 100.
    ls_h-word1 = 'Hundred'.
    ls_h-word2 = 'Hundred and'.

    ls_k-num = ls_h-num * 10.
    ls_k-word1 = 'Thousand'.
    ls_k-word2 = 'Thousand'.

    ls_m-num = ls_k-num * 1000.
    ls_m-word1 = 'Million'.
    ls_m-word2 = 'Million'.

    ls_b-num = ls_m-num * 1000.
    ls_b-word1 = 'Billion'.
    ls_b-word2 = 'Billion'.

*    Use the following if this is required in Lakhs/Crores instead of Millions/Billions
*
*    ls_h-num = 100.
*    ls_h-word1 = 'Hundred'.
*    ls_h-word2 = 'Hundred and'.

*    ls_k-num = ls_h-num * 10.
*    ls_k-word1 = 'Thousand'.
*    ls_k-word2 = 'Thousand'.

*    ls_m-num = ls_k-num * 100.
*    ls_m-word1 = 'Lakh'.
*    ls_m-word2 = 'Lakh'.

*    ls_b-num = ls_m-num * 100.
*    ls_b-word1 = 'Crore'.
*    ls_b-word2 = 'Crore'.

    SPLIT iv_num AT '.' INTO lv_inp1 lv_inp2.

    lv_int = lv_inp1.
    lv_wholenum = lv_int.

    IF iv_level IS INITIAL.
        IF lv_inp2 IS NOT INITIAL.
            CONDENSE lv_inp2.
            lv_dec_s   = lv_inp2.
            lv_dec     = lv_inp2.
        ENDIF.
    ENDIF.
    iv_level = iv_level + 1.

    SELECT * FROM yy1_number2string INTO TABLE @DATA(lt_d).

*   Whole Number converted to Words
    IF lt_d IS NOT INITIAL.
      IF lv_int <= 20.
        READ TABLE lt_d REFERENCE INTO DATA(ls_d) WITH KEY num = lv_int.
        rv_words = |{ ls_d->word }|.
    ELSEIF lv_int < 100 AND lv_int > 20.
        DATA(mod) = lv_int MOD 10.
        DATA(floor) = floor( lv_int DIV 10 ).
        IF mod = 0.
          READ TABLE lt_d REFERENCE INTO ls_d WITH KEY num = lv_int.
          rv_words = ls_d->word.
        ELSE.
          READ TABLE lt_d REFERENCE INTO ls_d WITH KEY num = floor * 10.
          DATA(pos1) = ls_d->word.
          READ TABLE lt_d REFERENCE INTO ls_d WITH KEY num = mod.
          DATA(pos2) = ls_d->word.
          rv_words = |{ pos1 } | && |{ pos2 } |.
        ENDIF.
      ELSE.
        IF lv_int  < ls_k-num.
          ls_o = ls_h.
        ELSEIF lv_int < ls_m-num.
          ls_o = ls_k.
        ELSEIF lv_int < ls_b-num.
          ls_o = ls_m.
        ELSE.
          ls_o = ls_b.
        ENDIF.
        mod = lv_int MOD ls_o-num.
        floor = floor( iv_num DIV ls_o-num ).
        lv_inp1 = floor.
        lv_inp2 = mod.

        IF mod = 0.
          DATA(output2) = num2words( EXPORTING iv_num  = lv_inp1
                                     CHANGING iv_level = iv_level ).
          rv_words =  |{ output2 } | && |{ ls_o-word1 } |.
        ELSE.
          output2 = num2words( EXPORTING iv_num   = lv_inp1
                               CHANGING  iv_level = iv_level ).
          DATA(output3) = num2words( EXPORTING iv_num  = lv_inp2
                                     CHANGING iv_level = iv_level ).
          rv_words = |{ output2 } | && |{ ls_o-word2 } | && |{ output3 } |.
        ENDIF.
      ENDIF.

      iv_level = iv_level - 1.
      IF iv_level IS INITIAL.
*       "Dollars" is base monetary unit used in this sample,
*       but this could change as per the currency of the scenario.
*       It must be ensured that relative fractional monetary unit
*       shall be updated later in the code relative to the base unit
        rv_words = |{ rv_words } Dollars|.
        IF lv_dec <= 20.
            READ TABLE lt_d REFERENCE INTO DATA(ls_d2) WITH KEY num = lv_dec.
            IF SY-SUBRC = 0.
                lv_dec_words = |{ ls_d2->word }|.
            ENDIF.
        ELSEIF lv_dec < 100 AND lv_dec > 20.
            DATA(mod1) = lv_dec MOD 10.
            DATA(floor1) = floor( lv_dec DIV 10 ).
            IF mod1 = 0.
                READ TABLE lt_d REFERENCE INTO ls_d2 WITH KEY num = lv_dec.
                IF SY-SUBRC = 0.
                    lv_dec_words = ls_d2->word.
                ENDIF.
            ELSE.
                READ TABLE lt_d REFERENCE INTO ls_d2 WITH KEY num = floor1 * 10.
                IF SY-SUBRC = 0.
                    DATA(pos1_d) = ls_d2->word.
                ENDIF.
                READ TABLE lt_d REFERENCE INTO ls_d2 WITH KEY num = mod1.
                IF SY-SUBRC = 0.
                    DATA(pos2_d) = ls_d2->word.
                ENDIF.
                IF POS1_D IS NOT INITIAL AND pos2_d IS NOT INITIAL.
                    lv_dec_words = |{ pos1_d } | && |{ pos2_d } |.
                ENDIF.
            ENDIF.
        ENDIF.
*       Since "Dollars" was used for base monetary unit, "Cents"
*       has been used as fractional monetary unit in the code below
*       This can be handled dynamically as well based on the requirement
        rv_words = |{ rv_words } { lv_dec_words } Cents|.
      ENDIF.
      RETURN.
    ENDIF.

 

Section: “Populate the Custom Field”

Within the BAdI code, while invoking the reusable component YY1_NUM2WORDS=>NUM2WORDS, it must be ensured that the amount is passed in total rather than only the number part before the decimals.

DATA: lv_amount TYPE string.

lv_amount = purchaseorderitem-netpriceamount.

purchaseorderitemchange-yy1_amountinwords_pdi =  yy1_num2words=>num2words( iv_num = lv_amount ).

 

And we are done…. the amount shall be converted to words by representing the base monetary unit as well as the fractional monetary unit (Decimal part) as required.

Feel free to ask any queries regarding the article in the comments section or in the following link.

/
Additional%20Parameter
Be the first to leave a comment
You must be Logged on to comment or reply to a post.