Wechat development series 9 – Create C4C Social Media Message and Service within Wechat app

* Wechat development series 1 – setup your development environment
* Wechat development series 2 – development Q&A service using nodejs
* Wechat development series 3 – Trigger C4C Account creation in Wechat app
* Wechat development series 4 – Send C4C Data change notification to Wechat app
* Wechat development series 5 – embedded your UI5 application to Wechat app
* Wechat development series 6 – Retrieve Wechat User info via oAuth2 and display it in UI5 application
* Wechat development series 7 – use Redis to store Wechat conversation history
* Wechat development series 8 – Map integration
* Wechat development series 9 – Create C4C Social Media Message and Service within Wechat app

* Implemented feature

* Implementation detail

* How to create individual customer, social media user profile, and bind both together
* How to create social media message once end user sends a text to Wechat subscription account

In the second blog of this series, Wechat development series 2 – development Q&A service using nodejs we have developed a kind of Q&A service in Wechat.

In this blog, let’s replace the Q&A service hosted by tuning API with another style: everytime the end user of your test subscription account has sent some message to it, then create a corresponding social media message in C4C system ( and a new service request will also be automatically created together with that social media message ).

Implemented feature

1. When the end user has subscribed the Wechat account by pressing “Subscribe(关注)” button: In C4C system an individual customer for this end user who has subscribed the Wechat test account will automatically be created, together with a new social media user profile created as well and bound to this individual customer. The end user will receive a notification about created customer ID and profile ID once creation finishes in C4C system. The created information could also be monitored in console: The created customer could be found in C4C system with a custom Channel type displayed as “Jerry’s Wechat Channel”. The created social media user profile bound to this customer could be found in tab “Social Profiles”: 2. As long as end user sends some new text to the Wechat subscription account, a new social media message will be created in C4C. The end user will receive a notification in Wechat about the created message ID, in my example it is 59160. The created social media message could be found in tab “Social Media Messages”: And a new ticket is also automatically created for this social media message. The creation of this ticket is a standard behavior of C4C, no custom development is necessary.

Implementation detail

The feature described so far is developed based on the existing project used in previous blogs. You must first create a custom channel for Wechat in Business Configuration: Write down this channel type code 905 for created Wechat channel: And create a concrete channel based on the 905 custom channel type created just now, write down this channel ID ZJERRYWC as well.

How to create individual customer, social media user profile, and bind both together

In my github repository for this Wechat development series, module createAccountInC4C.js is responsible for individual customer creation. Once finished, it will call another two modules highlighted below to create social media user profile and link the user profile with created individual customer. For social media user profile creation, the ID of previous created custom channel for Wechat, 905, is hard coded in module implementation createSocialMediaProfile.js. In createAccountInC4C.js, these three steps are clearly mentioned in the comment and implementation: And createAccount will be called for event handler of “subscribe” event:

How to create social media message once end user sends a text to Wechat subscription account

The message creation is implemented in module createSocialMediaActivity.js. In unittest folder there is a unit test written to demonstrate how to use this module:

* o0KlM1i2_4-zHRmDk-IWGRlA1Cjc: the wechat ID of user who has sent the text to the test Subscription account
* 1003: the ID of new social media message to be created
* 6066: social media user profile of current wechat user who has sent the text to the test Subscription account
* ZJERRYWC: the custom Wechat channel ID created previously in C4C system
* “Hello C4C Agent….”: the text of social media message to be created

Call this createSocialMessage module in the event handler when a new text is sent by Wechat end user. http://bit.ly/2DdqyWW #SAP #SAPCloud #AI

Mysterious Memberlists in Lumira Designer

Dimension members are one of the basic concepts in any analytics application. Every analytical app, dashboard or self-service tool contains member lists: inside of a Crosstab, on a chart axis or legends, on a filter control and so forth.

Therefore one of the very first features in Design Studio was an API to retrieve a member list and display it in a listbox, combobox etc. The API getMemberList is in the meantime replaced by getMembers. Let’s assume that our data source DS_1 contains a dimension “customer_id” and we have a listbox LISTBOX_1 that we want to fill with the members from dimension “customer_id”. This is easy and cab be done with the following script: var members = DS_1.getMembers(“customer_id”, 100); members.forEach(function(member) { LISTBOX_1.addItem(member.internalKey, member.text); });

What is interesting with this script?

* The functions has an argument to limit the number of members to be fetched. Dimensions can have a huge number of members and retrieving them can be very expensive.

* getMembers returns a array of quite interesting objects. They contains quite interesting fields and one method

The fields of a Member objects contain so-called presentations. Each member could have several, including several key types and texts with different length. Which text is returned by the “text” field could be e.g. configured in BEx Query Designer.

Very powerful is the function getAttributeMember. It allows you to access content from a so-called Display Attribute for each member. Let’s extend our sample to also show the birthdates of our customers: var members = DS_1.getMembers(“customer_id”, 100, [“birthdate”]); members.forEach(function(member) { var birthdayAttributeMember = member.getAttributeMember(“birthdate”); LISTBOX_1.addItem(member.internalKey, member.text + “ (” + birthdayAttributeMember.text + “)”); });

