Monday 22 May 2023

Additional accounting document creation in ECC for CRM Billing

Introduction: Recently we faced a scenario where it was required to create one more accounting document ( different document type) in ECC for CRM billing scenario and for a special case. There might be one question can come up why it was at all required and believe me that we had a same question. But business was very stringent due to one it’s special case and wanted to inherit this custom requirement with the standard process.

So we were left with no option and achieved same by exploring the requirement through ECC USER-EXIT/ CRM middleware BADI, automatically after the standard process gets completed.

Solution: We have middleware accounting BADI (BILL_ACC_IF) which will pass the data to ECC during accounting document creation from CRM billing. Let’s create one implementation and put code in method IF_EX_BILL_ACC_IF~ENRICH_ACC_DOCUMENT to populate changing parameter (extension table) CT_BAPICRMPAREX.


Please refer below code snippets to demonstrate the functionality:

ECC for CRM Billing, SAP ABAP Career, SAP ABPA Skills, SAP ABAP Jobs, SAP ABAP Tutorial and Materials, SAP ABAP Learning, SAP ABAP Guides

For my requirement, it was required to pass Header, Item and conditions details. So I created three separate DDIC structures in CRM and ECC both.

CONSTANTS : lc_kschl          TYPE kschl VALUE 'ZCND',
            lc_billh_struct   TYPE te_struc VALUE 'ZBILLING_H',
            lc_billi_struct   TYPE te_struc VALUE 'ZBILLING_I',
            lc_billcnd_struct TYPE te_struc VALUE 'ZBILLING_CND'.

    DATA : lt_cond_db TYPE prct_cond_du_tab,
           ls_head_db TYPE prct_head_du.

* Get Item Net Value and Product ID
            SELECT a~bdi_guid,
                   a~itemno_ext,
                   a~net_value,
                   a~product_descr,
                   b~product_id
              FROM /1bea/crmb_bdi AS a
              INNER JOIN comm_product AS b
              ON a~product = b~product_guid
              UP TO 1 ROWS
              INTO @DATA(ls_crmb_bdi_pr)
              WHERE a~bdi_guid = @is_item-be_item_guid.
            ENDSELECT.

            IF sy-subrc EQ 0.
              CLEAR : ls_head_db, lt_cond_db.
* Get Pricing conditions values against each line item
              CALL FUNCTION 'PRC_PRIDOC_SELECT_DB'
                EXPORTING
                  iv_pd_guid            = is_head-pridoc_guid
                  it_item_no            = VALUE prct_item_no_t( ( is_item-be_item_guid ) )
                IMPORTING
                  es_head_db            = ls_head_db
                  et_cond_db            = lt_cond_db
                EXCEPTIONS
                  database_read_failure = 1
                  OTHERS                = 2.
              IF sy-subrc NE 0.
                CLEAR ls_head_db.
              ENDIF.

              TRY.
DATA(lt_partner_std) = CORRESPONDING bill_acc_t_partner( lt_partner ).
* Populate condition type in conditions and pass it to the extension
                  DATA(ls_billing_cond) = VALUE zbilling_cnd( kposn = CONV kposn( is_item-be_item_no )
                                                              kschl = lc_kschl
                                                              kawrt = shift_left( lt_cond_db[ kschl = lc_kschl ]-kawrt )
                                                              kbetr = shift_left( lt_cond_db[ kschl = lc_kschl ]-kbetr )]
                                                              kwert = shift_left( lt_cond_db[ kschl = lc_kschl ]-kwert ) ).

                  ct_bapicrmparex[] = VALUE #( BASE ct_bapicrmparex[] ( be_head_no = cs_acc_achead-be_head_no
                                                                      structure = lc_billcnd_struct
                                                                      valuepart1 = CONV valuepart( ls_billing_cond ) ) ).
                CATCH cx_sy_itab_line_not_found.
                  CLEAR: ls_billing_cond.
              ENDTRY.

* Only populate below extension values if there is an 'ZCND' tax present at item level
              IF ls_billing_cond IS NOT INITIAL.

* Header item would be append only once as this method is being triggered at item level
                IF NOT line_exists( ct_bapicrmparex[ be_head_no = cs_acc_achead-be_head_no
                                                     structure = lc_billh_struct ] ).

