国内的 SAP 生态圈还是很繁荣的,笔者在小红书上也加入了一个 SAP 交流群。
群里有朋友提问,关于将 SAP ERP 的数据展示在 Form 上的需求。
这让笔者回忆起之前在 ABAP On-Premise 系统做过的类似需求。
如果需要将 ABAP On-Premise 系统的数据,嵌入到 PDF Form 里并在浏览器窗口里提供浏览和下载,一种做法是使用 Adobe Form Designer 这个软件,首先完成 Form 表单的开发,包括表单的界面布局设计,和数据绑定两大块的内容。
可以在 SAP 官网上找到 Adobe Form Designer 下载和安装的详细步骤:
然后在 SFP 事务码里,创建 Form Interface,将其同前一步骤开发好的 Form 表单进行数据绑定。
Form Interface 可以看成 Form 表单的输入参数。Form 表单内需要显示数据的控件,比如标签为 Name 的 Text 控件,需要绑定到 Form Interface 的对应字段上,假设该字段名称也为 Name.
那么还需要编写一个 ABAP 程序,负责从后台数据库表,将对应的数据取出,填充到 Form Interface 的 Name 字段去。
使用 Adobe Form Designer 绘制的 Form 表单,加上通过 ABAP 程序填充好的 Form Interface 结构,二者通过 ADS ( Adobe Document Service) 这个 Web Service,最终合二为一,被渲染成 PDF 二进制流。
这个流通过 ABAP SICF 事务码创建的 HTTP 服务,显示在浏览器窗口中。
以上思路在 SAP CRM 7.0 中测试通过。理论上这套方案,可以移植到任意以 ABAP 为技术栈的系统上。
介绍一下演示效果。
在 SAP CRM Service Order 的搜索结果界面,点击一个增强的 Export to PDF 按钮:
会弹出一个窗口,里面包含一个 PDF 文件。上图可以看到,原本 SAP WebClient UI 表格区域的数据,出现在了 PDF 中。
下面是详细的步骤描述。
和 SAP CRM WebClient 标准 UI 增强相关的步骤,同本文主题关联不大,因此笔者仅仅简要介绍。想查看对应详细步骤的朋友,可以点击文末阅读原文,或者访问下面笔者当初发布在 SAP 社区上的英文原版:
https://community.sap.com/t5/crm-and-cx-blogs-by-sap/export-webclient-ui-table-to-pdf/ba-p/13344419
(一) 完成 Adobe Form 表单的开发
前面说到,Adobe Form 表单负责定义最后渲染在浏览器里的 PDF,到底应该显示什么内容。
所以 Form 表单的开发,包含界面布局的设计,以及给表单内用来显示业务数据的控件指定数据源,也就是俗称的数据绑定(Data Binding)。
要绑定数据得要有数据源,所以我们先在事务码 SFP 里完成数据源即 Form Interface 的创建。
在事务码 SFP 里创建一个新的 Interface:
类型选择 ABAP Dictionary-Based Interface:
ABAP 开发人员需要指定 Form Interface 的 Import 和 Export 参数,其原理和指定 Class 的 Methods,以及 Function Module 的输入输出参数是一致的。
Interface 成功创建之后,系统会预先插入一些参数,比如 SFPDOCPARAMS 类型的结构,用于 PDF Form 输出控制。
我们需要编写 ABAP 程序,将前端界面显示的 Service Order 数据,从数据库表抽取出来,传递给 Adobe Form 表单。
所以定义一个 CRMS_ORDER_PDF 结构,用于存放从数据库表抽取出来,准备绑定到 Form 表单的业务数据。
然后在 Interface 里添加一个名叫 ORDERLIST 的参数,该参数的数据类型,使用之前定义好的 CRMS_ORDER_PDF 结构,用于接收 ABAP 程序传递过来的业务数据。
CRMS_ORDER_PDF 结构的定义:
ORDER_LIST 指向一个 Table Type,CRMT_ORDERADM_H_DU_TAB, 这是维护 CRM 订单数据抬头级别字段的标准结构。
Form Interface 创建完毕之后,回到 SFP 事务码,再创建一个 Form 模版 PF_CRM_ORDER_LIST:
将 Form 模版的 Interface 指定成刚刚创建成功的 IF_CRM_ORDER_LIST, 然后在 Form 模版的 Context 面板,将 Interface 里定义的全部字段,即 ORDER_NUMBER 和 ORDER_LIST, 拖拽到 Context 区域。
这样就能在 Form 模版里通过控件的数据绑定,来显示这些字段的值了。
上图黄色和绿色区域,分别对应 Form Interface 和 Context,二者就是整体和部分的关系。
比如 Sales Processing 这个业务领域,可能有 Lead, Opportunity, Sales Quote, Sales Order 等表单需要打印。
而这些表单有一些通用需要显示的字段,比如 ID,Description 等信息。出于重用性考虑,这些表单共用同一个 Form Interface,该 Interface 内包含了这些通用字段。而各个表单 Specific 的信息,可以单独维护在 Context 里。
点击 Layout 面板,就可以进行表单界面的设计了。
Adobe Form 表单采取了所谓 Data Driven 即数据驱动的呈现方式。
如下图所示,在表单正中放置了一个表格控件,表格控件的行项目区域,勾上了 Repeat Row for Each Data Item,而 Data Binding 即数据绑定区域,指向了 Form Interface 里 ABAP DDIC Structure 的 ORDER_LIST Table Type.
这种设计的语义是,运行时 Form 表单里的表格行项目实例,会根据 ABAP ORDER_LIST Table Type 的实际行数,动态进行创建。换言之,运行时 Form 表单行项目的实际个数,等于 ABAP ORDER_LIST 内表的行数。
表格行项目的数据源绑定好之后,给行项目内的每个控件也分别绑定上待显示的数据。
比如下图所示,Service Order ID 这个表格栏,应该显示 ABAP Structure 的 OBJECT_ID 字段值。
我把开发完毕的完整 Form 文件,上传到了这个链接:
https://pan.baidu.com/s/1dCZInpqPbvYpnZGE7Oj3bA?pwd=hdab
大家下载到本地后,在 SFP 事务码里使用 fb_xdp_up 命令,就可以将本地 Form 文件,重新导入到 ABAP 系统。
(二) 完成 ABAP 程序取数逻辑的编写和 PDF 二进制流的生成
我将逻辑封装在了 get_output_data 方法内部。方法所在类的完整代码,已经上传到了这个链接里:
https://pan.baidu.com/s/1dCZInpqPbvYpnZGE7Oj3bA?pwd=hdab
这个方法包含五个关键点。
1. 使用 Function Module FP_JOB_OPEN 新建一个 PDF 渲染作业。
2. 使用 Function Module FP_FUNCTION_MODULE_NAME,根据 Form 表单名称,获取专门用来渲染该表单为 PDF 的 Function Module 名称。
3. 使用 ABAP Open SQL,读取待显示到 PDF 中的 Service Order 抬头数据。
4. 调用第二步获得的专属 Function Module,生成 PDF 二进制流。这个 Function Module 内部会转而调用 Adobe Document Service,负责将 Form 表单和 ABAP 格式的内表数据,合并成 PDF 二进制流,将其存储到 Function Module 的输出参数中。
5. 调用 FP_JOB_CLOSE 关闭这次渲染作业。
(三) 将 PDF 二进制流通过 HTTP 暴露出来
我使用的 HTTP 暴露方式是所有 ABAP 系统都包含的 Basic Component,Internet Communication Framework 即 ICF 提供的 IF_HTTP_EXTENSION 接口,如下图所示。
ICF 是 ICM(Internet Comminication Manager) 的一部分,而后者又是 SAP NetWeaver 应用服务器的一部分,它起到了网关的作用,管理着 SAP 系统内部和外部之间的数据流。
通过 ICM,SAP 系统可以处理不同类型的互联网请求,如网页浏览器的 HTTP 请求、Web 服务调用,或者通过 SMTP 发送电子邮件。
ICM 通过支持各种协议,实现了 SAP 系统的跨平台交互功能。
ICF 中的 HTTP Request Handler 即 IF_HTTP_EXTENSION, 是 ICF 服务的扩展接口,它允许开发人员自定义处理 HTTP 请求和响应。
这个接口的核心任务是接收来自客户端的 HTTP 请求,进行解析,并返回相应的 HTTP 响应。
开发人员可以实现该接口来处理各种类型的 HTTP 请求,如 GET、POST、PUT、DELETE 等。
该接口的主要作用是帮助 ABAP 程序处理这些请求,从而可以根据业务需求来开发基于 HTTP 协议的应用程序。
笔者之前的文章曾经详细介绍过:
从 ABAP Netweaver 的 SICF 到 SAP Kyma 的 Lambda Function
使用 ICF 框架提供的事务码 SICF,创建一个新的 ICF 节点,取名叫 order_print.
给这个节点分配一个 ABAP 类,该类应当实现刚刚提到的接口 IF_HTTP_EXTENSION.
这个类需要实现接口 IF_HTTP_EXTENSION 里定义的 HANDLE_REQUEST 方法。
当我们使用浏览器访问包含了 SICF 节点 path 片段的如下 url 时,该方法就会自动触发。
https://<host name>:<port>/sap/crm/order_print
访问该 url 后,我们能在浏览器里看到什么内容,取决于该方法内部,通过 server 这个输入参数,设置了哪些输出数据。
方法的源代码如下,包含两个实现关键点:
1. 调用之前提到的封装好的方法 get_output_data, 生成 PDF,得到 PDF 二进制流,存储在变量 lv_pdf_xstring 里,如下图第 15 行代码所示。
2. 通过 HANDLE_REQUEST 方法的输入参数 server, 调用其 server->response 属性的 append_data 方法, 将前一步生成的包含 PDF 二进制流的 lv_pdf_xstring 变量值,返回给浏览器客户端。
最后我们在 SAP CRM 界面上,绘制一个新的 Export to PDF 按钮。
点击之后,弹出一个对话窗口,该窗口的 url,指定成之前提到的 url 即可。
https://<host name>:<port>/sap/crm/order_print
这一块的开发和 SAP CRM WebClient UI 技术紧密相关,大家点击文末阅读原文即可获得详细步骤。
以上是笔者在 ABAP On-Premise 系统做过的 Adobe PDF Form 项目的实现细节。当然现在的 SAP BTP 上也存在对应的 SAP Forms Service by Adobe 的概念,笔者后续的文章会继续介绍。