Enterprise Resource Planning Blogs by SAP
Get insights and updates about cloud ERP and RISE with SAP, SAP S/4HANA and SAP S/4HANA Cloud, and more enterprise management capabilities with SAP blog posts.
cancel
Showing results for 
Search instead for 
Did you mean: 
Business Object Journal Entry (I_JournalEntryTP) is available in S/4 HANA Cloud environment. It provides operations:

  • Post

  • Validate

  • Reverse

  • Change


You can try to build your own 'Posting' Fiori application by creating custom RAP business object. The posting can be achieved by calling the 'Post' action in Journal Entry object.

You can find an example in the documentation on how to call the 'Post' action from local program or class . This blog focus on how it work when calling within an RAP behavior class.

This blog will show a sample demo. Current it only support operations 'Create' & 'Delete'. Please note: This demo is only a demonstration of the technology and does not provide any guidance for the business.

Currently extensibility is not support in I_JournalEntryTP. The implementation is ongoing and will available soon.

Highlights:

  1.  I_JournalEntryTP~Post is defined as Save Action in RAP which can only be called during a specified RAP saver mehtod. Otherwise leads to a short dump.

  2. The allowed name of RAP save method is specified as finalize, adjustnumbers ( check the definition of underlying BO R_JournalEntryTP). It can be called from a finalize (determination on save in case Managed RAP BO ) or adjust_numbers (Late numbering)  RAP saver method. In this example, post action is called in the determination ‘Post’.

  3. The Journal entry final key can be retrieved by using CONVERT KEY which only allowed to be called during RAP save method(late save phase).

  4. It is suggested to call the Validate before Post action. Error message should raised in early phase.

  5. Please choose Odata V4 - UI as the service binding type ( the last step in below). You will encounter error 'At create the mandatory element 'BUZEI' of entity 'ZC_GeneralJournalEntryItem' was not provided' in case type Odata V2 - UI is selected.


 

The Fiori application will look like:



Video Demo

 


Worklist



Input Transactional Data



Save


 

