Introduction:
In this blog post, I would like to share knowledge on usage of Functions in ABAP RAP and then default few fields while creating a new business object in “Unmanaged scenario without Draft”. It is a common requirement in most of the applications to default some fields to save end user time or routine work.
Goal:
- Understanding and Developing functions in ABAP RAP
- Defaulting fields while creating new business object
Prerequisites:
Basic knowledge on ABAP Restful Application Programming. if this is something new, then go through OpenSAP course Building Apps with the ABAP RESTful Application Programming Model
What are the functions?
Functions are specified as nonstandard operations in behavior definitions and are implemented in the ABAP behavior pool class. Function cannot change the state of the object while “Action” can influence.
Business example:
In this example, we are going to create a purchase contract and default some of the fields
- Buyer Id: With logged in user (or “Manager id“ if logged in user is an assistant)
- Company Code: With company code details from his/her attributes (or additional custom logic)
- Financial year: With current financial year
- Created By: Logged in user
- Valid From: Today’s date (to be discussed in next blog post)
- Valid To: 1 year, Today’s date + 364 days (to be discussed in next blog post)
Steps:
- Create a table with underlying fields
@EndUserText.label : 'Purchase Contract' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zrk_t_pur_con { key client : abap.clnt not null; key con_uuid : sysuuid_x16 not null; object_id : zrk_pur_con_id; description : zrk_description; buyer : zrk_buyer_id; supplier : zrk_sup_no; sup_con_id : zrk_sup_con_id; comp_code : zrk_company_code; @Semantics.amount.currencyCode : 'zrk_t_pur_con.currency' target_value : abap.curr(10,2); currency : abap.cuky; stat_code : zrk_stat_code; fiscl_year : zrk_fiscal_year; valid_from : zrk_valid_from; valid_to : zrk_valid_to; created_by : abp_creation_user; created_at : abp_creation_tstmpl; last_changed_by : abp_locinst_lastchange_user; last_changed_at : abp_lastchange_tstmpl; locl_last_changed_at : abp_locinst_lastchange_tstmpl; }
- Create an interface view entity for data modeling
@AccessControl.authorizationCheck: #CHECK @EndUserText.label: 'ID for purchase contract' define root view entity Zrk_I_Pur_Con as select from zrk_t_pur_con { key con_uuid as ConUuid, object_id as ObjectId, description as Description, buyer as Buyer, supplier as Supplier, sup_con_id as SupConId, comp_code as CompCode, stat_code as StatCode, target_value as TargetValue, currency as Currency, valid_from as ValidFrom, valid_to as ValidTo, fiscl_year as FiscalYear, created_by as CreatedBy, created_at as CreatedAt, last_changed_by as LastChangedBy, last_changed_at as LastChangedAt, locl_last_changed_at as LoclLastChangedAt }
- Create a projection view entity to expose in the UI service
@EndUserText.label: 'Projection for purchase contract' @AccessControl.authorizationCheck: #CHECK @Metadata.allowExtensions: true define root view entity ZRK_C_PUR_CON as projection on Zrk_I_Pur_Con { key ConUuid, ObjectId, Description, Buyer, Supplier, SupConId, CompCode, StatCode, TargetValue, @Consumption.valueHelpDefinition: [{ entity: { name: 'I_Currency', element: 'Currency' }, useForValidation: true, label: 'Select currency from Dailog' }] Currency, ValidFrom, ValidTo, FiscalYear, CreatedBy, CreatedAt, LastChangedBy, LastChangedAt, LoclLastChangedAt }
- Enrich UI with metadata extension
@Metadata.layer: #CORE @UI: { headerInfo: { typeName: 'Purchase Contract', typeNamePlural: 'Purchase Contracts', description: { type: #STANDARD, value: 'Description' }, title: { // type: #STANDARD, value: 'ObjectId' } } } annotate entity ZRK_C_PUR_CON with { @UI.facet: [ { id: 'Header', type: #HEADERINFO_REFERENCE, label: 'Header', purpose: #HEADER, position: 10, targetQualifier: 'Header' }, { id: 'General', type: #IDENTIFICATION_REFERENCE, purpose: #STANDARD, label: 'General', position: 20 , targetQualifier: 'General'}, { id: 'Validities', type: #IDENTIFICATION_REFERENCE, label: 'Validities', position: 30 , targetQualifier: 'Validities'} ] @UI.hidden: true ConUuid; @UI:{ lineItem: [{ position: 10 }] , identification: [{ position: 10 , qualifier: 'Header'}]} ObjectId; @UI:{ lineItem: [{ position: 20 }] , identification: [{ position: 20 , qualifier: 'Header'}]} Description; @UI:{ lineItem: [{ position: 30 }] , identification: [{ position: 30 , qualifier: 'General'}]} @Consumption.valueHelpDefinition: [{ entity: { name: 'ZRK_I_BUYER', element: 'BuyerId' } }] Buyer; @UI:{ lineItem: [{ position: 40 }] , identification: [{ position: 40 ,qualifier: 'General'}]} @Consumption.valueHelpDefinition: [{ entity: { name: 'ZRK_I_SUPPLIER', element: 'SupNo' } , useForValidation: true }] Supplier; @UI:{ lineItem: [{ position: 50 }] , identification: [{ position: 50 ,qualifier: 'General'}]} @Consumption.valueHelpDefinition: [{ entity: { name: 'ZRK_I_SUP_CON', element: 'SupConId' } , additionalBinding: [{ localElement: 'Supplier', localConstant: '', element: 'SupNo', usage: #FILTER_AND_RESULT }] , useForValidation: true }] SupConId; @UI:{ lineItem: [{ position: 50 }] , identification: [{ position: 55 ,qualifier: 'General'}]} @Consumption.valueHelpDefinition: [{ entity: { name: 'ZRK_I_COMP_CODE', element: 'CompCode' } , useForValidation: true }] CompCode; @UI:{ lineItem: [{ position: 60 }] , identification: [{ position: 60 ,qualifier: 'Header'}]} StatCode; @UI:{ lineItem: [{ position: 70 }] , identification: [{ position: 70 , qualifier: 'Validities' }]} ValidFrom; @UI:{ lineItem: [{ position: 80 }] , identification: [{ position: 80 , qualifier: 'Validities' }]} ValidTo; @UI:{ lineItem: [{ position: 80 }] , identification: [{ position: 90 , qualifier: 'Validities' }]} @Consumption.valueHelpDefinition: [{ entity: { name: 'ZRK_I_FISCAL_YEAR', element: 'fiscal_year' } }] FiscalYear; @UI:{ lineItem: [{ position: 50 }] , identification: [{ position: 57 ,qualifier: 'General' , label: 'Target value'}]} TargetValue; @UI:{ lineItem: [{ position: 90 }] , identification: [{ position: 100, qualifier: 'General' , label: 'Created By' }]} CreatedBy; @UI.hidden: true CreatedAt; @UI.hidden: true LastChangedBy; @UI.hidden: true LastChangedAt; @UI.hidden: true LoclLastChangedAt; }
- Create a behavior definition as “Unmanaged implementation without Draft”
- Declare characteristics such as mandatory, numbering etc
- Declare a static function “DefaultForCreate” (scope of this blog post) with returning parameter as $self.
- Explanation: We are declaring the function as static since we don’t need any inputs from UI at this moment.
unmanaged implementation in class zbp_rk_i_pur_con unique; strict; define behavior for Zrk_I_Pur_Con alias PurCon late numbering lock master authorization master ( instance ) etag master LoclLastChangedAt { create; update; delete; field ( mandatory ) Buyer, Supplier, SupConId ; static function DefaultForCreate result [1] $self; }
- Explanation: We are declaring the function as static since we don’t need any inputs from UI at this moment.
- Implement behavior pool class
- Implement the method generated for given function
- Explanation: This has exporting parameter “result” and it has 2 properties
- %cid: we have to return the same value that we received in keys
- %param: This is a dynamic structure based on return parameter defined
- Business Logic
METHOD DefaultForCreate. DATA : lt_pur_con TYPE TABLE FOR READ RESULT zrk_i_pur_con\purcon . APPEND INITIAL LINE TO lt_pur_con ASSIGNING FIELD-SYMBOL(<fs_new_con>). <fs_new_con>-conuuid = cl_system_uuid=>create_uuid_x16_static( ). <fs_new_con>-Description = 'Defaulted from backend'. <fs_new_con>-ValidFrom = cl_abap_context_info=>get_system_date( ). <fs_new_con>-ValidTo = <fs_new_con>-ValidFrom + 364. <fs_new_con>-Buyer = sy-uname. <fs_new_con>-CreatedBy = sy-uname. " Wrapper logic to default company code based on logged in user <fs_new_con>-CompCode = zrk_cl_mng_pur_con=>get_defaults_for_create( )-comp_code. DATA(lv_date) = cl_abap_context_info=>get_system_date( ). <fs_new_con>-FiscalYear = lv_date+0(4). result = VALUE #( FOR <fs_rec_m> IN lt_pur_con ( %cid = keys[ 1 ]-%cid %param = <fs_rec_m> ) ) . ENDMETHOD.
- Explanation: This has exporting parameter “result” and it has 2 properties
- Implement the method generated for given function
- Generate the service Definition and Binding
@EndUserText.label: 'Service definition forZRK_C_PUR_CON_H' define service ZRK_UI_PUR_CON { expose ZRK_C_PUR_CON_H as PCHead; }
- Preview the application and check the metadata for following
<FunctionImport Name="DefaultForCreate" ReturnType="cds_zrk_ui_pur_con_u.PurConType" EntitySet="PurCon" m:HttpMethod="GET"/>
- You might notice that the fields are not defaulted on “Create”. We need to implement an annotation in Fiori Elements app with WebIDE/BAS.
Annotation Term=”Common.DefaultValuesFunction” fires a backend call that triggers the function “DefaultForCreate“
<Annotations Target="cds_zrk_ui_pur_con_u.cds_zrk_ui_pur_con_u_Entities/PurCon" > <Annotation Term="Common.DefaultValuesFunction" String="DefaultForCreate"> </Annotation> </Annotations>
- Save and preview the generated application and see the fields defaulted.
- See the response from backend in network calls.
Conclusion
Job is done and we understand what the functions are and how to implement them.
We can play around further logic to enhance this feature.