* Populate Header value and pass it to the extension
                  DATA(ls_billing_header) = VALUE zbilling_h( vbeln = CONV #( |{ CONV vbeln_vf( |{ is_head-be_head_no ALPHA = OUT }| ) ALPHA = IN }| )
                                                                       waerk = is_head-doc_currency
                                                                       fkdat = is_head-pstng_date
                                                                       bukrs = cs_acc_achead-comp_code
                                                                       kunag = CONV kunag( |{ lt_partner_std[  partner_pft =  '0001' ]-external_partner_number ALPHA = IN }| )
                                                                       kurst = ls_head_db-kurst
                                                                       xblnr = is_head-ref_doc_no ).

* In case of cancellation, we would need value date to determine fiscal year for original acc. document
                  IF is_head-obj_key_r IS NOT INITIAL.
                    DATA(lr_bdh_guid) = VALUE beart_bdh_guid( ).
                    SELECT transfer_date
                      FROM /1bea/crmb_bdh
                      UP TO 1 ROWS
                      INTO ls_billing_header-bill_transfer_dt
                      WHERE bdh_guid IN lr_bdh_guid
                      AND headno_ext = is_head-obj_key_r+0(10).
                    ENDSELECT.
                    IF sy-subrc NE 0.
                      CLEAR ls_billing_header-bill_transfer_dt.
                    ENDIF.
                  ENDIF.

                  ct_bapicrmparex[] = VALUE #( BASE ct_bapicrmparex[] ( be_head_no = cs_acc_achead-be_head_no
                                                                        structure = lc_billh_struct
                                                                        valuepart1 = CONV valuepart( ls_billing_header ) ) ).
                ENDIF.

* Populate all item value and pass it to the extension
                TRY .
                    DATA(lv_bus_area) = VALUE #( ct_acc_acgl09[ 1 ]-bus_area ).
                  CATCH cx_sy_itab_line_not_found.
                    CLEAR lv_bus_area.
                ENDTRY.

                DATA(ls_billing_item) = VALUE zbilling_i( vbeln = CONV #( |{ CONV vbeln_vf( |{ is_head-be_head_no ALPHA = OUT }| ) ALPHA = IN }| )
                                                          posnr = CONV posnr( is_item-be_item_no )
                                                          fkimg = shift_left( CONV char17( is_item-inv_qty ) )
                                                          meins = is_item-sales_unit
                                                          gsber = lv_bus_area
                                                          netwr = shift_left( CONV char21( ls_crmb_bdi_pr-net_value ) )
                                                          matnr = CONV matnr( ls_crmb_bdi_pr-product_id )
                                                          arktx = is_item-item_text ).

                ct_bapicrmparex[] = VALUE #( BASE ct_bapicrmparex[] ( be_head_no = cs_acc_achead-be_head_no
                                                                      structure = lc_billi_struct
                                                                      valuepart1 = CONV valuepart( ls_billing_item ) ) ).
              ENDIF.
              CLEAR : ls_billing_header,
                      ls_billing_item,
                      ls_billing_cond.
            ENDIF.
    ENDIF.

We have BADI BADI_ACC_DOCUMENT and method : IF_EX_ACC_DOCUMENT~CHANGE (with below filter condition) in ECC which will process further to create accounting document.

Filter Value: 

Value 1 : BEBD   Comparator 1 : = (Equal)   Filter : AWTYP

CONSTANTS : lc_msgtyp_e       TYPE bapi_mtype VALUE 'E',
              lc_msgtyp_a       TYPE bapi_mtype VALUE 'A',
              lc_aworg_crmb     TYPE aworg VALUE 'CRMB',
              lc_msgid          TYPE symsgid VALUE 'ZMSG',
              lc_msgno          TYPE symsgno VALUE '000',
              lc_billh_struct   TYPE te_struc VALUE 'ZBILLING_H',
              lc_billi_struct   TYPE te_struc VALUE 'ZBILLING_I',
              lc_billcnd_struct TYPE te_struc VALUE 'ZBILLING_CND'.

  TYPES : ty_tt_bitem TYPE STANDARD TABLE OF zfp0_billing_item WITH DEFAULT KEY,
          ty_tt_bcond TYPE STANDARD TABLE OF zfp0_billing_cond WITH DEFAULT KEY.

  DATA: lt_billing_item TYPE STANDARD TABLE OF zfp0_billing_item,
        lt_billing_cond TYPE STANDARD TABLE OF zfp0_billing_cond,
        lt_return       TYPE STANDARD TABLE OF bapiret2.

* Execution would be Only for CRM invoices
  IF c_acchd-aworg EQ lc_aworg_crmb.