You see that I have added “birthdate” twice – ones as an array at the optional last parameter of getMembers. And as argument of getAttribributeMember. If I would remove the third parameter of getMembers, the script would still work, but would run much much slower! If you pass the display attributes that you need to getMembers, they will be retrieved with as part of the single roundtrip from BW or HANA. If you try to get them later, you might end up with an additional roundtrip per customer!

Also attribute members have multiple presentations, including keys and texts, therefore you have the same fields available. But and attribute member can’t reference another attribute member.

Filter Behavior

Let’s now see how our member list reacts on active filters. If I modify my script like this, what happens? Will getMembers return only one entry? DS_1.setFilter(“customer_id”, “3872”); var members = DS_1.getMembers(“customer_id”, 100); members.forEach(function(member) { LISTBOX_1.addItem(member.internalKey, member.text ); });

No! The result is completely unchanged. Strange, isn’t it? But if I have a Crosstab or Chart in my app, I’ll see that the filter is work. It should only one customer.

Let’s try something else: DS_1.setFilter(“city”, “Albany”); var members = DS_1.getMembers(“customer_id”, 100); members.forEach(function(member) { LISTBOX_1.addItem(member.internalKey, member.text ); });

Now we might get a different result – or even the same. It depends on the “Member Access Mode” or “Read Mode of Members for Filtering”. You can configure it per dimension, e.g. in Initial View Editor:

You see that I have configured “Values in Master Data”. This means that filters have no influence on member lists at all, they contain always all members from master data.

The available modes depend on the data source type. The picture shows a HANA view. BW queries and Lumira Offline Datasets have different modes.

In Lumira 2.1 you can also set the Member Access Mode also with scripting: DS_1.setMemberAccessMode(“customer_id”, “MODE_POSTED_VALUES”); DS_1.setFilter(“city”, “Albany”); var members = DS_1.getMembers(“customer_id”, 100); members.forEach(function(member) { LISTBOX_1.addItem(member.internalKey, member.text ); });

Now my listbox will only contain customers from Albany. Obviously the result of getMembers depends on filters of other dimensions but not on filters of the current dimension itself.

Members for Filtering

This behavior is by intention. APIs like getMembers are intended to provide some UIs for choosing the members for filter operations. If the memberlist itself would be filtered, the UI would be unusable – after you have filtered for a member you can never come back to another filter state.

You can nicely try it out using several list boxes to simulate a Facet Filter component. Each listbox is filled with members from a different dimension. Clicking some entries would include the member into the filter. You could achieve this with scripting. Much easier however is to use Property Binding. Internally Property Binding uses exactly the same functionality as getMembers.

Create a listbox and drag-and-drop the data source component to it. You will be prompted for a dimension. Repeat the step for all other dimensions. Configure all dimension using “Only values with Posted Data”. In Initial View editor you might need to but them to the “Background Filter” basket.

As a result you have an app with cascading facet filters.

Members from Resulset

We learned that getMembers works, but it is intended to create UIs  that configure filters. Therefore in many cases it might get the wrong result and it is unnecessarily time consuming.

If you want to get exactly the members that you see in a Crosstab or Chart, you should retrieve the members from the resultset. This has two advantages:

* All filters are applied. You get exactly the members that you see in the Crosstab.
* It is much faster, as the resultset is usually anyway available.

Use the following script. Details about the resultset and the API getDataSelections can be found in this blog. var selections = DS_1.getDataSelections({ “customer_id”: “?” }); selections.forEach(function(selection) { var member = DS_1.getMember(“customer_id”, selection); LISTBOX_1.addItem(member.internalKey, member.text); });

With “member” you can do the same things as show above, e.g. retrieving other presentations and display attributes. However you should avoid using display attributes that are currently not shown in your resultset, as this could cause extra round trip to BW or HANA.

The members are retrieved as if you would search in a Crosstab in English reading order: first left-to-right then top-to-bottom. If the dimension is not the first on a its axis, the order might be a bit unexpected. The duplicate members that could also appear in such a case are automatically removed. The following picture shows this case: The “customer_id” dimension is placed as second dimension in rows axis after “city” using Key and Text presentations.

Getting a Single Member

Sometimes you have a member key and you need some presentation (e.g. the text) or a Display Attribute. The following snippet uses a constant key, but you can also use a variable. var member = DS_1.getMember(“customer_id”, { “customer_id”: “3872” }); APPLICATION.alert(member.text + “ ” + member.getAttributeMember(“birthdate”).text);

Again the warning: getAttributeMember can make the app very slow if used to frequently on data that is not yet cached.

Conclusion

It is important to understand that there are two ways how you can retrieve members:

* for filter UIs using getMembers
* from resulset using getDataSelections and getMember

I have explained the differences, advantages and disadvantages. I’m curious if there are other use cases that require new features or APIs for coming release. I’m looking forward to your comments.

  http://bit.ly/2DKxeNz #SAP #SAPCloud #AI