SAP产品的Field Extensibility

SAP开发人员的工作职责,除了实现软件的功能性需求外,还会花费相当的精力实现一些非功能性需求,来满足所谓的SAP Product Standard(产品标准)。这些产品标准,包含在SAP项目实施中大显身手的Extensibility,为客户业务流程的安全运行保驾护航的Security,也有看起来不太起眼,但实际上体现了SAP作为一家伟大企业所具有的社会担当的Accessibility,以及Internationalization等等。

今天咱们就来说说SAP产品的Extensibility。尽管SAP产品对现实世界中的业务流程做了一定程度的抽象,但是部分客户在实际使用过程中仍然会发现自己企业有些特殊的业务流程无法用SAP产品的标准功能来支持。此时SAP产品的Extensibility就有了用武之地。所谓Extensibility,即SAP产品的底层框架提供了足够的灵活性,确保客户和实施伙伴借助SAP提供的标准工具,能够对SAP产品进行增强(Enhancement),以此来满足自己特殊的业务需求。这种增强和直接修改SAP产品(Modification)的区别在于,前者是SAP推荐的方式,增强实现本身和被增强的SAP标准资源是不同的实体,分别存储于不同的包内。每次版本升级时,增强实现本身不会受到任何影响;而修改,则改动了SAP产品的标准代码或模型,每次版本升级这些修改都会全部丢失掉。而且在基于Netweaver的Cloud产品里,比如S/4HANA Cloud和SAP Cloud for Customer,客户和实施伙伴没有任何途径直接去修改Netweaver后台的资源。

SAP产品的Extensibility分为Field ExtensibilityProcess Extensibility

Field Extensibility,即用户可直接在浏览器里,通过简单的步骤在UI上期望的区域创建新的字段,我们称其为扩展字段(Extension Field)。有时候我们又会在这个术语前加上Simple的前缀,这个前缀强调,客户在创建新字段时,既不需要具备任何技术知识,也不需要了解该产品底层的数据模型的设计细节,而是通过类似我们在Windows系统下安装软件的向导一样,通过向导提示的简单步骤即可完成。

Process Extensibility,强调流程的增强,即通过SAP提供的增强工具,比如Business Add-In(BAdI), Post-Exit等等,对SAP产品的标准流程做一定程度的增强。没有接触过BAdI和Post-Exit的朋友们,如果做过Java Spring开发,可以把BAdI类比成Spring里各种各样的hook,把Post-Exit类比成Spring AOP(面向切面编程)。

本文会介绍SAP CRM和S/4HANA的Field Extensibility。本文的后续,SAP Cloud for Customer(C4C)的Field Extensibility,会由Jerry的同事,SAP成都研究院C4C开发团队的Boris来介绍。

SAP CRM Field Extensibility

SAP CRM的扩展字段的创建通过Application Enhancement Tool(AET)完成。用户在创建扩展字段之前,先要进入配置模式(Configuration mode),然后可以进行扩展字段创建的UI会自动被高亮。

点击高亮区域后,即可看到创建字段的按钮。点Create Field进入扩展字段的创建向导:

这里需要维护待创建扩展字段的明细信息,比如字段标签,字段类型,长度,字段所在的BO模型(下图的ORDERADM_H)等等。

上图我创建了一个标签为city name的扩展字段,保存并激活后,在UI上显示如下。

这一切步骤仅仅几分钟内就可完成,然而背后发生的事情远远没有这么简单。为了实现Simple Field Extensibility,SAP开发人员需要进行一系列的开发,我们称其为Extensibility Registration and Enablement。这种开发需要SAP Extensibility框架开发人员SAP应用开发人员双方共同参与。因为尽管从客户眼中看到的效果,仅仅是UI新出现了一个扩展字段,然而这只是冰山一角——后台的数据库表,BO模型和每一个交互层相关的接口数据结构也应该自动被该扩展字段所增强。

下面是一些例子,我在UI创建的标签为city name的扩展字段,自动出现在后台数据库表中:

和CRM Order相关的函数的接口结构也自动包含了这个扩展字段:

One Order的BO模型的BTAdminH节点也自动被这个扩展字段增强。

那么Extensibility框架怎么知道当扩展字段被创建时,这些属于某个应用程序的资源也需要被增强呢?原来,SAP Extensibility框架开发人员和SAP应用开发人员达成了一个契约:Extensibility框架开发人员定义了一个注册表,应用开发人员如果想让自己负责的UI支持Simple Field Extensibility,需要把自己应用的各种需要被框架自动增强的模型信息和数据库表信息填写到这个注册表里,这样当用户使用AET工具进行增强时,Extensibility框架就知道到底有哪些应用层相关的模型也需要自动被增强。

这个注册表的外观见下图:

除了注册之外,应用开发人员还有很多其他事情要做,因此SAP内部有个Application Extensibility Guideline的编程规范,我们做开发时就是照着这个文档来的。

如果实施伙伴自己开发了一个UI,也想让其支持Simple Field Extensibility,那么也需要按照这个Guideline来开发。

我以前做过一个例子供大家参考:

注册表的填写:

https://blogs.sap.com/2013/10/25/step-by-step-to-enable-your-genil-component-with-aet

按照Application Extensibility Guideline让您的应用支持Simple Field Extensibility

https://blogs.sap.com/2013/10/25/step-by-step-to-enable-your-genil-component-with-aet-part-two/

有的CRM顾问朋友们会不时问我,"为什么我的AET UI看不到Create Field的按钮,或者变成灰色了?”其实原因不外乎下面三种:

您的用户没有AET使用权限

您的系统上AET没有正确setup起来

