Oracle Apps R12——报表入门2:单表——报表开发流程
☆开发思路
开发表报代码流程中有几个重要的组件和重要的知识点需要搞懂,才能得心应手。报表通常是通过表格的形式来存在的,我们一般在开发代码的时候在【输出】中打印HTML,Css格式的表格,并把查询到的数据插入其中,即可完成一个报表的生成,至于这个表格是如何生成和到处为csv等等,其实像这样的很多细节,系统都是自动化的,我们只需要在EBS系统中简单的设置一下即可,后面娓娓道来。
开发报表代码主要有3个重要组件,开发中需要定义:
- ①临时表:
ptian_table
:是一个临时表(或中间表),用来存储从主表查询出来的符合条件的XX数据。 - ②游标:
cur_items:
定义了一个用于遍历ptian_table
中的数据。 - ③存储过程:自己定义名字,接收ERP参数数据和编写业务逻辑用的。主要用于接收来自EBS ERP界面输入的参数(定义存储过程的时候,可以自定义传入的参数。),并且可以根据参数从主表查询数据和一系列业务逻辑编写,插入临时表
ptian_table
。
数据流处理步骤:
- 存储过程接收参数。
- 根据参数从主表 查询符合条件的数据。
- 将查询结果插入临时表
ptian_table
。 - 游标
cur_items
查询临时表数据。 - 逐条输出HTML表格行。
步骤1 定义存储过程(Procedure)
1.1 ptian_table模板
-- 删除旧表(如果存在)
BEGINEXECUTE IMMEDIATE 'DROP TABLE ptian_table PURGE';
EXCEPTIONWHEN OTHERS THENIF SQLCODE != -942 THEN -- 忽略表不存在错误RAISE;END IF;
END;
/-- 创建新表
CREATE TABLE ptian_table (description VARCHAR2(4000),segment1 VARCHAR2(100),long_description VARCHAR2(4000),primary_unit_of_measure VARCHAR2(100),creation_date DATE
);
1.2 核心代码模板
-- 创建或替换存储过程 QUERY_INVENTORY_ITEMS
-- 功能:查询库存物料信息,并将结果生成HTML格式输出
-- 参数说明:
-- errbuf : 输出参数,用于返回错误信息
-- retcode : 输出参数,返回状态码(0成功,1失败)
-- p_segment1 : 输入参数,物料编码
-- p_item_type : 输入参数,物料类型
CREATE OR REPLACE PROCEDURE QUERY_INV_ITEMS (errbuf OUT VARCHAR2,retcode OUT VARCHAR2,p_segment1 IN VARCHAR2,p_item_type IN VARCHAR2
) IS-- 定义常量lv_api_name,用于记录当前API名称(用于日志标识)lv_api_name CONSTANT VARCHAR2(30) := 'QUERY_INV_ITEMS ';-- 定义变量lv_count,用于统计插入临时表的记录数lv_count NUMBER := 0;-- 定义游标cur_items,用于从临时表ptian_table中读取数据-- 查询字段:物料说明、编码、长描述、主单位、创建时间CURSOR cur_items ISSELECT description, segment1, long_description, primary_unit_of_measure, creation_date FROM ptian_table;BEGIN-- 记录开始执行的日志(包含时间戳和API名称)fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || lv_api_name || ' 开始执行');-- 清空临时表ptian_table,确保每次查询都是最新数据DELETE FROM ptian_table;-- 记录清空表操作的日志fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || '清空ptian_table表数据');-- 将MTL_SYSTEM_ITEMS_FVL表中的数据插入临时表ptian_table-- 筛选条件:物料编码、物料类型,创建日期范围(当前被注释,可按需启用)INSERT INTO ptian_table (description, segment1, long_description, primary_unit_of_measure, creation_date)SELECTDESCRIPTION,SEGMENT1,LONG_DESCRIPTION,PRIMARY_UNIT_OF_MEASURE,CREATION_DATEFROMMTL_SYSTEM_ITEMS_FVL MSIFWHERESEGMENT1 = p_segment1 -- 按物料编码过滤AND MSIF.ITEM_TYPE = p_item_type; -- 按物料类型过滤-- AND CREATION_DATE BETWEEN p_creation_date_from AND p_creation_date_to; -- 日期范围条件(已注释)-- 获取插入的记录数,并记录到日志中lv_count := SQL%ROWCOUNT;fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || '插入ptian_table记录数: ' || lv_count);-- 生成HTML输出(以下为HTML头部结构)-- 输出HTML文档声明与基础结构fnd_file.put_line(fnd_file.output, '<html xmlns:v="urn:schemas-microsoft-com:vml" ' ||'xmlns:o="urn:schemas-microsoft-com:office:office" ' ||'xmlns:x="urn:schemas-microsoft-com:office:excel">');fnd_file.put_line(fnd_file.output, '<head>');-- 设置字符集为UTF-8,确保中文显示正常fnd_file.put_line(fnd_file.output, '<meta http-equiv="Content-Language" content="zh-cn">');fnd_file.put_line(fnd_file.output, '<meta http-equiv="Content-Type" content="text/html;charset=utf-8">');fnd_file.put_line(fnd_file.output, '<title>物料信息查询结果</title>');-- 定义CSS样式,美化表格显示fnd_file.put_line(fnd_file.output, '<style>');fnd_file.put_line(fnd_file.output, 'body {background-color:#FFFFFF; font-family:Verdana; font-size:10pt; color:#000000;}');fnd_file.put_line(fnd_file.output, 'table {border-collapse:collapse; width:100%;}');fnd_file.put_line(fnd_file.output, 'th, td {border:1px solid #000000; padding:5px; text-align:left;}');fnd_file.put_line(fnd_file.output, 'th {background-color:#BBBBBB;}'); -- 表头灰色背景fnd_file.put_line(fnd_file.output, '</style>');fnd_file.put_line(fnd_file.output, '</head>');fnd_file.put_line(fnd_file.output, '<body>');-- 输出标题“物料信息查询结果”,居中显示fnd_file.put_line(fnd_file.output, '<h3 align="center">物料信息查询结果</h3>');-- 输出表格结构fnd_file.put_line(fnd_file.output, '<table>');-- 输出表头行,包含5列:物料、说明、长描述、计量单位、创建时间fnd_file.put_line(fnd_file.output, '<tr>');fnd_file.put_line(fnd_file.output, '<th>物料</th>');fnd_file.put_line(fnd_file.output, '<th>说明</th>');fnd_file.put_line(fnd_file.output, '<th>长描述</th>');fnd_file.put_line(fnd_file.output, '<th>计量单位</th>');fnd_file.put_line(fnd_file.output, '<th>创建时间</th>');fnd_file.put_line(fnd_file.output, '</tr>');-- 遍历游标cur_items中的每一条记录,生成表格行FOR rec IN cur_items LOOPfnd_file.put_line(fnd_file.output, '<tr>');-- 输出物料编码(segment1)fnd_file.put_line(fnd_file.output, '<td>' || rec.segment1 || '</td>');-- 输出物料说明(description)fnd_file.put_line(fnd_file.output, '<td>' || rec.description || '</td>');-- 输出长描述(long_description)fnd_file.put_line(fnd_file.output, '<td>' || rec.long_description || '</td>');-- 输出主单位(primary_unit_of_measure)fnd_file.put_line(fnd_file.output, '<td>' || rec.primary_unit_of_measure || '</td>');-- 输出创建时间(格式化为yyyy-mm-dd hh24:mi:ss)fnd_file.put_line(fnd_file.output, '<td>' || TO_CHAR(rec.creation_date, 'yyyy-mm-dd hh24:mi:ss') || '</td>');fnd_file.put_line(fnd_file.output, '</tr>');END LOOP;-- 结束HTML表格和文档fnd_file.put_line(fnd_file.output, '</table>');fnd_file.put_line(fnd_file.output, '</body></html>');-- 设置成功状态errbuf := NULL; -- 错误信息置空retcode := '0'; -- 返回码设为0表示成功-- 记录成功结束的日志fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || lv_api_name || ' 执行成功结束');EXCEPTION-- 异常处理块WHEN OTHERS THENerrbuf := SQLERRM; -- 捕获错误信息retcode := '1'; -- 返回码设为1表示失败-- 记录异常日志,包含错误详情fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || lv_api_name || ' 异常:' || errbuf);-- 在HTML输出中显示错误信息fnd_file.put_line(fnd_file.output, '执行失败,错误信息:' || errbuf);
END QUERY_INV_ITEMS;
/
上面提供一个了模板,包含以下几个步骤,我们一一分析和解释:
(1) 建立临时表★★★★★
-- 删除旧表(如果存在)
BEGINEXECUTE IMMEDIATE 'DROP TABLE ptian_table PURGE';
EXCEPTIONWHEN OTHERS THENIF SQLCODE != -942 THEN -- 忽略表不存在错误RAISE;END IF;
END;
/-- 创建新表
CREATE TABLE ptian_table (description VARCHAR2(4000),segment1 VARCHAR2(100),long_description VARCHAR2(4000),primary_unit_of_measure VARCHAR2(100),creation_date DATE
);
(2)数据准备:★★★★★
- 清空临时表
ptian_table
,确保每次查询的数据都是最新的,避免历史数据干扰。 - 建立存储过程代码,用于接收参数和传出参数,以及编写业务逻辑(报表)给ERP系统
- 记录清空操作日志。
-- 创建或替换存储过程 QUERY_INVENTORY_ITEMS
-- 功能:查询库存物料信息,并将结果生成HTML格式输出
-- 参数说明:
-- errbuf : 输出参数,用于返回错误信息
-- retcode : 输出参数,返回状态码(0成功,1失败)
-- p_segment1 : 输入参数,物料编码
-- p_item_type : 输入参数,物料类型
CREATE OR REPLACE PROCEDURE QUERY_INV_ITEMS (errbuf OUT VARCHAR2,retcode OUT VARCHAR2,p_segment1 IN VARCHAR2,p_item_type IN VARCHAR2
) IS
-- 定义常量lv_api_name,用于日志标识
lv_api_name CONSTANT VARCHAR2(30) := 'QUERY_INV_ITEMS';
-- 定义变量lv_count,用于统计插入记录数
lv_count NUMBER := 0;-- 记录存储过程开始执行的日志(包含时间戳和API名称)
fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || lv_api_name || ' 开始执行');-- 清空临时表ptian_table,确保每次查询都是最新数据
DELETE FROM ptian_table;-- 记录清空表操作的日志
fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || '清空ptian_table表数据');
(3)数据查询与插入★★★★★
- 从主表
MTL_SYSTEM_ITEMS_FVL
根据输入参数(物料编码、物料类型)筛选数据。 - 将筛选结果插入临时表
ptian_table
。 - 统计插入的记录数,并写入日志。
-- 将MTL_SYSTEM_ITEMS_FVL表中的数据插入临时表ptian_table
INSERT INTO ptian_table (description, segment1, long_description, primary_unit_of_measure, creation_date)
SELECT
-- 查询到数据后想要展示的列名,这些都是要查询的数据DESCRIPTION, SEGMENT1,LONG_DESCRIPTION,PRIMARY_UNIT_OF_MEASURE,CREATION_DATE
FROMMTL_SYSTEM_ITEMS_FVL MSIF
WHERESEGMENT1 = p_segment1 -- 按物料编码过滤AND MSIF.ITEM_TYPE = p_item_type; -- 按物料类型过滤-- AND CREATION_DATE BETWEEN p_creation_date_from AND p_creation_date_to; -- 日期范围条件(已注释)-- 获取插入的记录数
lv_count := SQL%ROWCOUNT;-- 记录插入记录数日志
fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || '插入ptian_table记录数: ' || lv_count);
(4)生成HTML格式的查询结果
- 输出HTML文档的头部信息,包括字符集、标题和样式,确保页面美观且支持中文。
- 输出表格结构和表头,定义展示字段(物料、说明、长描述、计量单位、创建时间)。
- 遍历临时表数据,逐条生成表格行,填充对应字段内容。
- 关闭表格和HTML文档标签。
-- 输出HTML文档声明与基础结构
fnd_file.put_line(fnd_file.output, '<html xmlns:v="urn:schemas-microsoft-com:vml" ' ||'xmlns:o="urn:schemas-microsoft-com:office:office" ' ||'xmlns:x="urn:schemas-microsoft-com:office:excel">');
fnd_file.put_line(fnd_file.output, '<head>');
-- 设置字符集为UTF-8,确保中文显示正常
fnd_file.put_line(fnd_file.output, '<meta http-equiv="Content-Language" content="zh-cn">');
fnd_file.put_line(fnd_file.output, '<meta http-equiv="Content-Type" content="text/html;charset=utf-8">');
fnd_file.put_line(fnd_file.output, '<title>物料信息查询结果</title>');
-- 定义CSS样式,美化表格显示
fnd_file.put_line(fnd_file.output, '<style>');
fnd_file.put_line(fnd_file.output, 'body {background-color:#FFFFFF; font-family:Verdana; font-size:10pt; color:#000000;}');
fnd_file.put_line(fnd_file.output, 'table {border-collapse:collapse; width:100%;}');
fnd_file.put_line(fnd_file.output, 'th, td {border:1px solid #000000; padding:5px; text-align:left;}');
fnd_file.put_line(fnd_file.output, 'th {background-color:#BBBBBB;}'); -- 表头灰色背景
fnd_file.put_line(fnd_file.output, '</style>');
fnd_file.put_line(fnd_file.output, '</head>');
fnd_file.put_line(fnd_file.output, '<body>');
-- 输出标题“物料信息查询结果”,居中显示
fnd_file.put_line(fnd_file.output, '<h3 align="center">物料信息查询结果</h3>');-- 输出表格结构和表头
fnd_file.put_line(fnd_file.output, '<table>');
fnd_file.put_line(fnd_file.output, '<tr>');
fnd_file.put_line(fnd_file.output, '<th>物料</th>');
fnd_file.put_line(fnd_file.output, '<th>说明</th>');
fnd_file.put_line(fnd_file.output, '<th>长描述</th>');
fnd_file.put_line(fnd_file.output, '<th>计量单位</th>');
fnd_file.put_line(fnd_file.output, '<th>创建时间</th>');
fnd_file.put_line(fnd_file.output, '</tr>');
-- ,逐条生成表格行,填充对应字段内容。
FOR rec IN cur_items LOOPfnd_file.put_line(fnd_file.output, '<tr>');fnd_file.put_line(fnd_file.output, '<td>' || rec.segment1 || '</td>'); -- 物料编码fnd_file.put_line(fnd_file.output, '<td>' || rec.description || '</td>'); -- 物料说明fnd_file.put_line(fnd_file.output, '<td>' || rec.long_description || '</td>'); -- 长描述fnd_file.put_line(fnd_file.output, '<td>' || rec.primary_unit_of_measure || '</td>'); -- 计量单位fnd_file.put_line(fnd_file.output, '<td>' || TO_CHAR(rec.creation_date, 'yyyy-mm-dd hh24:mi:ss') || '</td>'); -- 创建时间fnd_file.put_line(fnd_file.output, '</tr>');
END LOOP;
(5) 结束处理与状态返回
- 设置输出参数
errbuf
为空,retcode
为0,表示成功。 - 记录成功结束的日志。
-- 关闭HTML表格和文档标签
fnd_file.put_line(fnd_file.output, '</table>');
fnd_file.put_line(fnd_file.output, '</body></html>');-- 设置输出参数,表示存储过程执行成功,错误信息为空。写日志记录成功结束。
-- 设置成功状态
errbuf := NULL; -- 错误信息置空
retcode := '0'; -- 返回码设为0表示成功-- 记录成功结束的日志
fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || lv_api_name || ' 执行成功结束');
(6) 异常处理
- 捕获所有异常,获取错误信息。
- 设置错误返回码和错误信息输出。
- 记录异常日志,方便定位问题。
- 在HTML输出中显示错误提示,保证调用方能看到错误信息。
EXCEPTIONWHEN OTHERS THENerrbuf := SQLERRM; -- 捕获错误信息retcode := '1'; -- 返回码设为1表示失败-- 记录异常日志,包含错误详情fnd_file.put_line(fnd_file.log, to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ssxff6') || chr(9) || lv_api_name || ' 异常:' || errbuf);-- 在HTML输出中显示错误信息fnd_file.put_line(fnd_file.output, '执行失败,错误信息:' || errbuf);
END QUERY_INVENTORY_ITEMS;
1.3 查询具体报错信息的代码
当你在Oracle数据库中创建或编译存储过程(Procedure)、函数(Function)、包(Package)等PL/SQL对象时,Oracle会对代码进行语法和编译检查。
如果代码中存在语法错误或编译错误,Oracle不会直接报错阻止创建,但会将错误信息记录在user_errors
视图中。
这时,存储过程虽然“创建”成功,但实际上是无效的,无法正常执行。
因此,必须查询user_errors
视图,查看是否存在编译错误,才能确认代码是否正确。
SELECT line, position, text
FROM user_errors
WHERE name = 'QUERY_INV_ITEMS'
ORDER BY line, position;
步骤2 定义Executable(可执行文件/程序)
切换到职责:”应用开发”职责,【并发->可执行】,重点都用箭头标明和解释了:
步骤3 定义Concurrent Program(并发程序)
3.1 填写Program基本信息
还是“应用开发”这个职责,【并发->程序】
3.2 Program参数定义
还记得我们写的Procedure存储过程的代码吗,我们定义了一个存储过程:
-- 创建或替换存储过程 QUERY_INVENTORY_ITEMS
-- 功能:查询库存物料信息,并将结果生成HTML格式输出
-- 参数说明:
-- errbuf : 输出参数,用于返回错误信息
-- retcode : 输出参数,返回状态码(0成功,1失败)
-- p_segment1 : 输入参数,物料编码
-- p_item_type : 输入参数,物料类型
CREATE OR REPLACE PROCEDURE QUERY_INV_ITEMS (errbuf OUT VARCHAR2,retcode OUT VARCHAR2,p_segment1 IN VARCHAR2,p_item_type IN VARCHAR2,
) IS
... 下面的无关ERP的传参
只有输入参数需要设定,标红的设置是必须要填入的字段,不然无法创建第二个参数,
点击序号前面的竖杠即可创建下一个参数,例如创建第三个参数:
步骤4:绑定Concurrent Program到Request Group
切换职责到“系统用户”,【安全性->责任->请求】,选中【组】这个文本框,然后点击左上角的手电筒:
找到想要把Program分配给的请求组,例如我想把刚刚定义的Program分配给INV reports请求组(一个专门管理库存模块报表的请求组)。
直接回车,就会自动跳转到:
选中“请求”下面的表格任意一行后,再次点击“手电筒”左边的“新建”功能,是一个文件带着一个+号:
成功把新的程序加入到对应的请求组中,成为了一个新的请求,并且分配给了INV reports请求组,任意一个拥有该请求组权限的“责任”,都可以运行这个请求。
步骤5:切换到对应责任,运行Program
这里就是根据自己的需求来切换了,切换到拥有权限——你把程序绑定的请求组的权限的“责任”,
%制造%项目%
-- 个人需求所用,参考者忽略
然后【请求->运行】,运行程序:
继续选中名称,然后“手电筒”,搜索我们的Program名称,他现在由于绑定到了请求组中,已经成为了一个请求,但是名字不变。
弹出我们设定的2个输入参数,我们输入我们想要查询的指定数据,比如我的数据源头表可以从代码看到的是,我们输入的两个值p_segment1和p_item_type被用作于筛选的条件。只有数据表中SEGENT1 = p_segment1 和 ITEM_TYPE=p_item_types才会查询,返回指定的数据。而我们现在要做的就是填写这两个输入参数。
INSERT INTO ptian_table (description, segment1, long_description, primary_unit_of_measure, creation_date)SELECTDESCRIPTION,SEGMENT1,LONG_DESCRIPTION,PRIMARY_UNIT_OF_MEASURE,CREATION_DATEFROMMTL_SYSTEM_ITEMS_FVL MSIFWHERESEGMENT1 = p_segment1 -- 按物料编码过滤AND MSIF.ITEM_TYPE = p_item_type; -- 按物料类型过滤
随后直接点击提交即可。
步骤6 查看运行的请求和生成的报表
运行完毕,EBS顶部的侧边栏找到【请求->查看】,选择”请求(R)“,打开后界面如下:
点击“查找”,随后跳转:
选中这条运行完成的请求,然后点击查看输出(P),紧接着点击查看浏览器,另外一个是直接下载Excel,如果有需求也可以用这个,但是查看结果的话最好是浏览器,确认没问题了就导出来Excel。
得到报表,完成!