* Populate Billing Header, Item and conditions values from CRM
    LOOP AT c_extension2 ASSIGNING FIELD-SYMBOL(<fs_extension2>).
      CASE <fs_extension2>-structure.
        WHEN lc_billh_struct.
          DATA(ls_billing_header) = CONV zbilling_h( <fs_extension2>-valuepart1 ).
        WHEN lc_billi_struct.
          lt_billing_item = VALUE ty_tt_bitem( BASE lt_billing_item ( CONV zbilling_i( <fs_extension2>-valuepart1 ) ) ).
        WHEN lc_billcnd_struct.
          lt_billing_cond = VALUE ty_tt_bcond( BASE lt_billing_cond ( CONV zbilling_cnd( <fs_extension2>-valuepart1 ) ) ).
      ENDCASE.
    ENDLOOP.

    IF ls_billing_header IS NOT INITIAL.
      DATA(lt_vbrp) = CORRESPONDING vbrpvb_t( lt_billing_item[] ).
     LOOP AT c_accit ASSIGNING FIELD-SYMBOL(<fs_accit>) WHERE werks IS NOT INITIAL.
        DATA(ls_vbrp) = VALUE vbrpvb( werks = <fs_accit>-werks ).
        MODIFY lt_vbrp FROM ls_vbrp TRANSPORTING werks WHERE werks IS INITIAL.
        EXIT.
      ENDLOOP.

      DATA(lt_komv) = CORRESPONDING komv_t( lt_billing_cond[] ).

* Delete Extension fields after collecting in local internal table
* which would prevent execution of logic in case if method gets triggered again
      DELETE c_extension2 WHERE structure EQ lc_billh_struct
                             OR structure EQ lc_billi_struct
                             OR structure EQ lc_billcnd_struct.

* Store all the changing paramter locally before calling below FM,
* Internally below FM would check and call ACCOUNTING DOCUMENT creation logic
* It removes the existing record and updates with the new details, which further create an error for Normal accounting posting
      DATA(ls_acchd) = CORRESPONDING acchd( c_acchd ).
      DATA(lt_accit) = CORRESPONDING accit_tab( c_accit ).
      DATA(lt_acccr) = CORRESPONDING acccr_tab( c_acccr ).
      DATA(lt_accwt) = CORRESPONDING accwt_tab( c_accwt ).
      DATA(lt_acctx) = CORRESPONDING acctx_tab( c_acctx ).
      DATA(lt_accfi) = CORRESPONDING accfi_t( c_accfi ).

      CALL FUNCTION 'ZACCOUNT_DOC'
        EXPORTING
          is_vbrk         = ls_billing_header
          iv_billto_cntry = ls_billing_header-billto_cntry
          iv_simulation   = abap_true
        TABLES
          ct_cvbrp        = lt_vbrp
          ct_ckomv        = lt_komv
          ct_return       = lt_return.

      IF ( line_exists( lt_return[ type = lc_msgtyp_e ] ) OR line_exists( lt_return[ type = lc_msgtyp_a ] ) ).
* Append newly generated error in exisitng RETURN parameter
        c_return[] = VALUE #( BASE c_return[] ( LINES OF lt_return ) ( type = lc_msgtyp_e
                                                                       id = lc_msgid
                                                                       number = lc_msgno ) ).
        CLEAR lt_return.

      ELSE.
* Call FM in update task to avoid confustion between creation of two accounting documents
* One with standard one and other additional document.
        CLEAR lt_return.
        CALL FUNCTION 'ZACCOUNT_DOC' IN UPDATE TASK
          EXPORTING
            is_vbrk         = ls_billing_header
            iv_billto_cntry = ls_billing_header-billto_cntry
            iv_simulation   = abap_false
          TABLES
            ct_cvbrp        = lt_vbrp
            ct_ckomv        = lt_komv
            ct_return       = lt_return.
      ENDIF.

* Fillup all the parameters stored locally befor FM call to proceed expected standard functionality
      c_acchd = CORRESPONDING acchd( ls_acchd ).
      c_accit = CORRESPONDING accit_tab( lt_accit ).
      c_acccr = CORRESPONDING acccr_tab( lt_acccr ).
      c_accwt = CORRESPONDING accwt_tab( lt_accwt ).
      c_acctx = CORRESPONDING acctx_tab( lt_acctx ).
      c_accfi = CORRESPONDING accfi_t( lt_accfi ).

    ENDIF.
  ENDIF.

Function module, ZACCOUNT_DOC will call internally BAPI_ACC_DOCUMENT _CHECK in simulation mode and BAPI_ACC_DOCUMENT_POST if needs to create a real document with the required information.

No comments:

Post a Comment