您选择的UI不可被AET增强(即UI对应的UI没有注册成"可扩展”)

在给SAP提incident之前,可以按照我这篇博客的步骤,自行去调试找到原因:

https://blogs.sap.com/2013/09/29/inside-aet-why-create-field-button-is-visible-in-some-ui-while-invisible-in-others/

用AET创建的这些扩展字段,在运行时是如何画到UI上的呢?

简单地说,CRM WebClient UI的视图配置信息(即视图上需要显示的字段明细,字段间的相对位置,字段标签等等),都维护在一个所谓Configuration Context的实体中,以16位的GUID标识。

Context内容以XML格式存储在数据库表BSP_DL_XMLSTRX2里。

运行时,UI框架首先从上述数据库表里把XML数据读出来,解析成DOM, 然后根据DOM里每个节点对应的不同UI控件类型,实例化不同的UI控件。比如XML里定义的某个字段类型为inputfield, 则创建一个CL_THTMLB_INPUTFIELD类的实例。

更多关于扩展字段在运行时的渲染原理讲解,请参考我的博客:

https://blogs.sap.com/2016/12/22/how-extension-field-created-by-aet-is-rendered-in-web-client-ui/

SAP S/4HANA Field Extensibility

和SAP CRM在具体的应用程序UI上直接创建扩展字段稍有不同,S/4HANA通过一个统一的Tile(Custom Fields and Logic)作为入口来创建扩展字段:

扩展字段的明细维护界面和SAP CRM的AET工具大同小异。下图的Business Context指用户希望这个字段出现在S/4HANA Fiori应用,Product Master的明细界面的General区域内。

扩展字段创建完毕后,客户进到Product Master明细页面内,点击右键然后从Available Fields列表里选择出刚才创建的扩展字段,即可将此扩展字段显示在Fiori UI上。

S/4HANA的应用开发人员需要做的事情和前一章节介绍的SAP CRM类似,同样需要做注册。

下图是S/4HANA Extensibility的注册表。高亮的一行,代表我在扩展字段创建对话框从Business Context下拉菜单里选中的"Product Master General":

上述注册表针对Product Master General维护了两个ABAP DDIC include结构,意思是一旦这个扩展字段创建保存后,会自动出现在这两个DDIC include结构上。

从注册表上方高亮的标签页还可看出,在S/4HANA里通过浏览器创建的这些扩展字段,除了直接显示在Fiori UI之外,还能放到CDS view,OData模型,Web Service,IDoc这些模型中去。注册表里出现的这些选项仅仅表明它们可以支持用Extensibility扩展框架添加扩展字段,至于是否真正把扩展字段放进这些模型里去,则由客户自行决定。通过点击Enable Usage按钮即可将扩展字段添加到对应模型中去。

那么显示在Fiori UI上的S/4HANA扩展字段,在运行时又是如何被渲染出来的呢?为了回答这个问题,我们先分析当我们把扩展字段添加到Fiori UI时,Fiori UI发送给S/4HANA后台的HTTP请求到底包含了哪些信息。

表明该扩展字段在Fiori UI视图中的实现类型为Smart Field。什么是Smart Field?它也是UI5提供的控件之一,但和sap.m.Button, sap.m.Input这些拥有具体类型的UI控件不同,Smart Field在XML视图开发阶段,并没有和任何具体的UI显示控件绑定,实际上只是一个占位符。

下图是一个Smart Field的例子,仅仅凭借这个XML视图片段,我们根本不知道id为idPrice的Smart Field,在运行时到底会被渲染成一个什么样的UI5控件。相反,该控件的类型,在运行时才能决定下来,取决于其绑定的字段Price在OData模型的元数据中具有何种注解(annotation)。

在我的例子里,字段Price在元数据中被注解为一个拥有单位的Decimal字段,其配套的单位字段为OData模型里另一个字段:CurrencyCode。

因此在运行时,这个Smart Field会被UI5框架渲染成两个UI5控件,一个控件显示价格的金额, 绑定到OData模型上的字段Price,另一个控件显示价格单位,绑定到OData模型的字段CurrencyCode

更多Smart Field和渲染逻辑的讲解,请参考我的博客:

https://blogs.sap.com/2016/03/14/currency-example-how-smart-field-works/

2. id

什么是Smart Template的ObjectPage?请参考我的博客:

https://blogs.sap.com/2016/05/03/my-understanding-about-how-object-page-in-smart-template-is-rendered/

3. YY1_JDKminimumversionJ_PRD

Fiori UI扩展字段绑定的OData模型的字段名称。我们可以做个实验:在Fiori UI上该扩展字段里随便维护一个值,比如"1.7", 然后保存。关掉UI再重新打开,很容易在Chrome开发者工具里观察到从后台返回的OData响应结构里,有一个名为"YY1_JDKminimumversionJ_PRD"的字段包含了"1.7"这个值。Fiori UI的扩展字段正是绑定到了该模型字段上,因而能显示出"1.7"。

4. fileName

5. layer:CUSTOMER,packageName:$tmp

这几个字段需要联合起来解释。前面CRM章节已经介绍过,SAP CRM WebClient UI视图的配置信息,以XML的格式维护在后台数据库表中。然而S/4HANA Fiori应用因为基于UI5开发,不存在这种配置信息对应的存储数据库表,而是用文件的方式,把扩展字段和Fiori UI的对应关系存储起来,放到一个特殊的仓库里。文件的内容大体上就是我现在正在介绍的从Chrome开发者工具里观察到的JSON字符串,文件存储的区域称为LREP(Layered Repository)。LREP实际是ABAP实现的一个文件系统,可以用report /UIF/GET_CHANGES_4_TARGET浏览其内容。

执行report,最醒目的就是这几个layer,这也是LREP命名的由来,一个分层的文件系统。

Vendor layer:即SAP layer,包含SAP发布的标准内容。

Partner layer:Partner可以基于SAP layer的内容做增强。同CRM AET一样,Partner的增强可以通过配置放到一个可传输的ABAP包里,Partner在Fiori UI上创建的扩展字段均存储在这个ABAP包内,从开发系统传到测试和生产系统。

Customer layer:客户通过S/4HANA的扩展工具做的增强,一般都配置为存储于$tmp包内,不可传输,对同一系统的其他所有用户均可见。

Draft layer:和本文主题无关,用于S/4HANA的Draft概念处理,参考SAP help:https://help.sap.com/viewer/468a97775123488ab3345a0c48cadd8f/1709%20002/en-US/ed9aa41c563a44b18701529c8327db4d.html

User layer:存储personalization信息,仅对创建该资源的用户可见。

为什么要引入这个分层机制呢?还是为了实现文章开头提到的中心思想:确保Partner和客户做的增强不会因为SAP的产品升级而丢失。通过内容的分层存储,SAP,合作伙伴和客户做的内容彼此隔离,互不影响。在运行时,假设对于同一UI模型,SAP,合作伙伴和客户均有各自的资源,则最终用户看到的UI是这些资源的一个并集,我们称产生这个并集的过程为Merge。在Merge过程中如果遇到冲突,比如一个UI字段的标签,SAP,合作伙伴和客户均有各自的定义,则Merge结果以优先级最高的layer包含的内容为准。不同layer优先级从低到高,即上图report从上到下的layer依次为:

SAP->Partner->Customer->User

再回到我们正在进行的payload分析。执行report,结果如下。点击按钮显示LREP里这个文件的完整内容:

可以发现该文件内容就是我们在Chrome开发者工具里观察到的从Fiori UI发送到S/4HANA后台服务器的HTTP请求的payload:

因此,我们在Fiori UI从右键菜单的Available Fields里选择扩展字段放到Fiori UI上时,Fiori UI通过HTTP请求将该扩展字段的明细,即包含了迄今为止我们分析的这几个字段的JSON字符串发送到S/4HANA后台,存储在LREP中。

Product Master这个Fiori应用的Component ID,可以在BSP应用MD_PROD_MAS_S1的Component.js里找到。前面说过了,Product Master这个Fiori应用基于Smart Template构建,并没有自己的前端实现,因此Component.js只是一个wrapper,仅有不到6行代码。

当包含了扩展字段的Fiori UI即将渲染时,首先有一个HTTP请求将待渲染UI包含的所有扩展字段信息从LREP中读取出来。注意下图蓝色高亮区域内的/sap/bc/lrep/flex/data, 这就是S/4HANA后台LREP暴露给Fiori UI的存储服务。

Fiori UI读取到LREP返回的JSON后,解析到changeType为addFields,于是调用Fiori UI框架对应的处理逻辑,根据JSON里包含的扩展字段明细将其渲染出来。这实际上就是前面提到的,SAP layer的Fiori标准UI同Customer layer的扩展字段的Merge动作。

扩展字段Merge到Fiori UI的入口在AddFields.applyChange:

addElementIntoGroupElement会将扩展字段添加到Fiori UI对应的区域内:

addElementIntoGroupElement又会调用createControl将扩展字段的定义转换成对应的UI5控件实例,后者的Render负责将控件实例渲染成原生的HTML代码。至此,S/4HANA扩展字段的渲染就完成了。

为了避免每次用户打开包含了扩展字段的Fiori UI时,UI5框架都需要重复执行标准的Smart Template页面和扩展字段的Merge过程,UI5设计了一个基于LRU(Least Recently Used)的客户端缓存。该LRU缓存的实现采用的是HTML5支持的一种本地存储方案,IndexedDB。这种存储方案允许在浏览器客户端存储结构化数据,并提供了高效的支持异步操作的API。IndexedDB在S/4HANA Fiori UI中的读写操作封装在UI5的库文件LRUPersistentCache.js中。下图展示了一个包含了扩展字段的Fiori UI在渲染时,UI5从本地IndexedDB存储中读取之前Merge成功的UI页面的缓存数据。

从Chrome开发者工具的console页面能看到Merge好的Fiori UI页面内容在IndexedDB中的存储采取的是key-value键值对的方式。

IndexedDB提供的一系列get/set API由浏览器提供原生实现,其中get接收一个key作为输入参数,返回对应的值。

这个键值对仍旧可以在Chrome开发者工具的Application标签页里的IndexedDB区域中查看到。

作为本文的后续,我的同事Boris将会带来SAP Cloud for Customer的Field Extensibility设计概述。

更多阅读

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180714G0PG5I00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券