Quantcast
Channel: SAP CRM: Webclient UI - Framework
Viewing all 188 articles
Browse latest View live

How to navigate inside a UI component

$
0
0

Dear colleagues,

 

I just published a WIKI page explaining the navigation inside a UI component. A navigation changes the current view assembly. It always leaves the current view (via an outbound plug) and goes to a different view (via an inbound plug). The possible navigations (i.e. connections from outbound plugs to inbound plugs) are part of the runtime repository of the current UI component. It is also possible to go beyond the boundaries of the current UI component. Then the navigation either leads into an embedded UI component (outside in) or it leaves to the embedding UI component (inside out). In this case it is necessary to define plugs as part of the UI component. Happy reading.

 

Best Regards,

 

Hasan


How to use Dynamic Cross Component Navigation

$
0
0

Dear colleagues,

 

I just published a WIKI page that explains different kinds of cross component navigation and creation of descriptor objects.

 

Happy reading

 

Hasan

How to Implement a Central Edit Button on an Overview Page

BPath. Lesson 10. Aggregations

$
0
0
Table of Contents ...... Table of Code Examples
<< Lesson 09. Groupingsupcoming.... >>

 

Hello everybody,

 

as indicated in the last lesson groupings and aggregations somehow belong together. So I speeded a bit up with this lesson, which is a available a bit earlier. Content will be aggregations solely, with just one small exception. Nevertheless it will be an extensive lesson.

 

First and Last and Always...Aggregations. Theory.

 

Aggregations are aggregating a number of datasets to one value. The most prominent representants here are the COUNT, SUM and the AVERAGE function. But one should not forget "easy" aggregations like MIN, MAX, FIRST or LAST. On the other side there are also a couple of more complex aggregations as SEARCH, GEOAVG and STDEV. The full list can be obtained in the table at the end of this blog post.

 