Steps:

  1. Create transaction & draft tables

    1. Create header table.
      @EndUserText.label : 'Journal Entry Header'
      @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
      @AbapCatalog.tableCategory : #TRANSPARENT
      @AbapCatalog.deliveryClass : #A
      @AbapCatalog.dataMaintenance : #RESTRICTED
      define table zgje_header {

      key client : abap.clnt not null;
      key uuid : sysuuid_x16 not null;
      bukrs : bukrs;
      gjahr : gjahr;
      belnr : belnr_d;
      waers : waers;
      bldat : bldat;
      budat : budat;
      bktxt : bktxt;
      created_by : abp_creation_user;
      created_at : abp_creation_tstmpl;
      last_changed_by : abp_lastchange_user;
      last_changed_at : abp_lastchange_tstmpl;
      local_last_changed_at : abp_locinst_lastchange_tstmpl;

      }​


    2. Create item table
      @EndUserText.label : 'Journal Entry Item'
      @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
      @AbapCatalog.tableCategory : #TRANSPARENT
      @AbapCatalog.deliveryClass : #A
      @AbapCatalog.dataMaintenance : #RESTRICTED
      define table zgje_item {

      key client : abap.clnt not null;
      key uuid : sysuuid_x16 not null;
      key buzei : buzei not null;
      @Semantics.amount.currencyCode : 'zgje_header.waers'
      wrbtr : fins_vwcur12;
      sgtxt : sgtxt;
      hkont : hkont;
      kostl : kostl;
      prctr : prctr;
      item_created_by : abp_creation_user;
      item_created_at : abp_creation_tstmpl;
      item_last_changed_by : abp_lastchange_user;
      item_last_changed_at : abp_lastchange_tstmpl;
      item_local_last_changed_at : abp_locinst_lastchange_tstmpl;

      }​


    3. Create draft table for header
      @EndUserText.label : 'Journal Entry Header Draft Table'
      @AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
      @AbapCatalog.tableCategory : #TRANSPARENT
      @AbapCatalog.deliveryClass : #A
      @AbapCatalog.dataMaintenance : #RESTRICTED
      define table zgje_header_d {

      key mandt : mandt not null;
      key uuid : sysuuid_x16 not null;
      belnr : belnr_d;
      bukrs : bukrs;
      gjahr : gjahr;
      waers : waers;
      bldat : bldat;
      budat : budat;
      bktxt : bktxt;
      created_by : abp_creation_user;
      created_at : abp_creation_tstmpl;
      last_changed_by : abp_lastchange_user;
      last_changed_at : abp_lastchange_tstmpl;
      local_last_changed_at : abp_locinst_lastchange_tstmpl;
      "%admin" : include sych_bdl_draft_admin_inc;

      }​


    4. Create draft table for item
      @EndUserText.label : 'Journal Entry Item Draft'
      @AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
      @AbapCatalog.tableCategory : #TRANSPARENT
      @AbapCatalog.deliveryClass : #A
      @AbapCatalog.dataMaintenance : #RESTRICTED
      define table zgje_item_d {

      key mandt : mandt not null;
      key uuid : sysuuid_x16 not null;
      key buzei : buzei not null;
      @Semantics.amount.currencyCode : 'zgje_item_d.waers'
      wrbtr : fins_vwcur12;
      waers : waers;
      sgtxt : sgtxt;
      hkont : hkont;
      kostl : kostl;
      prctr : prctr;
      item_created_by : abp_creation_user;
      item_created_at : abp_creation_tstmpl;
      item_last_changed_by : abp_lastchange_user;
      item_last_changed_at : abp_lastchange_tstmpl;
      "%admin" : include sych_bdl_draft_admin_inc;

      }​




  2. Define business object structure

    1. Header root view.
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: 'GL Journal Entry'
      define root view entity ZR_GeneralJournalEntry
      as select from zgje_header
      composition [0..*] of ZR_GeneralJournalEntryItem as _Item
      {
      key uuid as Uuid,
      belnr as belnr,
      bukrs as Bukrs,
      gjahr as Gjahr,
      waers as Waers,
      bldat as Bldat,
      budat as Budat,
      bktxt as Bktxt,
      @Semantics.user.createdBy: true
      created_by as Created_By,
      @Semantics.systemDateTime.createdAt: true
      created_at as Created_At,
      @Semantics.user.lastChangedBy: true
      last_changed_by as Last_Changed_By,
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at as Last_Changed_At,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      local_last_changed_at as Local_Last_Changed_At,
      _Item

      }


    2. Item child view
      @AbapCatalog.viewEnhancementCategory: [#NONE]
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: 'GL Journal Entry Item'
      @Metadata.ignorePropagatedAnnotations: true
      @ObjectModel.usageType:{
      serviceQuality: #X,
      sizeCategory: #S,
      dataClass: #MIXED
      }
      define view entity ZR_GeneralJournalEntryItem
      as select from zgje_item
      association to parent ZR_GeneralJournalEntry as _Header on $projection.Uuid = _Header.Uuid
      {
      key uuid as Uuid,
      key buzei as Buzei,
      @Semantics.amount.currencyCode: 'Waers'
      wrbtr as Wrbtr,
      _Header.Waers as Waers,
      sgtxt as sgtxt,
      hkont as hkont,
      kostl as Kostl,
      prctr as Prctr,
      @Semantics.user.createdBy: true
      item_created_by as Item_Created_By,
      @Semantics.systemDateTime.createdAt: true
      item_created_at as Item_Created_At,
      @Semantics.user.lastChangedBy: true
      item_last_changed_by as Item_Last_Changed_By,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      item_last_changed_at as Item_Last_Changed_At,
      _Header
      }




  3. Create class for local transactional buffer.which will be used in behavior pool implementation.
    CLASS zgje_transaction_handler DEFINITION
    PUBLIC
    FINAL
    CREATE PUBLIC .

    PUBLIC SECTION.

    CLASS-DATA: go_instance TYPE REF TO zgje_transaction_handler.

    CLASS-METHODS: get_instance RETURNING VALUE(result) TYPE REF TO zgje_transaction_handler.

    TYPES: BEGIN OF ty_temp_key,
    cid TYPE abp_behv_cid,
    pid TYPE abp_behv_pid,
    END OF ty_temp_key,
    tt_temp_key TYPE STANDARD TABLE OF ty_temp_key WITH DEFAULT KEY,
    BEGIN OF ty_final_key,
    cid TYPE abp_behv_cid,
    bukrs TYPE bukrs,
    belnr TYPE belnr_d,
    gjahr TYPE gjahr,
    END OF ty_final_key,
    tt_final_key type STANDARD TABLE OF ty_final_key WITH DEFAULT KEY,
    tt_header TYPE STANDARD TABLE OF zgje_header WITH DEFAULT KEY.


    DATA: temp_key TYPE tt_temp_key.


    METHODS: set_temp_key IMPORTING it_temp_key TYPE tt_temp_key,
    convert_temp_to_final RETURNING VALUE(result) TYPE tt_final_key,
    additional_save IMPORTING it_create type tt_header
    it_delete TYPE tt_header,
    clean_up.

    PROTECTED SECTION.
    PRIVATE SECTION.
    ENDCLASS.



    CLASS zgje_transaction_handler IMPLEMENTATION.
    METHOD get_instance.
    IF go_instance IS NOT BOUND.
    go_instance = NEW #( ).
    ENDIF.
    result = go_instance.
    ENDMETHOD.

    METHOD additional_save.

    data: lt_create type TABLE of zgje_header.

    DATA(lt_je_key) = convert_temp_to_final( ).

    loop at it_create into data(ls_create).
    read TABLE lt_je_key into data(ls_je_key) with key cid = ls_create-uuid.
    if sy-subrc = 0.
    ls_create-belnr = ls_je_key-belnr.
    append ls_create to lt_create.
    endif.
    ENDLOOP.

    IF lt_create IS NOT INITIAL.
    INSERT zgje_header FROM TABLE @lt_create.
    ENDIF.

    IF it_delete IS NOT INITIAL.
    DELETE zgje_header FROM TABLE @it_delete.
    ENDIF.

    ENDMETHOD.

    METHOD clean_up.
    CLEAR temp_key.
    ENDMETHOD.

    METHOD convert_temp_to_final.
    DATA: ls_final_key TYPE ty_final_key.
    IF temp_key IS NOT INITIAL.
    LOOP AT temp_key INTO DATA(ls_temp_key).
    CONVERT KEY OF i_journalentrytp
    FROM ls_temp_key-pid
    TO FINAL(lv_root_key).

    ls_final_key-cid = ls_temp_key-cid.
    ls_final_key-bukrs = lv_root_key-companycode.
    ls_final_key-belnr = lv_root_key-accountingdocument.
    ls_final_key-gjahr = lv_root_key-fiscalyear.

    APPEND ls_final_key TO result.
    ENDLOOP.
    ENDIF.
    ENDMETHOD.

    METHOD set_temp_key.
    temp_key = it_temp_key.
    ENDMETHOD.

    ENDCLASS.


  4. Define behavior
    managed with additional save
    implementation in class zbp_r_generaljournalentry unique;
    strict ( 2 );
    with draft;

    define behavior for ZR_GeneralJournalEntry //alias <alias_name>
    //persistent table zgje_header
    with unmanaged save
    draft table zgje_header_d
    lock master
    total etag Last_Changed_At
    authorization master ( instance )
    etag master Local_Last_Changed_At
    {
    create;
    update ;
    delete ;
    association _Item { create; with draft; }
    draft action Resume;
    draft action Edit;
    draft action Activate optimized;
    draft action Discard;
    draft determine action Prepare;

    determination Post on save {create;}
    mapping for zgje_header corresponding;
    field ( readonly, numbering : managed ) Uuid;
    field ( readonly ) belnr;
    }

    define behavior for ZR_GeneralJournalEntryItem //alias <alias_name>
    persistent table zgje_item
    draft table zgje_item_d
    lock dependent by _Header
    authorization dependent by _Header
    etag master Item_Last_Changed_At
    {
    update ;
    delete ;
    field ( readonly ) Uuid;
    field ( mandatory: create,readonly : update ) Buzei;
    association _Header { with draft; }
    mapping for zgje_item corresponding;
    }​


  5. Use ADT tool to generate behavior pool calss. Double click highlight code.

  6. Implementation of behavior pool (local type).
    CLASS lhc_zr_generaljournalentry DEFINITION INHERITING FROM cl_abap_behavior_handler.
    PRIVATE SECTION.

    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
    IMPORTING keys REQUEST requested_authorizations FOR zr_generaljournalentry RESULT result.

    METHODS post FOR DETERMINE ON SAVE
    IMPORTING keys FOR zr_generaljournalentry~post.

    ENDCLASS.

    CLASS lhc_zr_generaljournalentry IMPLEMENTATION.

    METHOD get_instance_authorizations.
    ENDMETHOD.

    METHOD post.

    DATA: lt_entry TYPE TABLE FOR ACTION IMPORT i_journalentrytp~post,
    ls_entry LIKE LINE OF lt_entry,
    ls_glitem LIKE LINE OF ls_entry-%param-_glitems,
    ls_amount LIKE LINE OF ls_glitem-_currencyamount,
    lt_temp_key TYPE zgje_transaction_handler=>tt_temp_key,
    ls_temp_key LIKE LINE OF lt_temp_key.

    READ ENTITIES OF zr_generaljournalentry IN LOCAL MODE
    ENTITY zr_generaljournalentry ALL FIELDS WITH CORRESPONDING #( keys ) RESULT FINAL(header)
    ENTITY zr_generaljournalentry BY \_item ALL FIELDS WITH CORRESPONDING #( keys ) RESULT FINAL(item).

    "start to call I_JournalEntryTP~Post
    LOOP AT header REFERENCE INTO DATA(ls_header).
    CLEAR ls_entry.
    ls_entry-%cid = ls_header->uuid. "use UUID as CID
    ls_entry-%param-companycode = ls_header->bukrs.
    ls_entry-%param-businesstransactiontype = 'RFPO'.
    ls_entry-%param-accountingdocumenttype = 'AB'.
    ls_entry-%param-accountingdocumentheadertext = ls_header->bktxt.
    ls_entry-%param-documentdate = ls_header->bldat.
    ls_entry-%param-postingdate = ls_header->budat.
    ls_entry-%param-createdbyuser = ls_header->created_by.

    LOOP AT item REFERENCE INTO DATA(ls_item) USING KEY entity WHERE uuid = ls_header->uuid.
    CLEAR ls_glitem.
    ls_glitem-glaccountlineitem = ls_item->%data-buzei.
    ls_glitem-glaccount = ls_item->%data-hkont.
    ls_glitem-costcenter = ls_item->%data-kostl.
    ls_glitem-profitcenter = ls_item->%data-prctr.
    ls_glitem-documentitemtext = ls_item->%data-sgtxt.

    CLEAR ls_amount.
    ls_amount-currencyrole = '00'.
    ls_amount-currency = ls_header->waers.
    ls_amount-journalentryitemamount = ls_item->%data-wrbtr.
    APPEND ls_amount TO ls_glitem-_currencyamount.
    APPEND ls_glitem TO ls_entry-%param-_glitems.
    ENDLOOP.
    APPEND ls_entry TO lt_entry.
    ENDLOOP.

    IF lt_entry IS NOT INITIAL.
    MODIFY ENTITIES OF i_journalentrytp
    ENTITY journalentry
    EXECUTE post FROM lt_entry
    MAPPED FINAL(ls_post_mapped)
    FAILED FINAL(ls_post_failed)
    REPORTED FINAL(ls_post_reported).

    IF ls_post_failed IS NOT INITIAL.
    LOOP AT ls_post_reported-journalentry INTO DATA(ls_report).
    APPEND VALUE #( uuid = ls_report-%cid
    %create = if_abap_behv=>mk-on
    %is_draft = if_abap_behv=>mk-on
    %msg = ls_report-%msg ) TO reported-zr_generaljournalentry.
    ENDLOOP.
    ENDIF.

    LOOP AT ls_post_mapped-journalentry INTO DATA(ls_je_mapped).
    ls_temp_key-cid = ls_je_mapped-%cid.
    ls_temp_key-pid = ls_je_mapped-%pid.
    APPEND ls_temp_key TO lt_temp_key.
    ENDLOOP.

    ENDIF.

    zgje_transaction_handler=>get_instance( )->set_temp_key( lt_temp_key ).

    ENDMETHOD.


    ENDCLASS.

    CLASS lsc_zr_generaljournalentry DEFINITION INHERITING FROM cl_abap_behavior_saver.
    PROTECTED SECTION.

    METHODS save_modified REDEFINITION.

    METHODS cleanup_finalize REDEFINITION.

    ENDCLASS.

    CLASS lsc_zr_generaljournalentry IMPLEMENTATION.

    METHOD save_modified.
    "unmanaged save for table ZGJE_HEADER
    DATA: Lt_create type TABLE of ZGJE_HEADER,
    lt_delete type TABLE of ZGJE_HEADER.


    lt_create = CORRESPONDING #( create-zr_generaljournalentry mapping from entity ).
    lt_delete = CORRESPONDING #( delete-zr_generaljournalentry mapping from entity ).
    zgje_transaction_handler=>get_instance( )->additional_save( it_create = lt_create
    it_delete = lt_delete ).

    ENDMETHOD.

    METHOD cleanup_finalize.
    zgje_transaction_handler=>get_instance( )->clean_up( ).
    ENDMETHOD.

    ENDCLASS.​


  7. Create projection business object.

    1. Header projection root view
      @EndUserText.label: 'GL Journal Entry Projection'
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @Metadata.allowExtensions: true
      define root view entity ZC_GeneralJournalEntry
      provider contract transactional_query
      as projection on ZR_GeneralJournalEntry
      {
      key Uuid,
      belnr,
      Bukrs,
      Gjahr,
      Waers,
      Bldat,
      Budat,
      Bktxt,
      @Semantics.user.createdBy: true
      Created_By,
      @Semantics.systemDateTime.createdAt: true
      Created_At,
      @Semantics.user.lastChangedBy: true
      Last_Changed_By,
      @Semantics.systemDateTime.lastChangedAt: true
      Last_Changed_At,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      Local_Last_Changed_At,
      _Item : redirected to composition child ZC_GeneralJournalEntryItem
      }


    2. Item projection view.
      @EndUserText.label: 'GL Journal Item Projection'
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @Metadata.allowExtensions: true
      define view entity ZC_GeneralJournalEntryItem
      as projection on ZR_GeneralJournalEntryItem
      {
      key Uuid,
      key Buzei,
      @Semantics.amount.currencyCode: 'Waers'
      Wrbtr,
      Waers,
      sgtxt,
      hkont,
      Kostl,
      Prctr,
      @Semantics.user.createdBy: true
      Item_Created_By,
      @Semantics.systemDateTime.createdAt: true
      Item_Created_At,
      @Semantics.user.lastChangedBy: true
      Item_Last_Changed_By,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      Item_Last_Changed_At,
      _Header : redirected to parent ZC_GeneralJournalEntry
      }




  8. Create metadata extension

    1. Metadata extension for header projection view
      @Metadata.layer: #CUSTOMER
      annotate view ZC_GeneralJournalEntry with
      {
      @UI.facet: [ { id: 'Header',
      purpose: #STANDARD,
      type: #IDENTIFICATION_REFERENCE,
      label: 'Entry',
      position: 10 } ,
      { id: 'Items',
      purpose: #STANDARD,
      type: #LINEITEM_REFERENCE,
      label: 'Items',
      position: 20,
      targetElement: '_Item'}]

      // @UI.identification: [{ position: 10 }]
      // Uuid;
      @Consumption.valueHelpDefinition: [{ entity: { name:'I_CompanyCode', element: 'CompanyCode' } }]
      @UI: { lineItem: [{ position: 10}],selectionField: [{ position: 10 }],identification: [{ position: 20 }] }
      Bukrs;
      @UI: { lineItem: [{ position: 20}],selectionField: [{ position: 20 }],identification: [{ position: 30 }] }
      Gjahr;
      @UI: { lineItem: [{ position: 30}],selectionField: [{ position: 30 }],identification: [{ position: 40 }] }
      @Consumption.semanticObject: 'AccountingDocument'
      belnr;
      @Consumption.valueHelpDefinition: [{ entity: { name:'I_Currency', element: 'Currency' } }]
      @UI: { lineItem: [{ position: 40}],selectionField: [{ position: 40 }],identification: [{ position: 50 }] }
      Waers;
      @UI: { lineItem: [{ position: 50}],selectionField: [{ position: 50 }],identification: [{ position: 60 }] }
      Bldat;

      @UI: { lineItem: [{ position: 60}],selectionField: [{ position: 60 }],identification: [{ position: 70 }] }
      Budat;

      @UI: { lineItem: [{ position: 70}],identification: [{ position: 80 }] }
      Bktxt;

      }​


    2. Metadata extension for item projection view
      @Metadata.layer: #CUSTOMER
      annotate view ZC_GeneralJournalEntryItem with
      {
      @UI.facet: [ { id: 'Item',
      purpose: #STANDARD,
      type: #IDENTIFICATION_REFERENCE,
      label: 'Item',
      position: 10 } ]
      @UI: { lineItem: [{ position: 10}],identification: [{ position: 10 }] }
      Buzei;
      @UI: { lineItem: [{ position: 20}],identification: [{ position: 20 }] }
      sgtxt;
      @UI: { lineItem: [{ position: 30}],identification: [{ position: 30 }] }
      Wrbtr;
      @UI: { lineItem: [{ position: 40}],identification: [{ position: 40 }] }
      @Consumption.valueHelpDefinition: [{ entity: { name:'I_GLAccount', element: 'GLAccount' } }]
      hkont;
      @UI: { lineItem: [{ position: 50}],identification: [{ position: 50 }] }
      @Consumption.valueHelpDefinition: [{ entity: { name:'I_CostCenter', element: 'CostCenter' } }]
      Kostl;
      @UI: { lineItem: [{ position: 60}],identification: [{ position: 60 }] }
      @Consumption.valueHelpDefinition: [{ entity: { name:'I_ProfitCenterStdVH', element: 'ProfitCenter' } }]
      Prctr;

      }​




  9. Behavior projection .
    projection;
    strict ( 2 );
    use draft;

    define behavior for ZC_GeneralJournalEntry //alias <alias_name>
    {
    use create;
    use update;
    use delete;

    use action Resume;
    use action Edit;
    use action Activate;
    use action Discard;
    use action Prepare;

    use association _Item { create; with draft; }
    }

    define behavior for ZC_GeneralJournalEntryItem //alias <alias_name>
    {
    use update;
    use delete;

    use association _Header { with draft; }
    }​


  10. Right click view ZC_GeneralJournalEntry on the left tree navigation, choose 'New Service Definition'.
    @EndUserText.label: 'Manage_GL_Posting'
    define service Z_EXT_GL_POSTING {
    expose ZC_GeneralJournalEntry;
    expose ZC_GeneralJournalEntryItem;
    }​


  11. Right click service definition that created in step 10, choose 'New Service Binding'., Give a name and binding type Odata V4 - UI. Activate and publish. Use 'Preview' button to test.

15 Comments