Some general remarks to the usage of aggregations:

  • All aggregations refer to a group, technically they can only be used in the Loop-Block of a group definition. Attempts to use it in other locations will cause a dump.
  • Aggregations on a full dataset (not on a group) always have to be done via $(*::....)-approach. Either in the main-query or in a sub query.
  • The initialization part is automatically done by the aggregation. Unlike the examples of the hand-made aggregations, the Initblock of the group definition will typically remain empty.
  • Aggregations can be built upon every valid BPATH expression.
  • Any aggregation which is at the intermediate state of processing row i ( smaller then the total number of rows in that group) will have the value as if i would be the last row in that group. This has the consequence that nested aggregations may return different results then anticipated. E.g. the inner statement in COUNT(@AMOUNT=MAX(@AMOUNT)), is true for all rows, which have no bigger AMOUNT in a row smaller then its row. In case you really want to count the rows which are equal to the global maximum, the used approach is not sufficient. In this case a hand-made solution has to be taken (For the sake of completeness: $(MyGroup:!t=0;!a=0:!t=iff(@AMOUNT>!a,1,!t+iff(@AMOUNT=!a,1,0);!a=iff(@AMOUNT>!t,@AMOUNT,!a))

 

Aggregations. Practice

 

Basic aggregations: Count, Sum() & Product()

 

~*[!CLASS]/SearchResFlightRel/FlightBookRel$(@CLASS::!COUNT=Count();!SUM=Sum(@LOCCURAM))
Code Example 43,$(@GROUP::!X=Count();!Y=Sum(@FELD)), Count() and Sum()

 

Well, that's easy now, isn't it? Slightly easier compared to the hand-made approach.

 

The code explains itself. Hopefully. Count can be enhanced with a condition, so only rows fullfilling the condition are counted. As with the sum() function the same can be achieved using a sum(iff(condition,@Feld,0)) construction.

 

Product() works similar to Sum() but muliplies all available parameters. Might be useful for probabilities. Since the result may be a fracture, possibility to round result is provided.

 

Advanced Aggregations: Conc, Avg() / Geoavg() and Stddev()

 

~*[!CLASS_LONG,!FORCURKEY]/SearchResFlightRel/FlightBookRel$(@@CLASS,@FORCURKEY::!Average=AVG(@LOCCURAM,2);!PASSENGERLIST=CONC(@PASSNAME,",",@PASSNAME<>""))
Code Example 44,$(@@GROUP::!X=Avg(@Feld,Prec);!Y=Conc(@FELD,Sep,Cond)), Avg() and Conc()

 

The Average function without second parameter returns the value with 34 digits. This is probably more exact than you need, so it is possible to add the number of decimal places (supported by all 3 "advanced aggregations" which work on Numbers).

 

Conc() (please distinguish with the concatenate function, which is not an aggregation function) concatenates the handed over (with parameter 1) value separated by parameter 2. As third parameter it is possible to specify a condition. In this case it is used to sort out non-filled entries. In contrast to the LIST() function, this is not done automatically.

 

Please note that the Group condition is a long text field here, consequently the sort condition has to reflect this also.

 

Geoavg() calculates the geometrical average (nth root on the product of all elements). Calculation is done using logarithm. All numbers in the calculations must be positive.

 

Stddev() calculates the standard deviation.

Minima. Maxima.

 

~*/SearchResFlightRel/FlightBookRel$(@@CLASS,@FORCURKEY::!R=Min(@LOCCURAM,concatenate(@PASSNAME," paid ",""+@FORCURAM," ",@FORCURKEY)))
Code Example 45, $(@GROUP::!X=Min(@Feld,Returnvalue)), Min() and Max()

 

The example shows the usage of the Min/Max method. The result is assembled based on the first Min/Max row found with the second parameter. If no second parameter is provided, the min/max value is returned directly.

 

Example 46 shows, that aggregations are not necesarilly basing on attributes directly. The example calculates the maximum paid for a kilogram of luggage, with the side condition that the weight should be bigger than zero. Using the iff() function is a standard way to deal with these side condition, but please note that in this case it is actually not needed since the division is anyway defined in a way that 0 is returned if it is attempted to divide by zero. This is done to avoid dumps at run time which can not be detected at design time.

 

~*/SearchResFlightRel/FlightBookRel$(*::!R=Max(iff(@LUGGWEIGHT>0,@LOCCURAM%@LUGGWEIGHT,0)))
Code Example 46, $(*::!X=Max(function(@Feld1,@Feld2))), Max based on complex expression

 

First(), Last(), GrpIndex(), Any()

 

Some "easy" aggregations. First() returns the value corresponding to the first row, the aggregation may be equiped with a condition . Last() basically does ... nothing. It is just included for the sake of completeness, you will have the same effect if you state the parameter directly.

 

With GrpIndex() you might access the nth row (n is the first parameter, second parameter specifies the value to be returned). In contrast to direct indexing, n has to be positive here.

 

Any() uses the randomizer library to choose an arbitrary row from which the return is assembled.

Search(), SearchFirst()

 

SearchFirst() and Search() are searching for specific rows fullfilling the specified search condition. SearchFirst chooses the first found entry, Search all - or if no self linking is used, in consequence the last row.

 

~*/SearchResFlightRel/FlightBookRel$(*::!R=SearchFirst(@LUGGWEIGHT>30,@Passname))
Code Example 47, $(*::!X=SearchFirst(@F1,@F2)), SearchFirst

 

The following example shows the teamwork of the Search and the list function. The search function searches all bookings with "overweight" (considered to be above 24) and adds the name of the passenger to the list of overweighted passengers:

 

~*/SearchResFlightRel/FlightBookRel$(@@CLASS::!OVERWGHT=Search(@LUGGWEIGHT>24,LIST(!OVERWGHT,",",@PASSNAME)))
Code Example 48, $(*::!X=Search(Cond,func(!X))), Searches returning lists

Median()

 

Median() sorts the attributes and chooses the one in the middle (respectively one of the middle pair). Median is supported for Numeric Values, Strings and Dates. Median() has no optional rounding parameter.

 

Even though it sounds similar to the average, technically it is a bit more costly. For all other aggregations it is sufficient to keep one value per group, for the Median the complete list has to be kept. Performance seems to be harmed only by a smaller factor as the sort_in features of ABAP tables are used.

 

A Small leftover: ToRoot-Relation ("...")

 

~*/SearchResFlightRel/FlightBookRel/...$/*
Code Example 49,..., to root

 

Additional to the relation to the object itself ("."), the relation to the parent (".."), the relation to the root object is now available using the ... syntax. In contrast to the parent, the root is unambigous.

 

Aggregation Table


The letters refer to the 4 possible datatypes ( String, Date, Boolean, Number ). The GRPINDEX function for example has a first parameter of type Number follwed by a second parameter of any type. Conc has either two parameters of type String, or two string parameters followed by a Boolean parameter as third parameter.
Name
Main Parameters
Optional Parmeters
Description
COUNT
no
B
counts all elements where the condition evals to true
SUM
N
no
sums up handed over value
PRODUCT
N
N
multiplizes handed over entries. Optional parameter provided possibility to round the return
MIN
SDNB
SDNB
returns second parameter for the first found global minimum. If second parameter is not supplied first parameter will be returned
MAX
SDNB
SDNB
maximum, equivalent to minimum
AVG
DN
N
returns arithmetical average. Optional parameter provided possibility to round the return (not in case of date values), e.g. 0 rounds to whole digits, 2 to 2 decimal places.
GEOAVG
DN
N
returns geometrical average. Optional parameter see AVG.
FIRST
SDNB
no
returns entry from first row
LAST
SDNB
no
returns entry from last row (equivalent to the usage of the parameter directly)
GRPINDEX
N, SDNB
no
returns entry from row specified with parameter 1. Negative indexing is not possible here, since the size of the group is not known.
ANY
SDNB
no
returns one of the rows chosen by equal probability
STDEV
N
N
returns standard deviation. Optional parameter provided possibility to round the return.
CONC
S, S
B
concatenates first parameter separated by second parameter. Optionally only rows which fullfill the condition specified as third parameter are taken into account. If all rows evaluate to false, an empty string will be returned.
SEARCH
B
SNDB
returns parameter 2 belonging to row where condition is met. This means typically the last row fulfilling the condition, or, in case parameter 2 includes its own target an hand-made aggregation of all found rows (see examples)
SEARCHFIRST
B
SNDB
returns parameter 2 belonging to first row where condition is met.
MEDIAN
SND
no
returns median element from the list of attributes

 

Lesson 11 will deal with enhancements done mainly on the sub-function.

A example about how to analyze SYSTEM_NO_ROLL error in Webclient UI

$
0
0


In my previous blog Dropdown list issue in CRM Webclient UI - a very funny trouble shooting processI have shared with you how I deal with an drop-down list issue and how I finally find root cause.


In this blog I will share with you another example how I struggle with another issue which is drop down list relevant and how I decide which place in the code to set breakpoint to start my trouble shooting.


Issue description

 

Today I received one incident from customer, complaining that they could not successfully create an ERP order. They meet with out of memory issue in UI. How could a creation of ERP order ask for more than 700MB memory???

 

clipboard1.png

When I check in ST22, there are lots of SYSTEM_NO_ROLL errors.

clipboard2.png

Checking Active Calls/Events, I have only two findings:

 

1. according to callstack 62, this error occurs when UI framework tries to populate native html source code to be rendered for the end user.

2. callstack 51 shows the overview page is to be rendered. According to the reproduce steps provided by customer, to be more exact, the issue occurs when ERP order overview page is to be rendered.

 

clipboard3.png

Click tab "Source Code Extract", and I know the system fails to allocate necessary memory for this CONCATENATE operation.

I can not imagine how big the m_content or value could be.

 

clipboard4.png

The information in "Chosen variables" could not help me too much:

clipboard5.png

I realize I have to use debugging once again. But where do I set breakpoint?

 

My issue analysis process

 

Since all callstacks displayed in ST22 are webclient ui framework stuff, if I set breakpoints there, they will no doubt be triggered again and again, do nothing helpful for my analysis.

 

I just set two breakpoints in DO_PREPARE_OUTPUT of ERP Order header view and item view.

clipboard6.png

clipboard7.png

Both methods are executed successfully in debugger, nothing strange.

 

What kinds of UI element would lead to a large response stream to browser

 

Since this issue is caused by the huge memory allocation for the to-be-rendered html page, we can think about what kinds of UI element would lead to a huge response stream?

 

As far as I know, there are following candidates:

 

1. A long long web page with lots of text content.

 

When talking about webclient ui, the possible scenario is that a text-area has lots of characters. However this is not the case for this incident.

In ERP creation page it is impossible to have a long text which needs 700MB to display.

 

2. A table with so many entries.

 

In ERP_IT/ItemTab there is indeed a table element, however since currently we are in ERP order creation page, there is no item record at the creation phase.

 

3. A drop down list with so many entries.

 

If you have gone through my previous blogyou can know that the keys and descrptions for each entry in a drop down list will be included in the html source code. So in theory, if a drop down list has a hug number of entries, it is also possible to generate a large response stream.

 

So now all context node attributes which has implemented method GET_V_XXX are suspects. So I have set breakpoint on all of them.

clipboard8.png

And soon I have found the one who makes troubles, the GET_V_SPART. Totally 408089 entries are returned by method cl_crm_uiu_erp_order_vh=>get_values_for_spart.

clipboard9.png

When this big drop down list is finally being rendered, the handler class CL_THTMLB_SELECTION_BOX has to populate the final html source code for these 408090 entries, and run out of memory.

clipboard10.png

Since root cause is found, I asked responsible colleague to have a look why such huge numbers of entries are returned, as this absolutely does not make sense for end users.

 

Hope this tip of mine to choose where to set breakpoint could help for your trouble shooting. Of course in this case memory inspector s_memory_inspector could also help you identify code which generates high memory demand.

Using Word-Integration within Web UI Client

$
0
0

What's this all about?

This blog is about my experience in using the Microsoft Word Integration within the Web UI Client Framework. Primarily I use it for own documentation purposes thus I do not want to maintain lots of Word documents on my own storage any more.

In short there are three steps you have to take when using Word templates:

- create the template (Web UI application)

- create data provisioning (by implementing the later mentioned enhancement spot)

- add the function to call your Word template

 

The target

The business case is to print individual text-heavy letters with less dynamic Content (documents with a lot of dynamic Content (e.g. tables) should be implemented using Adobe Interactive Forms By Adobe).

 

 

Development Environment

You can start the development Environment by using the UI Component Workbench and testing the UI component  CRM_OI_TEMPLDSG.

bsp_wd_cmpwb.PNG

language to English. But you can see this as an exercise to improve your German skills (but you do not receive an certificate :-(). The following screen appears:

dtd_01.PNG

type (Objekttyp). This is the classic object type introduced a long time ago with the SAP Business Workflow environment (transaction code SWO1) type delivers the data needed for our Word template. Beside the other self explananing select-options it's important to know that you can administrate different types of templates:

  • Microsoft Office Word ("new" XML based documents, e.g. docx)
  • Microsoft Word (old doc Format)
  • Adobe XML form

 

The object-type is the base for processing the Microsoft Word templates as the coding of the SAP Standard processing class CL_CRM_OI_T_TEMPLATES_IMPL references this within the method OPEN_DOCUMENT().

 

You receive a list of all templates available by applying the selection-criteria:

result_list.png

There you can find the functionality to create, edit, copy and delete templates.

 

Creating a new Word template

Just press the "New"-Button and the following Screen appears:

new_template_01.PNG

After filling the gaps it's time to design the layout, this is the next step to proceed. So I thought using the button "start designer" (Designer starten) is the way to do so. But if you press this button Microsoft Word is being opened in an read-only mode - so you cannot modify the document (maybe another feature). You can find this in the following screenshot with the German word "Schreibgeschützt" (this means read-only):

word_read_only.PNG

So this is not the way we can edit our new template. Close the Microsoft Word application and you will see the "New" View again. Have a look at the radiobutton "use macros" (Makros verwenden). It seems to be a "feature" that the option to edit the template only is available when "use macros" is set to active.

 

Now the option for downloading (Button called "Herunterladen") the template appears so we can edit this document locally on our client by using Microsoft Word:

 

new_template_02.PNG

Using merge-fields

 

Adding dynamic fields to the Word template is very easy. During editing your document just use the "Add Field" function and choose "merge field". Please consider appropiate naming conventions in order to avoid confusing implementations with an outlook on the maintainance in the future.

All these merge-fields have to be accessed and filled within your BadI implementation (method GET_VALUES()).

merge_field_1.PNG

 

After you have finished editing the document just use the "upload functionality" (here called "Hochladen", in the screenshot before).

 

 

Data provisioning

Now we have a closer look at the data provisioning layer. Therefore it's essential to know the Enhancement-Spot CRM_OFFICE_INT_ENHANCEMENT. This BadI is called before the document is being opened. Thus we need an implementation of this EH-Spot.

 

The relevant Interface is called IF_EX_CRM_OFFICE_TEMP_BADI. It provides three methods:

Method nameDescription
GET_ATTRIBUTESDefinition of all available field-symbols (placeholders, fields) for use within the Word templates
GET_VALUES

Provide data to the fields

  • Parameter CT_VALUES
    • the most important parameter: you have to fill this key-value-pair with the IDs of your placeholders defined within in your template and fill the values which replace the placeholders
GET_DESC_NAMESnot used, yet

 

Go to transaction SE18 and open the mentioned enhancement spot and create a new enhancement implementation:

 

se18_01.png

You have to define the implementation's name and assign a composite enhancement-implementation if desired. At least you have to define the class' name. It might be a good approach to copy the pre-delivered sample class:

se18_02.PNG

After creating a new implementation class you first have to focus on the Method GET_ATTRIBUTES().field" in your template.

 

An implementation could look like this for the example above (merge-field names):

APPEND INITIAL LINE TO ct_attributes[] ASSIGNING <lf_attribute>.<lf_attribute>-name = |FIRST_NAME|.<lf_attribute>-description = space.
UNASSIGN <lf_attribute>.
APPEND INITIAL LINE TO ct_attributes[] ASSIGNING <lf_attribute>.<lf_attribute>-Name = |LAST_NAME|.<lf_attribute>-description = space.

 

The next step is to supply the attributes with values. This happens within the method GET_VALUES() which could look like this:

LOOP AT ct_values[] ASSIGNING <lf_value>. "CT_VALUES will be supplied with all the attributes added to ct_attributes[]
CASE <lf_value>-name.
WHEN 'FIRST_NAME'.<lf_value>-value = lv_first_name. "imagine lv_first_name contains the matching attribute from BOL-model
WHEN 'LAST_NAME'.
...
ENDCASE.
ENDLOOP.

It's recommended to implement the data provisioning logic (access to BOL-model) in a separate class in order to not overload the BadI implementation and be more flexible in the future. Maybe you've got many documents so it's better to practice "separation of concern".

 

 

Transport Management


Please keep in mind that Word templates are not connected to transport management automatically. For this you can use the report CRM_KW_TEMPLATE_TRANSPORT or use the SPRO -> CRM -> Basic functions -> Content mgmt. -> Transport template

Data Retrieval Parameters in SAP CRM Accounts

$
0
0

You can use configurable data retrieval parameter to limit the data retrieved in assignment blocks, based on selection parameters defined in the UI configuration tool.


Below is the List of Assignment Blocks of Accounts for which Configurable data retrieval is supported


Assignment blocks:

  • Interaction History
  • Planned Activities
  • Opportunities
  • Leads
  • Sales Orders
  • Sales Agreements
  • Quotations
  • Service Arrangements


Configuration: I will configure data retrieval configuration for Interaction History Assignment block to show only the Business activity with status completed.

 

T-Code: BSP_WD_CMPWB

 

Component= BP_BPBT and Click Display

 

1.png

 

Click BP_BPBT/AccountInterHistOV under View  and click tab Configuration

 

2.png

 

Click Edit button and add the Data Retrieval Parameters

Status= Completed and Transaction Category = Business Activity

3.png

 

Features

  • For assignment blocks that have been set up for configurable data retrieval, you can define which data records are selected and displayed by defining selection parameters as part of the user interface (UI) configuration. As a result, only specific data is displayed in the assignment block.
  • The system performance can be improved because of a reduced amount of data retrieved from the database. Whether the system performance is improved or not depends on how the configurable data retrieval has been implemented for a certain assignment block.
  • You can change the selection parameters at any time, altering the data selection presented to the business user.
  • You can set selection parameters for each view usage (business role), because the selection parameters are specified as part of the view configuration.
  • Report Views with Configurable Data Retrieval option  (BSP_DLC_CONF_DAT_RETRIEV_USAGE) provides you with a list of all views that support configurable data retrieval.

Dynamic navigation with simple steps

$
0
0

Hi All,

 

 

I saw a couple of posts related to dynamic navigation already. However, I felt of writing this since there are still many questions arise on the same.

 

Let us try a simple dynamic navigation where I will be taken to an opportunity details when I click on the number/name(In my case I give number) of the opportunity.

 

Step 1: Create a custom field in the opportunity header by AET with name Reference oppt. Here, user is going to give a reference opportunity ID. It's an independent reference opportunity ID and not created as a followup.

 

GET_P of the field:

 

CASE iv_property.
     WHEN if_bsp_wd_model_setter_getter=>fp_fieldtype.
       IF iv_display_mode EQ abap_true.
         rv_value = cl_bsp_dlc_view_descriptor=>field_type_event_link.
       ELSE.
         rv_value = cl_bsp_dlc_view_descriptor=>field_type_input.
       ENDIF.
     WHEN if_bsp_wd_model_setter_getter=>fp_onclick.
       rv_value = 'toref'.
   ENDCASE.

 

Step 2: Now the field is available in the opportunity as a hyperlink when in display mode.

Opport.PNG

 

Step 3: Next step is to create an event handler for this. Create an event called 'TOREF'.

 

1.PNG

 

Step 4: Write following code in event handler.

 

DATA: lr_entity      TYPE REF TO cl_crm_bol_entity,
           lr_col         TYPE REF TO cl_crm_bol_bo_col,
           ls_attributes  TYPE crmt_bsp_opportunity_bp ,
           lv_object_guid TYPE crmt_genil_object_guid,
           lr_core        TYPE REF TO cl_crm_bol_core,
           lr_entity_bt   TYPE REF TO cl_crm_bol_entity,
           lr_nav_serv    TYPE REF TO if_crm_ui_navigation_service,
           lr_nav_descr   TYPE REF TO if_bol_bo_property_access,
           lv_opportid    TYPE CRMT_DOC_FLOW_ID_WRK.

     CONSTANTS:
         lc_ui_action   TYPE crmt_ui_actions VALUE 'B'. "Display

     lr_entity ?= me->typed_context->btopporth->collection_wrapper->get_current( ).

     CHECK lr_entity IS BOUND.
     lr_entity->get_property_as_value( EXPORTING iv_attr_name = 'Z_FIELD'
                                       IMPORTING ev_result    lv_opportid ).

     lr_core = cl_crm_bol_core=>get_instance( ).

* Get the corresponding GUID of the ID with your code and assign to lv_object_guid.

     lr_entity_bt = lr_core->get_root_entity( iv_object_name = 'BTOrder'
                                              iv_object_guid lv_object_guid ).
     IF lr_entity_bt IS BOUND.
       CREATE OBJECT lr_col.
       lr_col->if_bol_bo_col~add( lr_entity_bt ).
     ENDIF.

* Depends on the view, give the right object id.

     cl_crm_ui_descriptor_obj_srv=>create_ui_object_based(
              EXPORTING iv_component        = 'BT111H_OPPT'
                        iv_ui_object_type   = 'BT111_OPPT'
                        iv_ui_object_action = 'B'               "Display mode.
              RECEIVING rr_result           = lr_nav_descr ).

     lr_nav_serv = cl_crm_ui_navigation_service=>get_instance( ).

* check if navigation is possible
     IF lr_nav_serv->is_dynamic_nav_supported( ir_descriptor_object = lr_nav_descr ) = abap_true.
       lr_col->if_bol_bo_col~insert( iv_bo    = lr_nav_descr
                                       iv_index = 1 ).
*   start the navigation
       lr_nav_serv->navigate_dynamically( iv_data_collection = lr_col ).
     ENDIF.


Step 5: By this much, the main part is done. Only thing left is to maintain the navigation bar profile values in SPRO.

Go to tcode:CRMS_UI_TLINK to get the target ID. Take the opportunity details and get the right target ID.

 

2.PNG

In our case we are taking the display. Hence choose TBT111MOV.

 

Step 6: Maintain SPRO values.

Take define navigation bar profile.

3.PNG

Take the navigation bar profile of you and maintain the values.

 

4.PNG

5.PNG

 

You are done with all the steps now. Go to your opportunity and see it's navigating to the correpsonding opportunity. Please let me know if in case of any doubts.

 

Thanks,

Faisal


CRM Rapid Applications - An Advanced Tutorial (Part 1)

$
0
0

Introduction

Recently I started to play around with the CRM Rapid Applications (Rapid Applications - SAP Library). I wanted to use it to create a little application with some basic functionality. The test application should prefill some data by e.g. using some number range object. Furthermore, it should perform some data validation. As I had attended sitMUC (SAP Inside Track Munich 2014 - Community Events - SCN Wiki) just a few weeks ago where Daniel Ridder and Tobias Trapp gave a presentation on BRFplus (SAP Business Rules Management) I wanted to implement the validations using BRFplus rules.

As with most of the interesting SAP technologies there are basic tutorials available for both, CRM Rapid Applications (e.g. SAP CRM 7.0 EhP1 - Rapid Applications (Part 1) and SAP CRM 7.0 EhP1 - Rapid Applications (Part 2) by Tzanko Stefanov) and BRFplus. However, more advanced tutorials (like e.g. AXT Rapid Applications Currency/Quantity fields by Luis Pérez Grau) are hard to find.

Therefore, I took me quite a while to implement my sample application. I only succeeded by asking questions in the BRFplus forum and by extensive debugging in the CRM Rapid Application framework. Consequently, I decided to collect the knowledge gained in the process in this mini blog series in order to provide an advanced tutorial for CRM Rapid Applications as well as BRFplus. The first part of the blog series is focused on the Rapid Application part and the integration of BRFplus, the second part (CRM Rapid Applications - An Advanced Tutorial (Part 2)) will provide details on the design of the the BRFplus rules.

 

The Application Design

The applications shown in the existing rapid application tutorials are based on existing database tables. In contrast to that I will show how to create an application from scratch including the data base tables using only the rapid application functionality. For the sample application I defined the following requirements which are quite common in business applications. The application should be able to store:

  1. Header data and corresponding attributes
  2. The header should consist of an ID, a reference to a business parter, a creation and change date and user
  3. The attributes should be simple key value pairs.

In addition to that

  • It should be possible to add but not to delete records
  • The ID should be taken from a number range object
  • The creation date and user and change date and user should be set automatically by the system
  • The validity of the attributes should be check according to the following rules
    1. "Attribute 1" is mandatory, "Attribute 2" is optional.
    2. Allowed values for "Attribute 1" are "A", "B" and "C",
    3. Depending on the value of "Attribute 1" the following values are allowed for "Attribute 2"
      Attribute 1Attribute 2
      A1 - 99
      B100 - 299
      C300 - 999

According to these requirements the following table show valid records for the sample application:

 

IDBusiness PartnerCreated AtCreated ByChanged AtChanged By
0000000001100009001.11.2014DRUMM2.11.2014DRUMM
0000000002140001233.11.2014DRUMM3.11.2014DRUMM

 

Header IDAttributeValue
0000000001Attribute1A
0000000002Attribute2B
0000000002Attribute2125

 

These requirements seemed simple enough for an introductory  tutorial when I defined them in the first place. However, it turned out, that there are quite some difficulties hidden in them, both for CRM Rapid Applications and the BRFplus implementation.

 

Creating the Rapid Application

Rapid Applications are created using a wizard provided by the Rapid Application framework. The wizard is started by creating a new Rapid Application. As the example application for this blog should be based on database table select "Create from DB Table" to create the new application.

2014-11-04 16_42_21-Manage Rapid Applications - [SAP] - Internet Explorer bereitgestellt von Ihrer r.png

The Rapid Application wizard consists of the following steps:

1.    Defining name and package for the application

2.    Defining the database model

3.    Defining the UI model

4.    Optionally assigning the application to some UI profiles.

During each steps it is possible to generate and test the application. For this blog I created the application ZCD_RAPIDAPP_EX in the $TMP package.

2014-11-04 16_43_59-Manage Rapid Applications - [SAP] - Internet Explorer bereitgestellt von Ihrer r.png

Defining the Database Model

As mentioned above the goal of this blog is to show how to create a Rapid Application from scratch. Therefore, the next step is to define the database model. First the table to store the header data needs to be created. In order to create a new database table click on the "New" in the "DB Model" step of the wizard. This creates a new database table with an empty field list. Initially the new database table has a auto-generated name (ZCTAB000000N in the screen shot below). It is possible to change this name by clicking on "Details" and providing a custom name for the table (ZCD_RAE_HEADER in the screen shot below).

2014-11-04 16_47_41-Manage Rapid Applications - [SAP] - Internet Explorer bereitgestellt von Ihrer r.png

Although the field list initially appears to be empty the Rapid Application framework automatically generates some "technical" database fields. These field are CLIENT, RECORD_ID, PARENT_ID and OBJECT_ID, with CLIENT and RECORD_ID being the key fields of the database. The auto-generated fields are used by the framework to e.g. link entries in different tables.

The next step is to define the table fields required by the application. The following screen shot shows the table fields I generated for the table ZCD_RAE_HEADER. Note that the field ZZ_OBJECT_ID is defined as a logical key. The definition of a logical key is optional. However, in order to being able to define dependant tables a logical key is required in the superordinate table. Furthermore note, that some of the fields (e.g. ZZ_PARTNER or ZZ_CREATED_BY) have the field type "Application Reference". Fields of type Application Reference are used to store references to other business objects like e.g. account or employee. Application references are used during the execution of the application to provide links in the UI to the respective objects. Finally, the fields available as search criteria need to be defined. This is done in the details view for each field.

2014-11-04 16_54_47-.png

To complete the database model for the application I created the dependent table ZCD_RAE_ATTRIBS to store the attributes. The following screen shot shows the details of this table. Note, that for the field ZZ_ATTRIBUTE I created a list of possible values. The values will be rendered as a drop down list when the application is executed.

2014-11-04 21_13_40-Field Details -- Webseitendialog.png

Defining the UI Model

The next step is to define the UI model. The definition of the UI model consists of two steps, generating the views and adjusting the UI customizing. The first step is to automatically generate the UI model. This will create a search page, an overview page and a embedded search page. After the views have been generated the next step is to adjust the view. The only necessary adjustment for the example application is to remove the delete function from the overview page (cf. the following screen shot).

2014-11-04 21_26_49-Manage Rapid Applications - [SAP] - Internet Explorer bereitgestellt von Ihrer r.png

I didn't add the the Rapid Application to a UI profile for this blog. Consequently, the final step is to save and generate the application. After the application has been generated it can be tested using the "Test" button. Note however, that not the complete functionality of the application is available in the test mode. For example, navigating to different business objects is not supported in the test mode.

The final step in defining the UI is to adjust the UI configuration. As with all SAP CRM applications this can either be done using the configuration mode or transaction BSP_WD_CMPWB_NEW. I change the standard configuration of the header details view as shown in the screen shot below. The business partner field is defined as mandatory, all other fields are display only. The other field should be automatically filled when a new record is created.

2014-11-04 22_19_54-http___srv06164.dataw1.de_8000_sap(bD1lbiZjPTEwMCZkPW1pbg==)_bc_bsp_sap_bspwd_cm.png

Extending the Rapid Application

In order to automatically populate the read only fields as well as to check the attributes according to the rules defined above it is necessary to extend the rapid application. In order to execute custom code during creating, changing and checking of a record the BAdI AXT_RT_TABLES_API is provided by SAP. The BAdI provides the methods ON_CREATE, ON_CHANGE, CHECK and QUERY. In this blog I will use the methods ON_CREATE, ON_CHANGE and CHECK to implement the requirements defined in the application design.

 

Automatically Populating Fields

The application design defines the following requirements:

  1. The Record ID should be taken from a number range object
  2. The creation date and user and change date and user should be set automatically by the system.

In order to achieve this I created the implementation ZCD_AXT_TABLES_RAE_HEADER_IMPL of the BAdI AXT_RT_TABLES_API. The execution of the BAdI is controlled by a filter on the table name. Therefore, I created the filter value ZCD_RAE_HEADER = TABLE_ID (see screen shot below).

2014-11-05 20_45_42-ABAP - CFD_100_drumm_en_[CFD] ZCD_RAPID_APPS_EX_ENH - Eclipse.png

The code snipped below shows the implementation of the ON_CREATE and ON_CHANGE methods of the BAdI implementation class. In the ON_CREATE method a new ZZ_OBJECT_ID is taken from a number range (lines 7 - 21). Besides that, only the fields ZZ_CREATED_BY, ZZ_CREATED_AT, ZZ_CHANGED_BY and ZZ_CHANGED_AT are initialized. The method GET_PARTNER_GUID_FOR_UNAME used in line 29 and 43 is a simple functional wrapper around the function module BP_CENTRALPERSON_GET.

 

  METHOD if_ex_axt_rt_tables_api~on_create.

    DATA: header_data TYPE zcd_rae_header_work,

          partner_guid TYPE bu_partner_guid.

       

    header_data = cs_work_structure.

    IF header_data-zz_object_id IS INITIAL.

      CALL FUNCTION 'NUMBER_GET_NEXT'

        EXPORTING

          nr_range_nr             = '01'

          object                  = 'ZCD_RAPID'

        IMPORTING

          number                  = header_data-zz_object_id

        EXCEPTIONS

          interval_not_found      = 1

          number_range_not_intern = 2

          object_not_found        = 3

          quantity_is_0           = 4

          quantity_is_not_1       = 5

          interval_overflow       = 6

          buffer_overflow         = 7

          OTHERS                  = 8.

 

      IF sy-subrc <> 0.

        RETURN.

      ENDIF.

    ENDIF.

 

    IF header_data-zz_created_by IS INITIAL.

      partner_guid = me->get_partner_guid_from_uname( ).

      header_data-zz_created_by = partner_guid.

      header_data-zz_created_at = sy-datum.

      header_data-zz_changed_by = partner_guid.

      header_data-zz_changed_at = sy-datum.

    ENDIF.

 

    cs_work_structure = header_data.

  ENDMETHOD.

 

  METHOD if_ex_axt_rt_tables_api~on_change.

    DATA: header_data TYPE zcd_rae_header_work.

 

    header_data = cs_work_structure.

    header_data-zz_changed_by = me->get_partner_guid_from_uname( ).

    header_data-zz_changed_at = sy-datum.

  ENDMETHOD.

Issue Invoking the BAdI

The problem with the current status of the example application is that the BAdI implementation ZCD_AXT_TABLES_RAE_HEADER_IMPL will never get called. The reason is, that the Rapid Application framework checks if a logical key (the field ZZ_OBJECT_ID in the example application) before calling the BAdI. If the logical key is initial, an error message is raised an the BAdI is not called. Consequently it is impossible to implement the requirement to get the object ID from a number range object using only the BAdI AXT_RT_TABLES_API.

The only solution I found to circumvent this problem was to overwrite the DO_CREATE_ROOT method in the implementation class of the overview page ZCD_RAE_HEADER_OVP.

2014-11-12 17_02_20-ABAP - CFD_100_drumm_en - Eclipse.png

In general I don't like the idea of having to enhance the overview page to enable the automatic creation of the object ID. Because the Rapid Application framework forces me to do this I have the logic to create an object is distributed across different parts of the application. However, I found a rather elegant solution for overwriting the method DO_CREATE_ROOT, which is shown in the following code snippet.

In order to create the root entity I simply invoke ON_CREATE method of the BAdI implementation in line 8. The loop in lines 17-22 then maps the resulting data to the creation parameters of the root entity. The root entity is then create in line 24.

 

METHOD do_create_root.

    DATA: header_data TYPE zcd_rae_header_work.

 

    DATA(bol_core) = cl_crm_bol_core=>get_instance( ).

    DATA(entity_factory) = bol_core->get_entity_factory( mv_main_entity ).

    DATA(creation_params) = entity_factory->get_parameter_table( ).

 

    NEW zcl_axt_tables_rae_header_impl( )->if_ex_axt_rt_tables_api~on_create(

      EXPORTING

        iv_object_id      = ''

        iv_parent_id      = ''

        iv_record_id      = ''

      CHANGING

        cs_work_structure = header_data

    ).

 

    LOOP AT creation_params ASSIGNING FIELD-SYMBOL(<creation_param>).

      ASSIGN COMPONENT <creation_param>-name OF STRUCTURE header_data TO FIELD-SYMBOL(<comp>).

      IF <comp> IS ASSIGNED.

        <creation_param>-value = <comp>.

      ENDIF.

    ENDLOOP.

 

    DATA(bol_col) = bol_core->root_create( iv_object_name = mv_main_entity

                                           iv_create_param = creation_params

                                           iv_number = 1 ).

    eo_entity =  bol_col->get_first( ).

  ENDMETHOD.

Checking the Attribute Validity

In order to check the attribute validity using BRFplus I created another implementation  ZCD_AXT_TABLES_RAE_ATTRIB_IMPL of the BAdI AXT_RT_TABLES_API. For this implementation, however, the filter value ZCD_RAE_ATTRIBS = TABLE_ID is used (see screen shot below).

2014-11-12 18_15_17-ABAP - CFD_100_drumm_en_[CFD] ZCD_RAPID_APPS_EX_ENH - Eclipse.png

In the implementation class of the BAdI only the CHECK method is used. The code snippet below shows the implementation of the method. In order to check the attributes validity according to the defined rules, it is important to get all attributes of a header record. This is done by first getting the current attribute entity using the RECORD_ID and PARENT_ID in lines 8-13. From the current attribute entity the header entity is read in lines 19. In lines 24-34 all attribute entities related to the header entity are read and the attribute table for the invocation of the BRFplus function is prepared. The simplest way to find the correct entity names and relations is, as with other CRM applications, to use the BOL Model Browser in the transaction BSP_WD_CMPWB_NEW. Finally, the BRFplus function is invoked in line 36.

  METHOD if_ex_axt_rt_tables_api~check.

    DATA: bol_key TYPE axts_ca_bol_key,

          attrib_properties TYPE zcd_rae_attribs_attr,

          attrib_properties_tab TYPE STANDARD TABLE OF zcd_rae_attribs_attr.

 

    DATA(bol_core) = cl_crm_bol_core=>get_instance( ).

 

    ASSIGN COMPONENT 'RECORD_ID' OF STRUCTURE is_work_structure TO FIELD-SYMBOL(<record_id>).

    ASSIGN COMPONENT 'PARENT_ID' OF STRUCTURE is_work_structure TO FIELD-SYMBOL(<parent_id>).

    bol_key-parent_id = <parent_id>.

    bol_key-record_id = <record_id>.

 

    DATA(current_entity) = bol_core->get_entity(

                                        EXPORTING

                                          iv_object_name = 'ZAET_CA_CTAB000014 '

                                          iv_object_id   = cl_crm_genil_container_tools=>build_object_id( is_object_key = bol_key ) ).

    CHECK current_entity IS BOUND.

 

    DATA(header_entity) = current_entity->get_parent( ).

    CHECK header_entity IS BOUND.

 

    DATA(attrib_entities) = header_entity->get_related_entities( iv_relation_name = 'ZAET_CA_TO_CTAB000014' ).

 

    DATA(iterator) = attrib_entities->get_iterator( ).

    DATA(attrib_entity) = iterator->get_first( ).

    WHILE attrib_entity IS BOUND.

      attrib_entity->get_properties(

        IMPORTING

          es_attributes = attrib_properties

      ).

      APPEND attrib_properties TO attrib_properties_tab.

 

      attrib_entity = iterator->get_next( ).

    ENDWHILE.

 

    ct_messages = me->execute_checks_using_brfplus( attrib_properties_tab ).

  ENDMETHOD.

Summary

The implementation of the second BAdI concludes the first part of the application. With only a few lines of code an mostly only configuration of the Rapid Application the result is a running example application that already fulfils most of the requirements stated in the application design. In the second part of the blog I'll show how implement the validity checks for the attributes (ie. the implementation of the method EXECUTE_CHECKS_USING_BRFPLUS  as well as the underlying BRFplus function.

 

Christian

CRM Rapid Applications - An Advanced Tutorial (Part 2)

$
0
0

Introduction

In part 1 of this tutorial (CRM Rapid Applications - An Advanced Tutorial (Part 1)) I described how to implement a basic application using the CRM Rapid Application framework (Rapid Applications - SAP Library). In this part of the tutorial I will show how the BRFplus (SAP Business Rules Management) can be used to perform data validations in CRM Rapid Applications.

 

Designing the BRFplus Application

As described in the requirements in the first part of this tutorial the attributes in the example application should be checked according to the following rules:

  1. "Attribute 1" is mandatory, "Attribute 2" is optional.
  2. Allowed values for "Attribute 1" are "A", "B" and "C",
  3. Depending on the value of "Attribute 1" the following values are allowed for "Attribute 2"
    Attribute 1Attribute 2
    A1 - 99
    B100 - 299
    C300 - 999

The basic approach to check the attributes in BRFplus will be the following (thanks to Christian Lechner for helping me out with the initial design):

  1. Create a BRFplus application with one BRFplus function CHECK_ATTRIBUTE_VALIDITY. The function takes a table of type ZCD_RAE_ATTRIBS_ATTR as input and returns if the attributes are valid and optionally an error message.
  2. The functions is implemented using a single rule set CHECK_ATTRIBUTE_VALIDITY_RS.
  3. In the rule set the attribute validity is checked in three steps using different expressions (e.g. table operations, formulae or decision tables):
    1. Check if the mandatory "Attribute 1" is present. If yes, store its value in a local variable in the rule set. Otherwise set an error message and exit.
    2. Check if the optional "Attribute 2" is present. If yes, store its value in a local variable in the rule set. If more the one "Attribute 2" is present set an error message and exit
    3. Check the attribute value ranges using the local variables and a decision table. If the value ranges are valid return success other set an error message and exit.

Especially allowing an optional "Attribute 2" while trying to check the value ranges in the decision table required some special handling. There are two reasons for this. First, it is necessary to convert the string values of the attributes into number. Second, optional attributes are not easy to handle in a decision table. I'll discuss both topics in more detail in the subsequent sections.

 

Implementing the BRFplus Application

Implementing the validity checks described above in a BRFplus application require to some changes to the implementation approach an experienced ABAP developer would take. The reasons for these are twofold. First, BRFplus has some special properties, like e.g. the handling of data objects, that are counter-intuitive for an ABAP developer. Second, an even more important, is the need to think in rules and expressions instead of objects, attributes and methods. This was, at least for me, the biggest difference.

In the following sections I'll describe the necessary steps in BRFplus as well as the underlying rational to the approach as detailed as possible. This way I hove to simplify the first steps in BRFplus for developers by helping them to avoid common mistakes and trap doors.

 

Creating the BRFplus Application

The first step in order to implement the validity checks in BRFplus is to create a BRFplus application. In order to create a the application start the BRFplus workbench using transaction BRF+. In the BRFplus workbench us "Create Application" to create a new application. for this blog I created the application ZCD_RAPIDAPP_EX as a local application.

2014-11-16 14_02_26-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

 

Creating the DDIC Objects and BRFplus Data Objects

With the application in place the next step is to create the required BRFplus data objects. BRFplus offers two options for creating data objects like elements, structures and tables. Either these data objects can be created in BRFplus using build in data types or they can be created on the basis of existing DDIC object. As mentioned before BRFplus handles data objects differently then an ABAP developer would expect. In particular it is not possible to reuse a data object across functions. As a ABAP developer one would naturally try to reuse a data structure like ATTRIBUTE_1_DATA e.g. as the data type of a variable and the line type of a table data object. However, this is not possible in BRFplus. Each time a data type is used in BRFplus a separate data object needs to be created for it. In order to reduce the manual modelling effort I will therefore base the data objects in BRFplus on DDIC objects.

Most of the required DDIC objects where already created in the first part of this blog. The only additional DDIC object necessary is the structure ZCD_BRF_RAPID_APP_RESULT_S that I will use as the data type for the result of the BRFplus function. The following table shows the details of this structure:

ComponentComponent Type
ARE_ATTRIBS_VALIDBOOLE_D
ERROR_MESSAGEBAPIRET2

 

With the necessary DDIC objects in place it is now possible to create the required data objects. The first data object that I created is the table object ATTRIBUTE_TABLE. Data objects are created by  right clicking on the application in the repository explorer and selecting "Create -> Data Object -> Table".

2014-11-06 20_11_24-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

In the following dialogue I set the name and description for the table data object. Furthermore, I set the binding type to "DDIC Table" and selected the DDIC type ZCD_RAE_ATTRIBS_ATTR_T. This table type was automatically created during the modelling of the Rapid Application in part one of the tutorial.

2014-11-06 20_10_06-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

After clicking on create a number of data objects are automatically created. Besides the table data object ATTRIBUTE_TABLE the structure ZCD_RAE_ATTRIBS_ATTR and the two data elements ZZ_ATTRUTE and ZZ_VALUE are created. The following screen shot shows the created data object:

2014-11-06 20_12_03-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

In addition to the table data object I also created the following data objects

Data ObjectDDIC TypeDescription
Structure ATTRIBUTE_1_DATAZCD_RAE_ATTRIBS_ATTRData object for local variable in the rule set to store the values of Attribute 1.
Structure ATTRIBUTE_2_DATAZCD_RAE_ATTRIBS_ATTRData object for local variable in the rule set to store the values of Attribute 2.
Structure CHECK_RESULTSZCD_BRF_RAPID_APP_RESULT_SData object for the result of the function CHECK_ATTRIBUTE_VALIDITY.

 

Creating the Function and the Rule Set

With the data objects in place the next step is to create the CHECK_ATTRIBUTE_VALIDITY function and the rule set implementing this function. A function is created by right clicking the BRFplus application and selecting "Create -> Function". Once the function is create make sure it is set to "Event Mode" (cf. the following screen shot).

In the signature of the function the function context (ie. the input data to the function) as well as the result data object are defined. For the CHECK_ATTRIBUTE_VALIDITY function I choose the table data object ATTRIBUTE_TABLE as the context and the structure data object CHECK_RESULT as the result data object. Note that the context of a function can consists of more then one data object. However, for this example application on the attribute table is needed.

2014-11-06 20_30_16-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

After creating the function the next step is to create the CHECK_ATTRIBUTE_VALIDITY_RS rule set implementing the function. Again, a function can have multi rule sets assigned but for the example application only one rule set is needed. Rule sets can be created by selection the tab "Assigned Rulesets" and clicking on "Create Ruleset". Once the rule set is created I added the two data objects ATTRIBUTE_1_DATA and ATTRIBUTE_1_DATA as variable to the rule set.

2014-11-06 20_32_33-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

Creating Auxiliary Expressions

Before modelling the rules implementing the rule set the next step is to create some auxiliary expressions. These auxiliary expression are necessary to implement the rules. As mentioned in the application design the input data to the BRFplus application will be the table data object ATTRIBUTE_TABLE. To create the BRFplus rules the following auxiliary expressions are necessary:

  1. A table expression IS_ATTRIB_1_VALID to check if exactly on attribute named "Attribute 1" is contained in the input table.
  2. A table expressions GET_ATTRIB1_VALUE to select the data of "Attribute 1" form the input table.
  3. A table expression IS_ATTRIB_2_VALID to check if zero or one attribute named "Attribute 2" is contained in the input table.
  4. A table expression IS_ATTRIB_2_SET to check if exactly one attributes named "Attribute 2" is contained in the input table.
  5. A table expressions GET_ATTRIB2_VALUE to select the data of "Attribute 2" form the input table.

From the application design most of the table expressions should be pretty clear. The first table expressions is needed to check if the "Attribute 1" is contained in the attribute table. If this is the case, the second table expression is used to select the data of "Attribute 1". The rules three and five will be used in a similar fashion. The fourth rule is needed in the decision table to distinguish between the case where the "Attribute 2" is set from the one where it is not set.

 

The following screen shots show the implementation of the two table expressions IS_ATTRIB_1_VALID andGET_ATTRIB1_VALUE. IS_ATTRIB_1_VALID uses the operation "Has exactly". Using this operation it checks if the attribute table contains exactly one row where the value of ZZ_ATTRIBUTE is equal to Attrib1. Note that BRFplus automatically provides the possible values for the data object in the value help. I modelled the possible values using the Rapid Application Tool in the first part of the tutorial.

2014-11-06 20_35_03-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

The GET_ATTRIB1_VALUE function uses the operation "First line" to select the first row where the value of ZZ_ATTRIBUTE is equal to Attrib1.

2014-11-06 20_46_33-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

The other table expressions are implemented accordingly.

In addition to the table expressions a value range expression CONTAINS_ONLY_NUMBER and a formula expression CONVERT_ATTRIBUTE_TO_NUMBER are needed. The CONVERT_ATTRIBUTE_TO_NUMBER expression uses the CONTAINS_ONLY_NUMBER expression and the build in expression TONUMBER to convert the values of Attribute 2 in the internal number representation of BRFplus. Only if the data is in this format checking the values ranges of Attribute 2 works as required. The following screen shots show the implementation details of these expressions:

2014-11-16 22_41_36-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png2014-11-16 22_42_22-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png


Testing the Expressions

With the auxiliary functions in place it is a good time to introduce the simulation tool that is part of the BRFplus workbench. This tool can be used to simulate the execution of functions, rules and expressions during development. In order to, for example, simulate the execution of a expressions simply click on "Start Simulation" in the "Detail" section of the table expression.

2014-11-06 20_42_41-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

Next, on the simulation screen simply accept the default values.

2014-11-06 20_43_30-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

In the following screen it is now possible to provide input data for the simulation. As one would expect it is also possible to store the entered data as test variants or to upload test data from an excel. In my simple example I simply entered the test data manually. By clicking on "Execute and Display Processing Steps" the function is executed and the result as well as the executed processing steps are displayed. Especially when developing complex expressions or functions the simulation is helpful to test the development and analyse errors.

2014-11-06 20_44_08-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

 

Creating the Decision Table

Using the auxiliary expressions it is now possible to define the decision table CHECK_ATTRIBUTE_VALUES. In the table settings of the decision table I used to "Insert Column" button to add the field ZZ_VALUE from the rule set variable ATTRIBUTE_1_DATA as the first column, the expression IS_ATTRIB_2_SET as the second column and the expression CONVERT_ATTRIBUTE_TO_NUMBER as the third column. As the result column of the decision table I selected a simple boolean value.

2014-11-16 22_46_43-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

Using this set up I modelled the following rules in the decision table, which basically checks the third requirement of the application design.

 

ZZ_VALUEIS_ATTRIB_2_SETCONVERT_ATTRIBUTE_TO_NUMBERResult
A;B;Cfalse...true
AtrueBetween 1 and 99true
BtrueBetween 100 and 299true
CtrueBetween 299 and 999true

 

The following screen shot shows the implementation of this decision table in the BRFplus workbench.

2014-11-16 22_47_16-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

 

Creating the Rules in the Rule Set

Finally, all the necessary building block to implement the rule set are in place. Using the rule set the following logic (mention already in the application design) will be implemented:

  1. Check if the mandatory "Attribute 1" is present. If yes, store its value in a local variable in the rule set. Otherwise set an error message and exit.
  2. Check if the optional "Attribute 2" is present. If yes, store its value in a local variable in the rule set. If more the one "Attribute 2" is present set an error message and exit
  3. Check the attribute value ranges using the local variables and a decision table. If the value ranges are valid return success other set an error message and exit.

The following screen shot shows the implementation of the first step in the logic implement in the rule set. The first rule uses the IS_ATTRIB_1_VALID table expression to check if the Attribute 1 is present. If this is the case the CHECK_RESULT-ARE_ATTRIBS_VALID is set to true and the variable ATTRIBUTE_1_DATA to the result of the GET_ATTRIB1_VALUE table expression. The second rule is an exit condition that checks if CHECK_RESULT-ARE_ATTRIBS_VALID is false. If this is the case the error message of the CHECK_RESULT is set to some message class value and the rule execution is terminated.

Note that the combination of the expressions IS_ATTRIB_1_VALID and GET_ATTRIB1_VALUE assures the correct data is selected. If IS_ATTRIB_1_VALID returns true only one Attribute 1 is present in the input data. So GET_ATTRIB1_VALUE which selects the first row containing the value Attribute 1 will always select the right value.

2014-11-16 23_05_30-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

The following screen shot show the implementation of the second and third step of the logic. Rules three and four are similar to the rules one and two. This time only Attribute 2 is checked instead of Attribute 1. The fifth rule finally uses the decision table CHECK_ATTRIBUTE_VALUES to check the validity of the attribute values.

2014-11-16 23_13_19-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

Integrating BRFplus in the Rapid Application

Implementing the rule set completes the implementation of the BRFplus application. The final step that is missing is to integrate the BRFplus application with the Rapid Application. BRFplus simplifies this integration by providing an ABAP code generator. In order to use this code generator it is necessary to switch the user mode to Expert in the personalization of the BRFplus workbench.

2014-11-06 19_56_15-BRFplus Workbench - Internet Explorer bereitgestellt von Ihrer regio iT.png

Once the expert mode is set the "Create Code Template" button becomes visible in the BRFplus function. The code generated using the button contains nice comments as documentation. Consequently, the generated code is basically self-explanatory. The following code snippet contains the method EXECUTE_CHECKS_USING_BRFPLUS which I omitted in the first part of the tutorial. Lines 13-35 is the code generated by BRFplus with its comments removed. Lines 37-45 is some addition error handling I added for the integration with the CRM Rapid Application framework.

 

METHOD execute_checks_using_brfplus.

    "Code template generated by BRF+

    CONSTANTS:lv_function_id TYPE if_fdt_types=>id VALUE '0050568F55091ED499BCB76706ABF315'.

    DATA:lv_timestamp TYPE timestamp,

         lt_name_value TYPE abap_parmbind_tab,

         ls_name_value TYPE abap_parmbind,

         lr_data TYPE REF TO data,

         lx_fdt TYPE REF TO cx_fdt,

         la_attributes_table TYPE zcd_rae_attribs_attr_t,

         la_check_result TYPE zcd_brf_rapid_app_result_s.

    FIELD-SYMBOLS: <la_any> TYPE any,

                   <error_message> TYPE bapiret2.

    GET TIME STAMP FIELD lv_timestamp.

    ls_name_value-name = 'ATTRIBUTES_TABLE'.

    la_attributes_table = i_attribute_properties.

    GET REFERENCE OF la_attributes_table INTO lr_data.

    cl_fdt_function_process=>move_data_to_data_object( EXPORTING ir_data             = lr_data

                                                                 iv_function_id      = lv_function_id

                                                                 iv_data_object      = '0050568F55091ED499BD0B2AE3281315' "ATTRIBUTES_TABLE

                                                                 iv_timestamp        = lv_timestamp

                                                                 iv_trace_generation = abap_false

                                                                 iv_has_ddic_binding = abap_true

                                                       IMPORTING er_data             = ls_name_value-value ).

    INSERT ls_name_value INTO TABLE lt_name_value.

    GET REFERENCE OF la_check_result INTO lr_data.

    ASSIGN lr_data->* TO <la_any>.

    TRY.

        cl_fdt_function_process=>process( EXPORTING iv_function_id = lv_function_id

                                                    iv_timestamp   = lv_timestamp

                                          IMPORTING ea_result      = <la_any>

                                          CHANGING  ct_name_value  = lt_name_value ).

      CATCH cx_fdt INTO lx_fdt.

        APPEND INITIAL LINE TO r_error_messages ASSIGNING <error_message>.

        <error_message>-message = lx_fdt->get_text( ).

    ENDTRY.

 

    "Fill error message table

    IF la_check_result-are_attribs_valid = abap_false.

      APPEND INITIAL LINE TO r_error_messages ASSIGNING <error_message>.

      <error_message>-id = 'ZCD_RAPID_APP_EX'.

      <error_message>-number = '004'.

      <error_message>-type = 'E'.

    ENDIF.

    IF la_check_result-error_message IS NOT INITIAL.

      APPEND la_check_result-error_message TO r_error_messages.

    ENDIF.

  ENDMETHOD.

The final Application

Finally, the following screen shot show the complete example application with some error messages returned from the value check in BRFplus.

2014-11-16 23_35_26-http___srv06164.dataw1.de_8000_sap(bD1lbiZjPTEwMCZkPW1pbg==)_bc_bsp_sap_bspwd_cm.png

Summary

To implement the BRFplus application I used a bottom up modelling approach. I started modelling the basic building block and combined them in the very end. The reason I used this approach is that I think it helps understanding which parts are necessary and why they are necessary to build an BRFplus application. Besides that, BRFplus also support a top down approach, where you basically create the necessary objects as you need them.

One thing that always puzzled me while working on this blog was the questions if performing the value checks in BRFplus is worth the effort. After all, the simple validity rules could be checked in just a few lines of ABAP code. In my opinion using the BRFplus approach is useful if either of the following hold

  • the business logic changes frequently
  • developers start to build customizing tables to enable to change the business logic independent of the program code
  • business users want to be able to change the rules themselves.

In the example application I could easily add a third attribute and value checks for it without touching any of the ABAP code. The change could be performed by just modelling in the Rapid Application and the BRFplus application. This is in my opinion a very important factor as it will reduce development and test effort.

 

As this blog was my first endeavour in the area of BRFplus I would be happy to discuss my approach in the comments section. I'm sure there are areas where my design could be improved.

 

In summary, this two part blog showed that the combination of CRM Rapid Applications and BRFplus is a very effective approach to develop small business applications from scratch.

 

Christian

Set default values on a search view

$
0
0
ComponentSupport Package
BBPCRMSAPKU70204
SAP_ABAPSAPKA73105
SAP_BASISSAPKB73105
WEBCUIFSAPK-73105INWEBCUIF

SAP_BW

SAPKW73105

 

Before start, sorry for awful look of the code snippets, SAP is currently working on this: Re: Why does the syntax highlighting not work

 

The Scenario

 

We want to set default values as search criteria and we can't implement the note 2045936 - Default Values for Advanced Search Pages (which looks very promising, but lit's not compatible with my current release )

 

What, Where and How

 

That's the most difficult part, decide what to do and most important WHERE, once you got that, the How comes alone, I will focus on a specific scenario because I don't want to transform this blog in a philosophical thing, but between me and you, it is .

 

The Example

 

Let's enhance the BP search (BP_HEAD_SEARCH) to don't show the "set to archive"  BPs, as I see it right know you have at least 3 options:

 

Use a BADI enabled for the search (BADI_CRM_BUPA_IL_SEARCH_EXT)

 

I always choose the BADI as long as is possible, don't forget the UI is the technology which is evolving faster, specially in latest years, Imagine tomorrow SAP decide to replace the whole WebUI for a SAP Fiori ecosystem, you will need to place, so why we don't use always the BADI approach? in this scenario the filter will be totally transparent to the user and this can be good or bad: "why this is giving no results?"

 

Another consideration before going straight to the BADI method is evaluate the performance, my selection should be performed before the standard? after? and that depends on the detail of the BADI, in our example, the BADI is called before the standard selects any data.

 

Enhance the WebUI controller method (CL_BP_HEAD__MAINSEARCH/DO_PREPARE_OUTPUT)

 

For the scenario which I described, the best solution, easy to maintain and user friendly, he or she can decide which criteria to use before the search. Important: don't forget to update the context node  me->typed_context->search->build_parameter_tab( ). otherwise the BOL changes won't be published and you will lose the information.

 

DATA: lo_qs              TYPE REF TO cl_crm_bol_dquery_service,

           lo_qs_col        TYPE REF TO if_bol_bo_col,

           lo_iterator      TYPE REF TO if_bol_bo_col_iterator,

           lo_param_ent TYPE REF TO if_bol_bo_property_access.

 

   super->do_prepare_output( EXPORTING iv_first_time = abap_false ).

 

   IF iv_first_time = abap_true.

     lo_qs ?= me->typed_context->search->collection_wrapper->get_current( ).

     lo_qs_col ?= lo_qs->get_selection_params( ).

     lo_iterator = lo_qs_col->get_iterator( ).

     lo_param_ent = lo_iterator->find_by_property( iv_attr_name = |ATTR_NAME|

                                                                               iv_value       = |XDELE| ).

     IF lo_param_ent IS BOUND.

       lo_param_ent->set_property( EXPORTING iv_attr_name = |SIGN|

                                                                             iv_value         = |I|   ).                                   

       lo_param_ent->set_property( EXPORTING iv_attr_name = |OPTION|

                                                                             iv_value         = |EQ|   ).

       lo_param_ent->set_property( EXPORTING iv_attr_name = |LOW|

                                                                             iv_value         = |N|   ).

       me->typed_context->search->build_parameter_tab( ).

     ENDIF.

   ENDIF.


Enhance the WebUI controller method for the search (CL_BP_HEAD__MAINSEARCH/EH_ONSEARCH)

 

Well that's probably the worst option, if we talk about behaviour is a mix of the other two, you can use the parameter and hide it or you can use the parameter and show it after search.

 

If the parameter is not on the screen, we'll add it, use it for the search and update the query parameters on the frontend. If is not in the screen, we just set the values as we wish.

 

  DATA: lo_qs        TYPE REF TO cl_crm_bol_dquery_service,

        lo_qs_col    TYPE REF TO if_bol_bo_col,

        lo_iterator  TYPE REF TO if_bol_bo_col_iterator,

        lo_param_ent TYPE REF TO if_bol_bo_property_access. lo_qs ?= me->typed_context->search->collection_wrapper->get_current( ).

 

  lo_qs_col ?= lo_qs->get_selection_params( ).

  lo_iterator = lo_qs_col->get_iterator( ).

  lo_param_ent = lo_iterator->find_by_property( iv_attr_name = |ATTR_NAME|

                                                iv_value     = |XDELE| ).

  IF lo_param_ent IS NOT BOUND.

    lo_param_ent ?= lo_qs->insert_selection_param( iv_index     = lo_qs_col->size( ) + 1

                                                   iv_attr_name = |XDELE|

                                                   iv_sign      = |I|

                                                   iv_option    = |EQ|

                                                   iv_low       = |N| ).

  ELSE.

    lo_param_ent->set_property( EXPORTING iv_attr_name = |SIGN|

                                          iv_value     = |I|   ).

    lo_param_ent->set_property( EXPORTING iv_attr_name = |OPTION|

                                          iv_value     = |EQ|   ).

    lo_param_ent->set_property( EXPORTING iv_attr_name = |LOW|

                                          iv_value     = |N|   ).

  ENDIF.

  me->typed_context->search->build_parameter_tab( ).

  super->eh_onsearch( EXPORTING htmlb_event    = htmlb_event

                                htmlb_event_ex = htmlb_event_ex ).

 

 

This option is in case we disabled the field via configuration so we use it for the search but we don't want to show it to the user.

 

DATA: lo_qs        TYPE REF TO cl_crm_bol_dquery_service,

         lo_qs_col    TYPE REF TO if_bol_bo_col,

         lo_iterator  TYPE REF TO if_bol_bo_col_iterator,

         lo_param_ent TYPE REF TO if_bol_bo_property_access.

 

   lo_qs ?= me->typed_context->search->collection_wrapper->get_current( ).

   lo_qs_col ?= lo_qs->get_selection_params( ).

   lo_iterator = lo_qs_col->get_iterator( ).

   lo_param_ent = lo_iterator->find_by_property( iv_attr_name = |ATTR_NAME|

                                                 iv_value     = |XDELE| ).

 

   IF lo_param_ent IS NOT BOUND.

     lo_param_ent ?= lo_qs->insert_selection_param( iv_index     = lo_qs_col->size( ) + 1

                                                    iv_attr_name = |XDELE|

                                                    iv_sign      = |I|

                                                    iv_option    = |NE|

                                                    iv_low       = |Y| ).

   ENDIF.

   me->typed_context->search->build_parameter_tab( ).

   super->eh_onsearch( EXPORTING htmlb_event    = htmlb_event

                                 htmlb_event_ex = htmlb_event_ex ).

   IF lo_param_ent IS BOUND.

     lo_qs_col->remove( iv_bo = lo_param_ent ).

   ENDIF.

 

 

Conclusion

 

As you see there's no straight forward solution, the solution depends plenty on your landscape, business process and user demands. Every solution has its pros and cons, if you are in control of all of this, everything will go smooth

 

 

Cheers!

 

Luis

Use Notepad++ to edit your webclient UI component view

$
0
0

It is possible to use your favourite html editor ( for example, Notepad++ ) to edit the html source code of your UI component view or BSP application by simple configuration:

 

Just specify the absolute path of your local html source code editor ( .exe file ) in SE80 settings below:

clipboard1.png


Then in BSP_WD_CMPWB or SE80, use menu Edit->Start Local HTML Editor:


clipboard2.png

And then your local html editor is automatically opened with the given view source code loaded.

clipboard3.png

How does local html editor launched with source code

 

There is no magic behind the scene.

 

1. Once Edit-local menu is selected, first the path of local editor exe file is retrieved:

clipboard4.png

2. The source code of current html view is fetched and stored in internal table source:

clipboard5.png

3. The source code of html view is downloaded and stored in a temporary file in your local machine:


clipboard6.png

4. local editor is launched to open that temporary file generated in step3, and that's all.

clipboard7.png

clipboard8.png


Runtime analysis of WebUI navigation

$
0
0

Applies To:

 

Analysis of WebUI Navigation at runtime.

 

 

 

Summary:

 

You want to understand how a particular WebUI navigation is implemented.

 

This document does not intend to provide the concept of navigation in WebUI and how that could be implemented. It is assumed that reader understands navigation.

 

 

 

Prerequisites:

 

View, View Area, View Sets, Navigation

 

 

 

Overview:

 

In WebUI multiple views could be assigned to a view area. This assignment is maintained in repository of a component. At any point during runtime, only one of the assigned views could be displayed. This is referred to as current viewarea content.

figure1.jpg

 

Navigation in WebUI is nothing but a request to change the content of the view area with another view.

This achieved by navigation link which holds the target view. Framework do not need view area information as a view can be present in only one viewarea which can be read from repository.

 

These requests are build up into a queue and processed by view manager. There are two webui methods which are used in this regard.

 

CL_BSP_WD_WINDOW-IF_BSP_WD_NAVIGATION~NAVIGATE and CL_BSP_WD_VIEW_MANAGER- PROCESS_NAV_QUEUE.

 

CL_BSP_WD_WINDOW-IF_BSP_WD_NAVIGATION~NAVIGATE is responsible for posting request to nav queue while CL_BSP_WD_VIEW_MANAGER- PROCESS_NAV_QUEUE is for processing queue elements and trigger navigation.

 

Just before performing the user action which will do navigation, put external break points in these two methods and then take the action like button click etc.

 

You will get hit in CL_BSP_WD_WINDOW- IF_BSP_WD_NAVIGATION~NAVIGATE method one or multiple times as there are number of navigation requests. You would easily understand from call stack which controller is triggering that.

 

figure2.jpg

 

After you come out of all navigate method calls you will reach CL_BSP_WD_VIEW_MANAGER-PROCESS_NAV_QUEUE and can look into queue.
During trigger of individual navigation from queue entries there may be other navigation requests adding up in the queue.

 

figure3.jpg

Navigation is only completed for this http request / response cycle once the control comes out of process_nav_queue.

 

It may be noted here that CL_BSP_WD_WINDOW- IF_BSP_WD_NAVIGATION~NAVIGATE is a public method which could be overridden in a window and you could have skipped a major navigation request. However the chance of happening this is very less, as it is not required to redefine this functionality

How to show a custom Program In a View

$
0
0

Topic: How to show a custom Program In a  View

 

Moudle : SAP CRM TECHNICAL

Author: Rasmi Ranjan Pradhan

 

 

Hi All,

In one of my requirement, I have to show  a Zprogam from web ui view  level means when we click the attribute it will call the program and show the smart forms.

 

 

This Is my Overview Page and when I click On transaction history View 'Zsmart_form_demo '  then it will show a  z program and from Z program to populate  the smartform

 

My  challenge is that to call a z program through web-ui

 

So I got an idea to lunch The program T.code In ui so  I go through the T.code

CRMS_IC_CROSS_SYS

And find the link

http://cit47-srvr.ivlgroup.com:8000/sap/bc/gui/sap/its/webgui/!?~transaction=IC_LTX&~okcode=ICEXECUTE

so my intention is that replace  IC_LTX  with the custom T.code so  I write the code like that

in one event.

 

 

method eh_ondocflow.
call method super->eh_ondocflow
exporting
htmlb_event    = htmlb_event
htmlb_event_ex = htmlb_event_ex.

data: lv_index
type i, lv_column type string,
lr_coco
type ref to cl_btdocflo_bspwdcomponen_impl, " local coco impl
lr_entity
type ref to cl_crm_bol_entity,
lr_window
type ref to cl_bsp_wd_window,
gv_popup 
type ref to if_bsp_wd_popup,
lv_adminh_obj
type string,
gs_type
type  crmst_docflow_btil.
cl_thtmlb_util=>get_event_info(
exporting
iv_event = htmlb_event_ex
importing
ev_index = lv_index
ev_column = lv_column ).
*   * Find Rec
lr_entity ?= me->typed_context->btdocflow->collection_wrapper->find( iv_index = lv_index ).
*  lv_adminh_obj = lr_entity->get_property_as_string( iv_attr_name = 'CREATED_BY' ).

*  lv_adminh_obj = lr_entity->GET_PROPERTIES( ES_ATTRIBUTES = 'CREATED_BY' ).

*  GET_PROPERTY_AS_VALUE
***  IF NOT GT_ATTR IS  BOUND.
***  gs_type-ref_guid = GT_ATTR-ref_guid.
***  ENDIF.
* iNSERT rec into component controller
lr_coco ?= me->comp_controller.
lr_coco->typed_context->btdocflow->collection_wrapper->clear_collection( ).
lr_coco->typed_context->btdocflow->collection_wrapper->add( iv_entity = lr_entity iv_set_focus = abap_true ).
* Navigate - Select type of navigation based on which link
***  CASE lv_column.
***    when 'id_description'.
***      IF GV_POPUP IS INITIAL.
***        GV_POPUP = COMP_CONTROLLER->WINDOW_MANAGER->CREATE_POPUP( iv_interface_view_name = 'Z05_EMPTY/MainWindow' iv_usage_name = 'CU_OV' iv_title = 'POP_UP' ).
***      endif.
***      GV_POPUP->SET_ON_CLOSE_EVENT( IV_VIEW = ME IV_EVENT_NAME = 'CLOSE_GV_POPUP' ).
***      GV_POPUP->SET_WINDOW_WIDTH( 640 ).
***      GV_POPUP->SET_DISPLAY_MODE( IF_BSP_WD_POPUP=>C_DISPLAY_MODE_SURROUNDED ).
***      GV_POPUP->OPEN( ).
***endcase.
*******bidyut
types:
begin of ty_zcr4_tab,
mandt
type mandt,
objtype_a
type swo_objtyp,
objkey_b
type crmt_doc_flow_id_wrk,
end of ty_zcr4_tab.
data: wa_zcr4_tab
type  ty_zcr4_tab,
it_zcr4_tab
type table of ty_zcr4_tab.

wa_zcr4_tab-mandt = sy-mandt.
wa_zcr4_tab-objtype_a =
'5000040'.
wa_zcr4_tab-objkey_b =
'ZSMART_FORM_DEMO'.
append  wa_zcr4_tab to it_zcr4_tab .
modify zcr4_tab from table it_zcr4_tab.
*************

data: lr_struct_ref
type ref to crmt_gsurlpopup_params,

lr_value_node
type ref to cl_bsp_wd_value_node,

lr_bo_coll   
type ref to if_bol_bo_col,

lw_url_params
type crmt_gsurlpopup_params.


if not gv_popup is bound.

gv_popup ?= comp_controller->window_manager->create_popup(

iv_interface_view_name =
'GSURLPOPUP/MainWindow'

iv_usage_name          =
'CUGSURLPopup'

iv_title               =
'Google'

).

endif.
data: v_its_url
type crmc_ic_boradm-its_url,
f2
type string value 'IC_LTX',
f3
type string value 'Z_SMART_FORM'.
select its_url from crmc_ic_boradm into v_its_url where mapped_logsys = 'CRM'.
endselect.
if v_its_url  ca f2.
replace f2 in v_its_url  with f3.
*   write : / f1.
endif.
lw_url_params-url =
'http://cit47-srvr.ivlgroup.com:8000/sap/bc/gui/sap/its/webgui/!?~transaction=Z_SMART_FORM&~okcode=ICEXECUTE&sap-client=100'.
* replace LC_ltx with 
Z_SMART_FORM

lw_url_params-scrolling = abap_false.


get reference of lw_url_params into lr_struct_ref.


create object lr_value_node
exporting
iv_data_ref = lr_struct_ref.


create object lr_bo_coll

type

cl_crm_bol_bo_col.



lr_bo_coll->add( lr_value_node ).



data lr_ctxt_node type ref to cl_bsp_wd_context_node.



lr_ctxt_node ?= gv_popup->get_context_node( iv_cnode_name =
'PARAMS'   ).



lr_ctxt_node->set_collection(

collection                    = lr_bo_coll

).



gv_popup->open( ).
*  data: f1 type char10 VALUE 'ID1'.
*  data: f2 type char10 .
*  f2 = '5000040'.
* get parameter id 'TDSFNAME' field f1.

*  EXPORT F2 = F2 TO MEMORY ID F1.
**CALL TRANSACTION 'Z_SMART_FORM' AND SKIP FIRST SCREEN.
*  submit Z_SMART_FORM and return.
endmethod.

 

After that when I click on

A popup will come and the logic for the report copied from Sap technical Site

 

 

Code for the Program

*&---------------------------------------------------------------------*
*& Report  Z_SMART_FORM
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

report  z_smart_form.
tables: crmd_orderadm_h,
zcr4_tab.

types:
begin of ty_zcr4_tab,
mandt
type mandt,
objtype_a
type swo_objtyp,
objkey_b
type crmt_doc_flow_id_wrk,
end of ty_zcr4_tab.
data: wa_zcr4_tab
type  ty_zcr4_tab,
it_zcr4_tab
type table of ty_zcr4_tab.
data: f1
type zcr4_tab-objtype_a,
f2
type zcr4_tab-objkey_b.

selection-screen begin of block crm with frame title text-001.

select-options: obj_id
for crmd_orderadm_h-object_id no intervals no-extension.

selection-screen end of block crm.

data: wa_cparam
type ssfctrlop,
wa_output
type ssfcompop,
ssf_name
type tdsfname,
func_mod_name
type rs38l_fnam.

data: es_output_orderadm_h     
type  crmt_output_orderadm_h_com,
es_output_activity_h     
type  crmt_output_activity_h_com,
es_output_lead_h         
type  crmt_output_lead_h_com,
es_output_opport_h       
type  crmt_output_opport_h_com,
es_output_orgman_h       
type  crmt_output_orgman_h_com,
es_output_pricingdata_h  
type  crmt_output_pricingdata_h_com,
es_output_sales_h        
type  crmt_output_sales_h_com,
es_output_shipping_h     
type  crmt_output_shipping_h_com,
et_output_partner_h      
type  crmt_output_partner_h_comt,
et_output_payplan_d_h    
type  crmt_output_payplan_d_h_comt,
es_output_customer_h     
type  crmt_output_customer_h_com,
es_output_cumulat_h      
type  crmt_output_cumulat_h_com,
et_output_billing_h      
type  crmt_output_billing_h_comt,
et_output_cancel_h       
type  crmt_output_cancel_h_comt,
et_output_appointment_h  
type  crmt_output_appointment_h_comt,
et_output_billplan_d_h   
type  crmt_output_billplan_d_h_comt,
et_output_billplan_h     
type  crmt_output_billplan_h_comt,
et_output_status_d_h     
type  crmt_output_status_d_h_comt,
et_output_srv_subject_h  
type  crmt_output_srv_subject_h_comt,
et_output_srv_subject_i  
type  crmt_output_srv_subject_h_comt,
et_output_srv_refobj_h   
type  crmt_output_srv_refobj_h_comt,
et_output_srv_refobj_i   
type  crmt_output_srv_refobj_h_comt,
et_output_srv_reason_h   
type  crmt_output_srv_reason_h_comt,
et_output_srv_result_h   
type  crmt_output_srv_result_h_comt,
es_output_acs_h          
type  crmt_acs_h_com,
et_output_orderadm_i     
type  crmt_output_orderadm_i_comt,
et_output_orgman_i       
type  crmt_output_orgman_i_comt,
et_output_pricingdata_i  
type  crmt_output_pricingdata_i_comt,
et_output_pricing_i      
type  crmt_output_pricing_i_comt,
et_output_product_i      
type  crmt_output_product_i_comt,
et_output_sales_i        
type  crmt_output_sales_i_comt,
et_output_shipping_i     
type  crmt_output_shipping_i_comt,
et_output_schedlin_i     
type  crmt_output_schedlin_i_comt,
et_output_partner_i      
type  crmt_output_partner_i_comt,
et_item_cstics_i         
type  crmt_item_cstics_tab,
et_output_customer_i     
type  crmt_output_customer_i_comt,
et_output_billing_i      
type  crmt_output_billing_i_comt,
et_output_cancel_i       
type  crmt_output_cancel_i_comt,
et_output_finprod_i      
type  crmt_output_finprod_i_comt,
et_output_ordprp_i       
type  crmt_output_ordprp_i_comt,
et_output_ordprp_i_d     
type  crmt_output_ordprp_i_d_comt,
et_output_ordprp_objl_i_d
type  crmt_output_ordprp_objl_i_comt,
et_output_appointment_i  
type  crmt_output_appointment_i_comt,
et_output_billplan_d_i   
type  crmt_output_billplan_d_i_comt,
et_output_billplan_i     
type  crmt_output_billplan_i_comt,
et_output_orderadm_i_qt  
type  crmt_output_orderadm_i_qt_comt,
et_output_orderadm_i_in  
type  crmt_output_orderadm_i_in_comt,
et_output_schedlin_i_cf  
type  crmt_output_schedlin_i_cf_comt,
et_output_status_i       
type  crmt_output_status_i_comt,
et_output_service_i      
type  crmt_output_service_i_comt,
et_output_wrk_set_e_s_bbp
type  /1cn/working_set_e_s_bbp_t,
ev_language              
like  sy-langu,
et_output_service_i_assi 
type  crmt_output_service_i_***_comt,
es_output_buag_h         
type  crmt_output_buag_h_com,
et_output_buag_i         
type  crmt_output_buag_i_comt,
et_output_price_agmts_h  
type  /1cn/working_set_e_s_crm_t,
et_output_price_agmts_i  
type  /1cn/working_set_e_s_crm_t,
et_output_status_h       
type  crmt_output_status_h_comt,
guid                     
type  crmt_object_guid.

*data : f2 type TDSFNAME.
* data: f1 type char10 VALUE 'ID1'.

initialization.

*set parameter id 'SSFNAME' field f2.
*  IMPORT f1 FROM MEMORY ID F1.
*  IMPORT f2 = f2 FROM MEMORY ID F1.

*obj_id-low = f2.
start-of-selection.

select single guid into guid from crmd_orderadm_h where object_id = obj_id-low.

select mandt
objtype_a
objkey_b
from zcr4_tab into table it_zcr4_tab .
loop at it_zcr4_tab into wa_zcr4_tab.
f1 = wa_zcr4_tab-objtype_a.
f2 = wa_zcr4_tab-objkey_b.
endloop.
*delete it_ZCR4_TAB .

*IF it_ZCR4_TAB IS NOT INITIAL.
*delete  ZCR4_TAB from  TABLE it_ZCR4_TAB.
*ENDIF.
*delete  ZCR4_TAB from wa_ZCR4_TAB. """""""""
*  delete  ZCR4_TAB FROM  WHERE OBJTYPE_A = wa_ZCR4_TAB-OBJTYPE_A AND
*        OBJKEY_B  = wa_ZCR4_TAB-OBJKEY_B .

if it_zcr4_tab is not initial.
delete  zcr4_tab from  table it_zcr4_tab.
endif.

if not guid is initial.

wa_cparam-no_dialog =
'X'.
wa_cparam-preview =
'X'.
wa_output-tddest =
'LP01'.
wa_output-tdtitle =
'Order Confirmation'.
wa_output-tdnewid =
'X'.
wa_output-tdimmed =
'X'.
wa_output-tdcopies =
'001'.

***  CALL FUNCTION 'CRM_OUTPUT_SINGLE_READ'
***    EXPORTING
***      iv_smart_form             = 'ZSMART_FORM_DEMO'
***      iv_guid                   = guid
***    IMPORTING
***      es_output_orderadm_h      = es_output_orderadm_h
***      es_output_activity_h      = es_output_activity_h
***      es_output_lead_h          = es_output_lead_h
***      es_output_opport_h        = es_output_opport_h
***      es_output_orgman_h        = es_output_orgman_h
***      es_output_pricingdata_h   = es_output_pricingdata_h
***      es_output_sales_h         = es_output_sales_h
***      es_output_shipping_h      = es_output_shipping_h
***      et_output_partner_h       = et_output_partner_h
***      et_output_payplan_d_h     = et_output_payplan_d_h
***      es_output_customer_h      = es_output_customer_h
***      es_output_cumulat_h       = es_output_cumulat_h
***      et_output_billing_h       = et_output_billing_h
***      et_output_cancel_h        = et_output_cancel_h
***      et_output_appointment_h   = et_output_appointment_h
***      et_output_billplan_d_h    = et_output_billplan_d_h
***      et_output_billplan_h      = et_output_billplan_h
***      et_output_status_d_h      = et_output_status_d_h
***      et_output_srv_subject_h   = et_output_srv_subject_h
***      et_output_srv_subject_i   = et_output_srv_subject_i
***      et_output_srv_refobj_h    = et_output_srv_refobj_h
***      et_output_srv_refobj_i    = et_output_srv_refobj_i
***      et_output_srv_reason_h    = et_output_srv_reason_h
***      et_output_srv_result_h    = et_output_srv_result_h
***      et_output_status_h        = et_output_status_h
***      es_output_acs_h           = es_output_acs_h
***      et_output_orderadm_i      = et_output_orderadm_i
***      et_output_orgman_i        = et_output_orgman_i
***      et_output_pricingdata_i   = et_output_pricingdata_i
***      et_output_pricing_i       = et_output_pricing_i
***      et_output_product_i       = et_output_product_i
***      et_output_sales_i         = et_output_sales_i
***      et_output_shipping_i      = et_output_shipping_i
***      et_output_schedlin_i      = et_output_schedlin_i
***      et_output_partner_i       = et_output_partner_i
***      et_item_cstics_i          = et_item_cstics_i
***      et_output_customer_i      = et_output_customer_i
***      et_output_billing_i       = et_output_billing_i
***      et_output_cancel_i        = et_output_cancel_i
***      et_output_finprod_i       = et_output_finprod_i
***      et_output_ordprp_i        = et_output_ordprp_i
***      et_output_ordprp_i_d      = et_output_ordprp_i_d
***      et_output_ordprp_objl_i_d = et_output_ordprp_objl_i_d
***      et_output_appointment_i   = et_output_appointment_i
***      et_output_billplan_d_i    = et_output_billplan_d_i
***      et_output_billplan_i      = et_output_billplan_i
***      et_output_orderadm_i_qt   = et_output_orderadm_i_qt
***      et_output_orderadm_i_in   = et_output_orderadm_i_in
***      et_output_schedlin_i_cf   = et_output_schedlin_i_cf
***      et_output_status_i        = et_output_status_i
***      et_output_service_i       = et_output_service_i
***      et_output_wrk_set_e_s_bbp = et_output_wrk_set_e_s_bbp
***      ev_language               = ev_language
***      et_output_service_i_assi  = et_output_service_i_assi
***      es_output_buag_h          = es_output_buag_h
***      et_output_buag_i          = et_output_buag_i
***      et_output_price_agmts_h   = et_output_price_agmts_h
***      et_output_price_agmts_i   = et_output_price_agmts_i.



call function 'SSF_FUNCTION_MODULE_NAME'
exporting
formname =
'ZSMART_FORM_DEMO'
importing
fm_name  = func_mod_name.

call function func_mod_name
exporting
control_parameters  = wa_cparam
output_options      = wa_output
user_settings       =
'X'
orderadm_h          = es_output_orderadm_h
activity_h          = es_output_activity_h
opport_h            = es_output_opport_h
orgman_h            = es_output_orgman_h
partner_h           = et_output_partner_h
pricingdata_h       = es_output_pricingdata_h
sales_h             = es_output_sales_h
shipping_h          = es_output_shipping_h
payplan_d_h         = et_output_payplan_d_h
cumulat_h           = es_output_cumulat_h
customer_h          = es_output_customer_h
acs_h               = es_output_acs_h
billing_h           = et_output_billing_h
cancel_h            = et_output_cancel_h
appointment_h       = et_output_appointment_h
billplan_d_h        = et_output_billplan_d_h
billplan_h          = et_output_billplan_h
status_d_h          = et_output_status_d_h
status_h            = et_output_status_h
srv_subject_h       = et_output_srv_subject_h
srv_reason_h        = et_output_srv_reason_h
srv_result_h        = et_output_srv_result_h
orderadm_i          = et_output_orderadm_i
orderadm_i_qt       = et_output_orderadm_i_qt
orderadm_i_in       = et_output_orderadm_i_in
orgman_i            = et_output_orgman_i
pricingdata_i       = et_output_pricingdata_i
pricing_i           = et_output_pricing_i
product_i           = et_output_product_i
sales_i             = et_output_sales_i
schedlin_i          = et_output_schedlin_i
schedlin_i_cf       = et_output_schedlin_i_cf
shipping_i          = et_output_shipping_i
partner_i           = et_output_partner_i
item_cstics_i       = et_item_cstics_i
customer_i          = et_output_customer_i
billing_i           = et_output_billing_i
cancel_i            = et_output_cancel_i
finprod_i           = et_output_finprod_i
ordprp_i            = et_output_ordprp_i
appointment_i       = et_output_appointment_i
billplan_d_i        = et_output_billplan_d_i
billplan_i          = et_output_billplan_i
status_i            = et_output_status_i
working_set_e_s_bbp = et_output_wrk_set_e_s_bbp
language            = ev_language
exceptions
formatting_error    =
1
internal_error      =
2
send_error          =
3
user_canceled       =
4
others              = 5.
if sy-subrc <> 0.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
endif.

else.
message 'Ticket does not exists' type 'I'.
endif.

Custom Dashboard on Home screen

$
0
0

Requirement: Displaying Orders on Home screen of ADMIN or ASM (Area Sales Manager ) to review an order with the Incentive Amount(custom field which is having some ordering value) and then approve/reject the Incentive Amount in the order justifying the reason behind it by entering the Incentive Amount  Rejection note. The ordering process is made very simple where SR needs to choose the Sold to Party, relevant products, enter product quantity and Incentive Amount while the other information get auto determined. The Order with Incentive Amount gets approved, First by ASM (Sales Manager) and then by ADMIN. This entire approval process of Orders in the CRM dashboard is extremely time and effort efficient.

 

Orders displayed in Custom Dashboard are based on:

•Display Custom Dashboard only to few countries

•Orders Should have the OMV value

•Custom Dashboard is enabled to only ASM/ADMIN

•Approve/Reject by ASM/ADMIN for the orders

 

Go to Component: BT115H_SLSO

Create a new view Zorderdashboard with required fields.

1.JPEG.png

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Now write the logic to get the orders based on territory and shop money value and status.

Write the logic in DO_PREPARE_OUTPUT.

1) Get the user logged in check he is ADMIN/ASM

2) Read all territories of the user by using FM CRM_TERRMAN_TERR_BY_EMP_OBJ.

 

2.JPEG.png

 

 

3) Fetch all the territories that are assigned under ASM/ADMIN

 

3.JPEG.png

 

4) Fetch all the employees of the territory and by using the CRMD_ORDER_INDEX table get all the orders.

5) Get the order details by using FM: CRM_ORDER_READ.

 

4.JPEG.png

6) Then prepare the structure to display them in UI

 

5.JPEG.png

 

The view needs to be displayed in home screen

So to display one view in another component we need to create the Component usage.

Create a window for the dashboard view

 

6.JPEG.png

 

Now go to run time repository:

Add the window and the Dashboard view

 

7.JPEG.png

 

Now add the interface view

8.JPEG.png

 

 

Now add the interface view to component usage

 

9.JPEG.png

 

Now add the same component usage in Home screen component.

Go to home screen component: WCC_SLS_HOME

Go to Run time repository and add the component usage

 

10.JPEG.png

10.JPEG.png

 

Now the view will be seen in the home screen.

Logic to enable the view only to ASM/ADMIN will be written in below methods:

DETACH_STATIC_OVW_VIEWS/REATTACH_STATIC_OVW_VIEWS

 

So in DETACH_STATIC_OVW_VIEWS Method

We should add the logic not to remove the custom view for ADM/ASM

View id is component usage.viewname

Component usage is CU_ORDERDASHBOARD

View name: ZBT115H_SLSO_P/ZWinDashboard

 

1.png

 

And for the method REATTACH_STATIC_OVW_VIEWS

We should add the logic to add the custom view for ADM/ASM

 

2.jpeg.png

Now open the UI:

Configure the custom View in page configuration:

3.jpeg.png

2.png

 

Take the order 5004721 and reject it.

 

1.jpeg

 

Rejected order disappears from the screen after entering the rejected notes.

Check in GUI the rejected notes:

3.png

Now approve the order 5004799

 

3.png

 

 

Check the status in GUI:

 

2.png

 

 

Now login to ADMIN this 5004799 order will be available in ADMIN as it is approved by ASM.

3.png

 

Now approve the order 5004799 and check the status of the order and it will be completed.

 

1.png

 

We need to implement the whole logic of APPROVE/REJECT using events and to store the Rejected Notes/status using the FM CRM_ORDER_MAINTAIN for both ADMIN/ASM.

 

In this way entire approval process of Orders in the CRM dashboard is implemented.

Hope this helps!!


How to configure Account Factsheet

$
0
0

Hi All,

 

I was searching in SCN how to configure Account Factsheet and i thought i can create a blog about configuration for Account Factsheet.

I hope this blog will be useful.

The fact sheet provides you with an immediate and condensed overview of information about business partners taken from several sources such as business partner master data, statistical and transaction data, derived from SAP CRM, SAP ECC, and SAP NetWeaver BI. It enables you to see quickly and easily the most important details about your key customers.

 

There are standard factsheet available, if we need custome we can create our custom factsheet according client requirements

 

Where it is used?

 

After confirming the Account in Account Identification page when user navigated to Factsheet user will get all the details about the confirmed account in IC roles

 

How to configure?

 

Step1: In SPRO Tcode open Maintain factsheet under CRM-> UI Framework -> UI Framework Definition -> Fact sheet -> Maintain Fact sheet

fact3.PNG


Step2: In Factsheet maintain custome factsheet name

Fact1.PNG

Step3: Choose your custom Factsheet and then click on View Assignments and assign the views which all will be needed to show it in Factsheet

fact2.PNG

Step4: Choose Layout if you need custom layout then maintain your custom one with how many rows and colum otherwise use standard layout.

fact4.PNG

Step5: Maintain Grid layout definition for the layout ( if custom )

fact5.PNG

Step6: Create a logical link for your custom Factsheet, parameter should be your custom factsheet ID and target ID should be BSP_DLC_FS

 

fact7.jpg

Step7: Change the logical link ID for Object type ACCOUNTFACTSHEET in Maintain Outbound plug mappings for your custom Navigation bar profile with the custom logical link ID

fact6.jpg

 

Step8: Now the configuration of view for Factsheet should be done in Component BSP_DLC_FS component

 

Open BSP_DLC_FS component in tcode BSP_WD_CMPWB

Choose factsheet view and click on configuration tab

 

Here choose your custom factsheet fromdropdown and click on Seacrh

fact12.PNG

 

Step9: here click on New button and give your Role config key and click next

fact8.PNG

Step10: In Next step choose Page type as Tile and choose your standard or custom layout maintained in step5

 

 

fact9.PNG

 

Step11: Then in Assign Views choose each tile and assign views from available viewsfact10.PNG

 

Step12:click on configuration on each view and create configurations for the views with proper component usage and object type

fact10.PNG

 

Step13: Click on finish to complete configuration

fact11.PNG

 

After the configuration is done for the factsheet the maintained view will be shown in the Factsheet in your Business role WEBUI

 

Regards,

Deepika.

A hidden gem BOL method mergeDuplicateDocument

$
0
0

Once again searching for a solution for a generic solution in the SAP Standard paid off. I've got the business requirement that two existing Service Requests (SR, CRM Object Type BUS2000223) had to be merged. When they are merged, then the leading SR should get a new status and the duplicate should get the status "Cancelled". Using my trusted Google Custom Search- SAP Community Network Search I found the SAP Standard documentation Deduplication of Leads. I followed the description and was able to to get the merge of Leads working in our CRM sandbox system.

 

For a quick test possibility I've created an report and used the coding from method EH_ONMERGE_SEL of class CL_BT108S_L_RESULT_IMPL to replicate the call of the BOL method mergeDuplicateDocument. That way I could already test the merge of SR's without the need to make adjustments in the WebUI.

 

The first issue to get around was the customizing view CRMV_ORDER_DEDUP. It restricts the possible transaction types to leads. As I didn't take the time to investigate further I've simply created my own maintenance view for the table CRMC_ORDER_DEDUP.

 

Then I investigated the Package CRM_ORDER_DEDUP with the implementation to merge Leads (CRM Object Type BUS2000108). But my first attempt to use a custom class as a subclass of CL_CRM_DEDUP_MERGE_LEAD_IMPL failed. I've got a dump in the area of the document flow. Then I've reached out to other CRM Experts via Twitter:

 

Luckily Andrei Vishnevsky replied:

 

 

I've exchanged my investigations with him via E-Mail and he provided a lot of valuable information. So I've created first an interface IF_CRM_DEDUP_MERGE_BUS2000223 (of course with a SSCR-Key needed) and added the instance methods MERGE_STATUS and MERGE_HEADER. For the needed parameters I took the information from the interface IF_CRM_DEDUP_MERGE_BUS2000108. Based on that I've implemented my own merge class with the super class CL_CRM_DEDUP_MERGE. Here it would be great if SAP could provide an implementation of the abstract class CL_CRM_DEDUP_MERGE that could be re-used for other objects. In the moment I had to re-implement the constructor in my class as a copy from CL_CRM_DEDUP_MERGE_LEAD. The rest was straight forward and I've implemented the methods MERGE_STATUS and MERGE_HEADER according to the business requirements.

 

But the execution of my test report still failed with a dump. Via debugging I've found that there where missing entries for SR Object Type BUS2000223 in the customizing view MVOBLRO. So I  took the entries for the Lead:

 

Role TypeObject TypeRepository
CRM_DEDUPBUS2000108BO
CRM_PRIMEBUS2000108BO

 

and create similar ones for the Service Request:

 

Role TypeObject TypeRepository
CRM_DEDUPBUS2000223BO
CRM_PRIMEBUS2000223BO

 

After that adjustment the call of BOL method mergeDuplicateDocument went through smoothly.

 

Do you have a similar requirement? Should SAP perhaps provide support for merge in additional CRM Object Types? Let me know via the comments.

How to create Call Back/ Event Handler Function Modules

$
0
0

Hi All,

 

This blog is about how to create and register the Event handler Function modules for our own requirements.

 

How it is used?

 

This Event Handler FM's are useful in many scenarios like

1. When we want to update some field values on save of the Transactions ( Here FM will be called after ORDER_SAVE BADI check is successful so you can confirm that transaction will be saved)

2. When partner value is changing on after change you wnat to perform any update in Transaction

3.You can register this FM for any objects ( ORDERADM_H, ORDERADM_I, CUSTOMER_H, CUSTOMER_I, ... etc)

 

Step1:

How to create the FM

 

Fm should created with name endes with _EC for ex ZTEST_EVENT_SAVE_EC

FM2.PNG

 

Step2:

Created FM must registered for Specific Objects ( CRM_ORDERADM_H, CRM_ORDERADM_I, ..etc)

FM3.jpg

 

Under this register your custom FM for your specific object

FM4.jpg

 

Step3:

 

After registeration Maintain Event handler for which transaction, object and event in SPRO under CRM->Transaction->Basic Settings->Edit Event Handler Table

FM5.jpg

 

After this You can all the registered FM for events

FM6.jpg

 

Click on new entries and maintain the event for your custom FM

FM7.jpg

 

In the above screenshot FM is registered for Object PARTNER , for Event After Change and execution time is Immediately

 

This FM will be called immediately after Partner changed for Service Request Transactions

 

This FM will be called for Header/Item partner changes with importing fields passed are object name, event attributes with old and new values

FM9.jpg

 

In the above screenshot FM is registered for Service Request Transaction types, for object ORDERADM_H and for event SAVE and execution time before saving

 

This FM will be called when Service Request Transaction are saved and before saving the document you want to do any update according to user inputs you can do the code in this custom FM.

 

This FM will be called only once

 

Possib;e call Callback are

 

FM8.jpg

 

After these steps are done FM will be called on registered Events.

 

Regards,

Deepika.

BRF Plus-A real time example

$
0
0

Hi All,

 

Here is a blog on BRF Plus for beginners.

 

What is BRF Plus:

 

"BRFplus (Business Rule Framework plus) is a business rules management system (BRMS) offered by SAP AG. BRFplus is part of the SAP NetWeaver ABAP stack. Therefore, all SAP applications that are based on SAP NetWeaver can access BRFplus within the boundaries of an SAP system.

how you can create a simple BRF Application for the following use case."

 

I believe the above definition would have confused you a bit. Being an SAPier, where we can use a BRF Plus application. That is what all we want to know. . Let's come back to the real world where we have lots of RULES to obey. And BRF is all about rules.

 

Consider an application of salary determination of applicants to a post where the salary and designation of an applicant is getting calculated based on certain rules. And those rules are prone to change. Meaning, in a year itself, there can be many rules for determining the salary. So it's always advisable to segregate rules from the code.

 

We can still go with normal IF ELSE conditions. But when the frequency of change in rules if high, let's leave that to a separate application where even a lay man can manage the rules.

 

I will show you how to create a normal BRF application for the above requirement. Our use case is,

 

Determine the salary for an employee based on the company he is coming from and years of experience he has.

 

In this application, we will see the creation of,

 

1) Data Objects

2) Expressions

3) Functions

4) Rule sets

5) Actions

 

Let's say that a company decides to recruit people from outside and the process of recruitment is already done. But the salary discussion is yet to happen and they have a list of people from different companies as applicants. So, they decide to create a BRF Plus application and it will run with input of company name and years of experience.

 

For this purpose, they have a list of values relevant for this. I will categorize them as 3 tables as in the picture.

 

Tables.JPG

 

As in the above picture, the rule is->If the applicant is belonging to a tier 1 company, then he has to have the experience of 2 years to be an SE while for an applicant from tier 2 company needs 3 years. Also, the salary for the tier 1 company applicant is more than the tier 2 company(Just for the use case example. May not be true in real life. ).

 

So, the rule is set and we know what needs to be done. Now we can go to BRF workbench with tcode BRF+ or BRFPlus or FDT_Workbench.

 

BRF Workbench.JPG

 

I am clicking on 'Create application'.

 

Create application.JPG

 

Keep in mind that I have created as Local object. If we select the storage type dropdown, we can move this across landscape. Following are the types.

 

System-> transportable objects which should not be changed in your system environment.

Customizing-> transportable objects which allow changes.

Master Data-> objects which are local by default and cannot be transported.

 

Click on save and activate. Then,

 

1) Creation of Data objects->Elements/Structures/Tables.

I am creating all data objects required for this example here.

 

Element creation.JPG

I can either create elements one by one or can go with Elements(Mass creation).

 

Activate all the data elements. (I created an internal table also to hold the values from db table.).

Data objects.JPG

2) Create expression required for the function.

As I mentioned at the beginning, we have 3 tables out of which 1 we will make as db table and other two will be created as decision table. Just for showing their operations I am keeping so.

 

So, here we need 2 decision tables and 1 db table. For fetching data from DB table, we would need to create a DB lookup also.

 

Decision table.JPG

I give the input and output of the decision table.

Save it and activate. Now the table is ready. Here we can insert the values to the table.

 

 

In the same way, create one more decision table called YEARS_TABLE with fields as Years of experience, Tier and designation. Make the entries.

 

 

I already have a DB table created and it has the following values.

 

 

Now go with creation of DB lookup in the same way we created the decision table in expressions by choosing Database Lookup.

 

 

Now we are done with the creation of expressions.

 

3) Create functions->This will be the one we will use in our ABAP program/buttons/BADIs etc. It's very similar to FM or methods of a class.

 

 

Now go to tab 'Assigned Rulesets' and create a rule set. And then assign the rules to the rule sets.

 

Here I assign rules one by one. After all the assignment, the screen will look like.

 

 

This means, we have the salary available in the table 'Salary table'.

 

Now go to Simulate function.

 

 

 

Execute it.

 

 

You have the result as follows.

 

 

These are nothing but the fields of my table ZSALARY. (The heading is coming like this since I kept the data elements of fields as standard char20 and all).

 

Now you are done with the creation of a simple BRF application.

 

I will explain on how to send a mail from this application and how to execute it from an ABAP program/BADI in the part 2 of this blog BRF Plus- A real time example - 2

 

Please let me know if you have any queries.

 

Thanks,

Faisal

BRF Plus- A real time example - 2

$
0
0

Hi All,

 

This is a continuation of my first blog  BRF Plus-A real time example

 

In my last blog I shown you on how to create a BRF Plus application and how to simulate the same. Here we will see how we can send the result of the BRF Plus application as an email and how we can execute the same from CRM WebUI or from an ABAP reprot.

 

1) Creation of actions:

In the previous blog, we saw that the result of salary is put into the table called 'Salary table' after executing certain rules. Now if I want to send a mail with the result to a recipient, that can be achieved with actions.

 

 

 

I give the name for action as send_email and save the action. We will get the following screen.

 

 

Here in recipients list, we can give the value from context parameter. For instance you are evaluating something for a BP and the result is to be sent to the email ID of the BP. In that case you can create context parameter email and get the BP's email id using a function and assign that email here in recipients.

 

In Subject, you can give the title and then can give the body of the mail. In order to bring the context parameters to the mail, you can give them like &1, &2, &3 etc. After giving the parameter names, press on 'Refresh message palceholders' button and then you will see the option to assign the values from &, &2 etc.

 

 

Here you can assign the values and send the mail. Save the action and activate it.

 

We are done with sending of mails. Now we have to assign the action to the function.

 

Go to the rule set where you have created all other rules.

 

 

Choose the action we have created. Now the bottom side of rule set will look like this.

 

 

Simulate the application and see the result.

 

Keep in mind that, in simulation mode the actions won't get executed. You should have a COMMIT for actions to get executed.

 

 

Now let's see how can we bring this BRF Plus application to a program/BADI/event handler etc.

 

The first thing to be got is the id of the function which you can get from the 'General' tab.

 

 

Now write the following code (I am considering the example of a report).

 

PARAMETERS:p_comp  TYPE CHAR40,
            p_years TYPE char2.

DATA: lo_fuction    TYPE REF TO if_fdt_function,
       lo_context       TYPE REF TO if_fdt_context,
       lo_result          TYPE REF TO if_fdt_result,
       lt_salary          TYPE zsalary_tt,
       lo_message    TYPE REF TO cx_fdt.


FIELD-SYMBOLS:<fs_salary> TYPE zsalary.
 
START-OF-SELECTION.
   TRY .
       " Get BRFplus function
       lo_fuction ?= cl_fdt_factory=>if_fdt_factory~get_instance( )->get_function( '2C44FD80B9EC1ED4A9F31B316E69AE7A' ).

       " Set the BRFplus function context ( input variables )
       lo_context = lo_fuction->get_process_context( ).
       lo_context->set_value( iv_name  = 'COMPANY_NAME' ia_value = p_comp ).
       lo_context->set_value( iv_name  = 'YEARS_OF_EXP' ia_value = p_years ).

       " Process the BRFplus function
       lo_fuction->process( EXPORTING io_context    lo_context
                            IMPORTING eo_result     lo_result ).

       " Retrieve the BRFplus function result
       lo_result->get_value( IMPORTING ea_value lt_salary ).
     CATCH cx_fdt INTO lo_message.
   ENDTRY.
   READ TABLE lt_salary ASSIGNING <fs_salary> INDEX 1.
   IF sy-subrc = 0.
     COMMIT WORK.
     WRITE: 'Your level is ',
             <fs_salary>-zlevel,
             'And the salary you can get is ',
             <fs_Salary>-salary.


Everything is done now. Go and execute your report.


 

And the output would be,

 

 

Please let me know if you have any queries.

 

 

Thanks,

Faisal

Viewing all 188 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>