作者: IT168 JarWang 日期: 2009-05-07
【IT168 技术文档】
ORACLEERP开发基础之前言
http://tech.it168.com/a2009/0427/274/000000274048.shtml
ORACLE ERP开发之OracleForms基础(一)Forms设置部分
http://tech.it168.com/a2009/0428/274/000000274178.shtml
ORACLEERP开发基础之OracleForms基础(二)FORMS代码部份
http://tech.it168.com/a2009/0428/274/000000274236.shtml
ORACLE ERP开发基础之Oracle Report基础
http://tech.it168.com/a2009/0429/274/000000274343.shtml
ORACLEERP开发基础之Oracle数据库基础
http://tech.it168.com/a2009/0504/274/000000274773.shtml
开发工具安装
Oracle EBS11i 开发工具是developer6i的东西,非常古老。因为是转手过来的机器,开发工具都是安装好了,
所以也没去在意这工具的安装。今天系统重装了,才知道这工具安装起来也是特烦人的。
先安装FORMS6i与REPORT6i,安装完后一定要打补丁。否则会出现一大堆莫名的错误。接着安装discoverer。
安装discoverer时先将注册表备份出来,因为discoverer不能和Forms 安装在同一目录。然后再安装 discoverer的补
丁,接着再将注册表恢复回来。
大致这样可以了。注意顺序一定要不能错了,否则又会出现一堆无聊的错误。唉!developer6i是98年的工
具。盼着EBS R12应该是用DEVELOPER10g吧。
EBS二次开发包注册
2.1 从 Server 中下载 ERP Library (.pll), 存放于本机Server Path : /u01/au/11.5.0/resource
Form文件与Library文件要存放在固定目录
例如: Form存放在 d:/ErpForm/Form
Library 存放在d:/ErpForm/Library;
2.2 在 Regedit > HKEY_LOCAL_MACHINE > SOFTWARE > ORACLE > FORMS60_PATH
加入client 端存放 Form 及 Library的路径(如图)
例如:d:/ErpForm/Library;d:/ErpForm /Form(建议要加在最前面)
利用TEMPLATE.fmb 模板来开发Form
Oracle 已经为我们提供了一个Form 的开发模板,(/data/deve/devappl/au/11.5.0/forms/ZHS/TEMPLATE.fmb)
我们的开发实际要基于这个模板,这个模板里面已经存在了我们将会用到的Oracle 标准的对象,我们需要做的,
就是这这个模板的基础上面,添加我们自己的对象。这也是人家说二次开发没啥技术含量的重大原因。但如果
真让你出写那么PL 包,估计也没几人能写好。人有时就是这样,让你站在巨人的肩膀,还在抱怨这抱怨那的。
我也是这类人啦。出出气呀。^_^!
1、更改template.fmb 文件名,同时删除一些无用的样本对象。
2、增加数据块
一路照做就可以了,步骤太简单就不用讲了吧。完成后,选择“仅创建数据块”。
3、增加一个画布
选择工具菜单上面的布局向导来做。
注意:选择画布时,选择新画布。如上图。
显示记录数一般为10就可以了。如上图。
注:将画布名称改成与数据块的名称一致
4、增加一个窗口
注:窗口尽量保持与画面一致,同时选择相应的主画布。对应的画布那边也要选择主窗口
5、选择对象的子类信息
所有的对象都应选择相对的子类信息,这边仅以BLOCK为例。
6、调整布局 不需要显示到画布的ITEM,可以将ITEM的画布属性设成空
7、修改触发器(这一步很关键!)
8、选择第一导航块
9、上传及编译FORMS
9.1先将FORMS上传至 /data/deve/devappl/au/11.5.0/forms/ZHS/
9.2telnet至server,进入/data /deve/devappl/au/11.5.0/forms/ZHS/目录
目录:cd /data/deve/devappl/au/11.5.0/forms/ZHS/ 编译:f60gen FRMSTONE.fmb apps/apps 复制:cp FRMSTONE.fmx /data/deve/deveappl/hek/11.5.0/forms/ZHS/ 注:一定要进入FORM的目录,再进行编译。否则编译也可以通过,但会出现奇怪的问题。这个问题折腾了我二天时间,才发现是ORACLE的BUG。 好了,一个最基本的EBS FORM就开发完成了。
注册表单FORM
1. 定义表单
操作路径:应用开发员=>应用产品=>表单
填写说明如下:
表单:FORMS文件名
应用:HEK 惠尔康客户化应用
用户表单名:这个参数与“功能”中的表单名是相关联的。
2.定义功能 操作路径:应用开发员=>应用产品=>功能
2.1
2.2
2.3
3.定义菜单
操作路径:应用开发员=>应用产品=& gt;菜单
注:“子菜单”是指菜单可以将另一个菜单的功能全部包含进来。也就是父菜单的关系。
5. 完成设置,效果如下:
注册请求
1.定义可执行
路径:系统管理员->并发->方案->可执行
注意:定义成请求的过程或函数,必须加入(Errbuf Out Varchar2,Retcode Out Number)两个形参。
2.定义并发程序
路径:系统管理员-> 并发->方案->可执行
注意:并发程序界面的可执行组中的“名称”,与上面的可执行并发程序界面的“简称”是一致的。
注意:如果要传入参数到PL/SQL包中,点击《参数》按钮进行设置。
也可以为参数赋给默认值,例:默认取得用户ID
如果要限定参数的取数范围,则要定义值集(如何定义?请参考后面的定义值集)。
3.定义请求组 路径:系统管理员->安全性->责任->请求
4.将请求组置于职责下
系统管理员->安全性 ->责任->定义
注册职责
1.新建菜单 操作路径:应用开发员=>应用产品=>应用菜单
2.新建一个职责
操作路径:系统管理员=>安全性=>责任=>定义
将职责分配给用户
操作路径:系统管理员=>安全性=& gt;用户=>定义
3.转到EBS主页,多显示一个职责。
注册值集
1.定义集 操作路径:总帐=>设置=>财务系统=>验证=>集
1.1如果值的来源于数据表,则值的验证类型选择“表”,然后点击“编辑信息”。
注:如果要实现请求参数的值集来源,作前后过滤条件的话。其语法:
where head.customer_number = :$FLEX$.HEK_OM_KHJGB_ACCT_CUST
and head.created_by = :$FLEX$.HEK_OM_FHD_CREATE_BY
and TO_CHAR(head.ordered_date,'YYYYMMDD') = :$FLEX$.HEK_DAY_TIME_ID ORDER BY HEAD.ORDER_NUMBER
:$FLEX$.为参数界面的名称
HEK_OM_KHJGB_ACCT_CUST为某个参数的值集名称。
1.2实现的效果如下:
1.3定义多列值集
1.4实现效果
1.5从属
2.定义值
2.1如果值的来源固定的某些值,那么可以直接定义值。将值的验证类型选择=>独立
2.2定义集所对应的值
操作路径:操作路径:总帐=> 设置=>财务系统=>验证=>值
通过应用开发员取得系统管理员责任
一般公司都有区分DBA与DEVELOPER的,业务机上一般是APPS用户由DBA掌握,诸如用户管理也是DBA来负责的。developer是不知道 没有APPS密码,也没有‖SYSTEM ADMINISTTRATOR―职责的。也就是说DEVELOPER只有‖Application Developer―这个职责。同理,应用开发员是没有权限给终端用户加权限的。 但我今天测试了一下,developer虽然没直接加职责的权限,但developer有‖Application Developer―职责,这个职责下面有注册菜单的权限,完全可以通过注册菜单将SYSTEM ADMINISTROR加进来。如图:
查询视图时没有显示数据
在EBS以外的地方 (含report),如果确认SQL语句没有问题,这主要是因为视图是OU屏蔽的,因此需要初始化身份。例如:PO_HEADERS。有两种方法,方法一适用于知道相应的组织代码,方法二就比较通用了。
方法一:
BEGIN
DBMS_APPLICATION_INFO.set_client_info(122); --122代表具体的组织代码,不同公司,代码是不同。
END;
方法二:
BEGIN
APPS.FND_GLOBAL.apps_initialize
( user_id =>APPS.FND_GLOBAL.user_id, resp_id =>APPS.FND_GLOBAL.resp_id, resp_appl_id =>APPS.FND_GLOBAL.resp_appl_id );
END;
注册报表 (report)
路径:系统管理员->并发->方案->可执行
注册报表与注册请求的步骤是差不多的。
定义并发
路径:系统管理员->并发->方案 ->定义
定义报表参数
点击上图的参数按钮。
注:这边与注册请求不一样的就是,要填写变量栏(变量名称就是REPORT中的变量名称)
定义请求组
操作路径:系统管理员->安全性->责任->请求
将请求组置于职责下
系统管理员->安全性->责任->定义
OK,完成了报表的注册。
请求的输出及日志
1、 Effect:
Oracle EBS会在EBS的安装目录,保存查看输出及日志之文件。查看具体目录:
select t.logfile_name,t.outfile_name from fnd_concurrent_requests t
where t.request_id = _request_id
2、当我们自行定义了一个并发请求时,也经常需要使用上面的方式进行一些调试。
请求的输出:apps.Fnd_File.Put_line (apps.FND_FILE.OUTPUT, '请求的输出');
请求的日志:apps.Fnd_File.Put_line (apps.FND_FILE.LOG, '请求的日志');
对于报表,一般情况是把报表的内容输出到OUTPUT,把报表中间的debug逻辑输出到日志中。
对于请求包,一般情况是输出到日志中。OUTPUT就不用管了。
实现手动提交请求
在Form里面,我们可以用
APPS.FND_REQUEST.SUBMIT_REQUEST
提交一个Request到Oracle Request Manager。如果提交成功,该函数返回Request ID,否则,返回0。
1、初始化
在提交一个Request之前,我们会调用Oracle Standard的Procedure对这个Request做一些基本的参数的初始化。
APPS.FND_GLOBAL.apps_initialize
( user_id =>APPS.FND_GLOBAL.user_id, resp_id =>APPS.FND_GLOBAL.resp_id, resp_appl_id =>APPS.FND_GLOBAL.resp_appl_id );
注:这个初始化不是必须的,之所以要初始化,是因为视图是OU屏蔽的。 上述语句等同于dbms_application_info.set_client_info;
2、函数介绍
2.1 函数APPS.FND_REQUEST.SUBMIT_REQUEST有105个参数:
APPS.FND_REQUEST.SUBMIT_REQUEST
( APPLICATION IN VARCHAR2 DEFAULT NULL,
PROGRAM IN VARCHAR2 DEFAULT NULL,
DESCRIPTION IN VARCHAR2 DEFAULT NULL,
START_TIME IN VARCHAR2 DEFAULT NULL,
SUB_REQUEST IN BOOLEAN DEFAULT FALSE,
chr(0),'','','','','','','','','','','','','','','','','','','', '','','','','','','','','','', '','','','','','','','','','', '','','','','','','','','','', '','','','','','','','','','', '','','','','','','','','','', '','','','','','','','','','', '','','','','','','','','','', '','','','','','','','','','' )
RETURN NUMBER;
2.2 参数详解
2.2.1 APPLICATION(必需参数)
应用程序的名称缩写。一般我们可能会用到下面的几个:
Oracle Assets „„> OFA
Oracle General Ledger „„> SQLGL
Oracle Inventory „„>INV
Oracle Order Management „„>ONT
Oracle Payables „„SQLAP
Oracle Pricing „„QP
Oracle Purchasing „„PO
Oracle Receivables „„AR
操作路径:系统管理员=>应用=>注册
2.2.2 PROGRAM(必需参数)
要提交到 Oracle Request Manager的并发程序之简称
操作路径:应用开发员=>并发=>程序
2.2.3 第三、第四个参数
第三、第四个参数默认为空
2.2.4 第五个参数
第五个参数默认为false
2.2.5 第六至第十零五个参数
第六至第十零五个参数为要传入到请求中的自定义参数值。如果无须这么多参数时,以chr(0)作为参数结束的标记。Chr(0)后面剩余的参数为 ‘‘。
手动提交请求示例
declare
v_order_number number;
v_req_id number;
begin
v_order_number := :HEK_ODS_TH_FEE_M_V.OE_HEAD_NUMBER;
if :HEK_ODS_TH_FEE_M_V.OE_HEAD_NUMBER is null then FND_MESSAGE.DEBUG('请选择配送单后再打印!');
RAISE FORM_TRIGGER_FAILURE;
end if;
v_req_id := fnd_request.submit_request('HEK',
'HEK_退货单',
'', '',
FALSE, v_order_number,--v_batch_no, null,--v_batch_no, null,--v_cust_num, null,--v_driver_num, null,--v_trans_num, null,--v_vendor_num, null, null,null, chr(0), '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '','', '', '', '', '', '', '', '', '', '','', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '','');
if (v_req_id = 0) then FND_MESSAGE.RETRIEVE;
FND_MESSAGE.ERROR;
else
update HEK_ODS_TH_FEE_M set print_mark='Y' where OE_HEAD_NUMBER = (v_order_number);
commit_form;
fnd_message.debug('您的请求已经提交,请求号为:' || to_char(v_req_id) || ',请通过查看->请求来查看输出结果。');
end if;
end;
客制化菜单
Effect:
Usage:调用 app_special.instantiate包
Examples:
1、增加一个自定义Form Level的触发器(SPECIAL11)
2、增加测试代码如下:
――――――――――――――――――――――――――――――――――――――――――――――
declare
v_invoice_num varchar2(50);
begin
v_invoice_num := '菜单栏客制化100';
fnd_message.debug(v_invoice_num);
end;
―――――――――――――――――――――――――――――――――――――――――――――――
3、在Form WHEN-NEW-FORM-INSTANCEFJ 进行调用:
APP_SPECIAL.INSTANTIATE('SPECIAL11','测试菜单2', '', TRUE, 'LINE');
―――――――――――――――――――――――――――――――――――――――――――――――
4、一些特殊说明:
上面自定义FORMS级触发器,名字必须定义为“SPECIAL+数字‖,否则会报错。并且数字的大小决定了菜单出现的先后顺序。数字还有更大作用就是决定了,自定义菜单选项放在哪个主菜单下。
SPECIAL1—SPECIAL15在“工具”主菜单下。
SPECIAL16—SPECIAL30在“报表”主菜单下。
SPECIAL31—SPECIAL45在“活动”主菜单下。
SPECIAL46以上就直接报错了。^_^
如下图
5、 控制自定义菜单的是否激活可用。
使用 app_special.enable函数可以控制菜单是否可以使用。
例如:基于不同的数据块,实现菜单的不可用。在block的 ‖when-new-block-instance‖中加入
效果:
app_special.enable('SPECIAL1',property_off);
效果
6、 在自定义的菜单上使用checkbox按钮。
①增加一个自定义Form Level的触发器(SPECIAL1_CHECKBOX),代码如下:
if app_special.get_checkbox('SPECIAL1_CHECKBOX')='TRUE' then
fnd_message.debug('Special 1 is True!');
else
fnd_message.debug('Special 1 is False!');
end if;
注:使用 app_special.get_checkbox来获取checkbox的状态值。
②在Form的WHEN-NEW-FORM- INSTANCE触发器中初始化菜单。
app_special.instantiate('SPECIAL1_CHECKBOX','Spe&cial 1 Box w Line', '',TRUE,'LINE');
app_special.set_checkbox('SPECIAL1_CHECKBOX','TRUE');
注:app_special.set_checkbox是对checkbox进行赋值操作。
③效果如下:
客制化右键菜单
1、 首先如果是要为某个ITEM,另外开发一个右键菜单。这个需要直接按照FORM的开发教程,自定义一个POPUP菜单就可以了。但本文讲的在EBS所有的快捷菜单上,额外增加所需的菜单按钮。也就是要图上所示的快捷菜单上增加菜单按钮。
2、 在FROM-LEVEL增加自定义触发器(名字规则为:POPUP+N)
3、 ITEM的“PRE-POPUP-MENU”触发器上初始化菜单。
APP_POPUP.INSTANTIATE('POPUP1','First Entry');
APP_POPUP.INSTANTIATE('POPUP10','SECONED Entry',TRUE,'LINE');
APP_POPUP.INSTANTIATE('POPUP3','THREE Entry',FALSE,NULL);
4、 如果是整个BLOCK的ITEM都需要客制化快捷菜单,可以在BLOCK的“PRE-POPUP-MENU”定义。
APP_POPUP.INSTANTIATE('POPUP1','global');
调用EBS日期控件
1、 首先将ITEM的LOV属性设置为“ENABLE_LIST_LAMP”、列表验证属性设置为“否”
2、在ITEM的“KEY-LISTVAL”解发器下加入对下代码:
BEGIN
calendar.show();
END;
3、注意:ITEM对应数据库类型必须是 DATE类型,否则会报frm-40700错误。效果如下:
查询数据时限定语言环境
EBS是个多语言的业务系统,界面上只查询到一条记录,实际上后台数据表是多条记录的。如:值集表。
select * from apps.FND_FLEX_VALUES_TL t, apps.FND_FLEX_VALUES B
where B.FLEX_VALUE_ID = T.FLEX_VALUE_ID
and t.language = userenv('LANG')
实现历史记录查询
1.要实现的效果
2.在定义数据表时,必须加入以下五个字段:
即:CREATED_BY NUMBER、CREATION_DATE DATE、LAST_UPDATED_BY NUMBER、LAST_UPDATE_DATE DATE、LAST_UPDATE_LOGIN NUMBER 网上有些文章说还必须定义主键、序列。但如果仅实现此功能是不需要的。当然从数据库设计方面来说,这些定义也是必须的。
3、在数据块的PRE-INSERT、PRE-UPDATE触发器中加入代码 FND_STANDARD.SET_WHO;
实现文件夹功能
1、在客制的Form里面实现Oracle Folder的功能,最终效果如下:
2、使用TEMPLATE.fmb创建一个Form(名称:FRMSTONE)。
2.1添加Form Objects 包括一个Window,MY_FOLDER
一个 Canvas, MY_FOLDER 一个Block,MY_FOLDER(可以使用向导创建),并修改其属性。如下图:
注:使用文件夹的数据块,其名称总长不能超过22字节!
2.2修改Trigger和ProgramUnits
Form Level Trigger: PRE-FORM:
app_window.set_window_position('MYFOLDER', 'FIRST_WINDOW');
Program Units:APP_CUSTOM:
if (wnd = 'MYFOLDER') then app_window.close_first_window;
2.3修改FORM属性设置
注:须要说明的是:设成第一个导航数据块的blockname必须至少一个 item处于canvas中,否则会报FRM-40106的错误。
2.4其他的具体步骤同一般的FORM开发一样,就不多说了。
3、添加Folder相关的Objects
3.1文件夹相关的Window、Canvas、Block、Item…等都包含在 Oracle Standard Form APPSTAND.FMB。
3.2在同一窗口打开标准的Form和我们自己客制的 Form,并且选择APPSTAND.FMB 的Object Groups STANDARD_FOLDER然后用鼠标拖动至我们自己的Form的Object Groups。
3.3此时会弹出对话框:
选择子类,你会发现Form Builder都会在你的Form里面自动添加很多对象。做完这一步后,请不要关闭APPSTAND.FMB。
4、增加 Attached Libraries
做完上面的步骤后,检查一下Attached libraries里面有没有APPFLDR。如果没有,我们需要手工添加。
5、增加Stacked Canvas FOLDER_STACK 这一步是必须的,而且你期望实现Folder拖动功能的那些Item都是放在这个Canvas里面,这个Stacked Canvas又是放在前面我们建立的Canvas:MY_FOLDER上面的。
5.1增加Stacked Canvas 双击打开Canvas MY_FOLDER,在左侧工具条里面选择Stacked Canvas,然后在Canvas MY_FOLDER里面拖动即可。
5.2设置Stacked Canvas属性
6、更改数据块MY_FOLDER中的Item属性
7、创建控制块MY_FOLDER_PROMPT
7.1设置块的属性 子类信息:block 数据库数据块:否
7.2增加ITEM 注:增加Item,名称必须和Block MY_FOLDER中的Item保持一致。 另外,数据块中有些item(如id)不要显示在canvas上的,那么此类item也无须在MY_FOLDER_PROMPT创建相应的item.
7.2设置其初始值,也就是显示的标签名及子类信息
7.3增加其他相关Item 我们还需要增加其他的一些Item,都是和Folder的功能有关的。
8、增加相应的trigger
9、其他注意事项
9.1错误:在编译带有文件夹的FORM时,经常会出现如下提示。
FRM-30085: Unable to adjust form for output
这是因为ITEM超出的了画布的高度或是宽度造成的。如图:
可以通过调整ITEM的X坐标或Y坐标来解决此错误。
9.2 错误:无法实现拖动。
解决:数据块的item与folder的item与调整一定的距离。
实现手电筒查找的功能
首先,先按TEMPLATE将其他功能先实现,然后再来实现查找的功能。手电筒查找的Effect有两二种,①LOV形式,②window形式。
1、LOV查找模式:一般适用于查询结果只返回单条条件的查询(如主从数据块的界面)。
①创建一个parameter参数:TEST_P
②创建一个基于主键查询LOV-TEST_L,并将主键字段映射给Parameter参数TEST_P。
③在数据块中创建PRE-QUERY触发器。
IF :parameter.G_query_find = 'TRUE' THEN :BLOCKNAME.HEADER_ID := :parameter.TEST_P; :parameter.G_query_find := 'FALSE';
end if;
④在数据块中再创建QUERY_FIND触发器。
begin
app_find.query_find('TEST_L'); --调用LOV
end;
⑤完成效果如下:
2、WINDOW查找模式:一般适用多条件查询,可以返回多条查询记录。
①打开在标准FORM---APPSTAND.fmb。将对象组中的QUERY_FIND拖至待开发的FORM中。
②重命名第一个数据块、画布、窗口(QUERY_***都是刚才自动创建的)。 USER GUID说可以重命名,但我没有重命名成功。也懒得去折腾一个命名的问题。^_^ ③打开QUERY_FIND画布,改写“新建”、“查找”两个按钮WHEN-BUTTON-PRESS之中的代码。
NEW按钮代码:
--app_find.new('Your blockname here'); app_find.new('HEK_SALE_CUST_MANUAL');
FIND按钮代码:
:parameter.G_query_find := 'TRUE'; --app_find.find('your blockname here'); app_find.find('HEK_SALE_CUST_MANUAL'); :parameter.G_query_find := 'FALSE';
④设置QUERY_FIND数据块的“前一导航数据块”,也就是要实现询查功能的数据块。
⑤修改QUERY_FIND数据块KEY-NXTBLK触发器代码
:parameter.G_query_find := 'TRUE'; --app_find.find('your blockname here'); app_find.find('HEK_SALE_CUST_MANUAL'); :parameter.G_query_find := 'FALSE';
⑦修改QUERY_FIND窗口的标题及尺寸,并在QUERY_FIND画布上创建要查找的条件ITEM。 ⑧在要实现查询的数据块(如:HEK_SALE_CUST_MANUAL),创建PRE-QUERY触发器。
if :parameter.g_query_find = 'TRUE' then
app_find.query_range(name_in('query_find.CUSTOMER_NUMBER'),name_in('query_find.CUSTOMER_NUMBER'),'HEK_SALE_CUST_MANUAL.CUST_CODE');
app_find.query_range(:query_find.CUST_DATE,:query_find.CUST_DATE,'HEK_SALE_CUST_MANUAL.TIME_ID'); :parameter.G_query_find := 'FALSE';
end if;
注:app_find.query_range参数前两个是QUERY_FIND中的ITEM,后一个是查询块中的ITEM。
⑨在要实现查询的数据块(如:HEK_SALE_CUST_MANUAL),再创建QUERY_FIND触发器。
--APP_FIND.QUERY_FIND('','',''); app_find.query_find('HEK_SALE_DEPT_MANUAL','QUERY_FIND','QUERY_FIND');
⑩完成效果如下:
记录指示器切换标签页
Effect:
1、 在该数据块中手工增加一个ITEM
设置ITEM的属性:子类信息:设成 DRILLDOWN_RECORD_INDICATOR,数据库项:否.
2.在新增的ITEM的WHEN-MOUSE-CLICK触发器加入
declare
v_order number;
begin
v_order := : block_name1.item; --block_name表示要切换tab的数据块
if v_order is not null then
go_block(' block_name1');
set_block_property('block_name1 ',DEFAULT_WHERE,'ORDER_NUMBER='||v_order);
execute_query;
end if;
end;
3.这样就可以实现类似VB中的TAB切换了。由于ORACLE FORM没有专门的TAB函数,只能如此曲折实现。
EBS的条件查询方法
1、使用app_find.find
IF (NAME_IN('PO_HEADERS.PO_HEADER_ID') IS NOT NULL) THEN
:parameter.G_query_find := 'TRUE';
app_find.find('CUX_PO_HEADERS_ADD_MESSAGE');
go_block('CUX_PO_HEADERS_ADD_MESSAGE');
:parameter.G_query_find := 'FALSE';
ELSE
FND_MESSAGE.DEBUG('请先保存订单头');
END IF;
在CUX_PO_HEADERS_ADD_MESSAGE的PRE-BLOCK中处理
copy(name_in('PO_HEADERS.PO_HEADER_ID'),'CUX_PO_HEADERS_ADD_MESSAGE.PO_HEADER_ID');
2、使用Default_where属性处理
declare
lv_default varchar2(2000);
begin
lv_default:='.....';
go_block('CUX_PO_HEADERS_ADD_MESSAGE');
set_block_property('CUX_PO_HEADERS_ADD_MESSAGE',DEFAULT_WHERE,‘PO_HEADER_ID=‘||lv_default);
execute_query;
end;
3、使用app_find.query_range()来处理 注:app_find.query_range()必须有三个参数,也只能有三个参数
begin
app_find.query_range(:find_date_from, :find_date_to, 'CUX_PO_HEADERS_ADD_MESSAGE.REATE_DATE'); :parameter.Q_query_find:='false';
end;
4、查询综合运用举例
前提准备工作,数据块:HKE_TEST 控制块:CONTROL
4.1.在数据块HKE_TEST的PRE-QUERY触发器设定查询条件
if :parameter.g_query_find = 'TRUE' then
app_find.query_range(:CONTROL.CUST_F, :CONTROL.CUST_F ,'HKE_TEST.CUSTOMER_NUMBER'); :parameter.g_query_find := 'FALSE';
end if;
4.2在查询按钮的WHEN-BUTTON-PRESSED
:parameter.G_query_find := 'TRUE';
app_find.find('HEK_TEST');
:parameter.G_query_find := 'FALSE';
自定义代码
例:实现某个项为必填项(当然自定义代码的功能还有很多)。
菜单:诊断=>自定义代码=>个性化
选择相应的触发器。如果是基于block的触发器,则要选择触发器对象(是哪一个块?)。
选择相应的项为必填。注:项的名称可通“检查”来查看。
作用于项的内容是什么,本例为必填。
保存完后,就可以实现“销售人员这一项为必填了”。
Fnd_Profile
begin
fnd_message.debug('user_id= ' || fnd_profile.value('user_id')); --取当前登录EBS用户ID
fnd_message.debug('user_name= '||fnd_profile.value('USERNAME')); --取当前登录EBS用户名
fnd_message.debug('FND_Global.User_Name='|| FND_Global.User_Name); --取当前登录EBS用户名 fnd_message.set_string('GL_SET_OF_BKS_ID='||fnd_profile.value('GL_SET_OF_BKS_ID'));
fnd_message.show; --取当前帐套
end;
弹性域
弹性域分成键弹性域、说明性弹性域。这两种弹性域用途是不一样的。 关键性弹性域:在使用KEY弹性域的基表中,只保存ID。但可以通ID查询到相应的SEGMENT。在表结构中表现为***_ID、SEGMENT1等。 说明性弹性域:针对特定用户扩展输入特定信息的字段。在表结构中表现为attribute_category、attribute1字段等。
注册关键性弹性域
注册说明性弹性域
1. 创建数据表。
注:使用说明性弹性域的数据表,必须含有ATTRIBUTE_CATEGORY及若干ATTRIBUTEN字段。 ATTRIBUTE_CATEGORY字段:指弹性域的CONTEXT字段。 ATTRIBUTEN字段:指实际使用的字段。
create table HEK_DISCOUNT_POLICY_H_NEW(
POLICY_ID NUMBER, POLICY_NO VARCHAR2(30) not null, CUSTOMER_ID NUMBER, CUSTOMER_NUMBER NUMBER , CUSTOMER_NAME VARCHAR2(100), BENIFICIARY_ID NUMBER, BENIFICIARY_NUMBER NUMBER , BENIFICIARY_NAME VARCHAR2(100), RELATION_NUMBER NUMBER , RELATION_NAME VARCHAR2(100), PAYMENT_TYPE VARCHAR2(20), POLICY_PERIOD VARCHAR2(20) not null, ATTRIBUTE_CATEGORY VARCHAR2(150), ATTRIBUTE1 VARCHAR2(150), ATTRIBUTE2 VARCHAR2(150), ATTRIBUTE3 VARCHAR2(150), CREATED_BY NUMBER, CREATION_DATTE DATE, LAST_UPDATED_BY NUMBER, LAST_UPDATE_DATE DATE, LAST_UPDATE_LOGIN NUMBER)
2.注册弹性域表
begin
ad_dd.register_table('HEK','HEK_DISCOUNT_POLICY_H_NEW','T',10,10,40);
commit;
end;
3.注册弹性域表的列
begin
ad_dd.register_column('HEK','HEK_DISCOUNT_POLICY_H_NEW','ATTRIBUTE_CATEGORY',1,'VARCHAR2',150,'N','N'); ad_dd.register_column('HEK','HEK_DISCOUNT_POLICY_H_NEW','ATTRIBUTE1',2,'VARCHAR2',150,'N','N'); ad_dd.register_column('HEK','HEK_DISCOUNT_POLICY_H_NEW','ATTRIBUTE2',3,'VARCHAR2',150,'N','N'); ad_dd.register_column('HEK','HEK_DISCOUNT_POLICY_H_NEW','ATTRIBUTE3',4,'VARCHAR2',150,'N','N');
commit;
end;
4.查看是否注册成功
select ft.table_id from FND_TABLES ft where ft.table_name='HEK_DISCOUNT_POLICY_H_NEW'
select * from FND_COLUMNS fc where fc.table_id in (select ft.table_id from FND_TABLES ft where ft.table_name='HEK_DISCOUNT_POLICY_H_NEW')
5.注册弹性域列 操作路径:应用开发员=>弹性域=>说明性=>注册
点列按钮,选择注册相应的列
6. 注册弹性域段
点段按钮,输入提示文本
7.启用说明性弹性域
7.1先按将相应的FORM开发好,然后在FORM级的以下TRIGGER中加入
PRE-QUERY: FND_FLEX.EVENT('PRE-QUERY');
POST-QUERY: FND_FLEX.EVENT('POST-QUERY');
PRE-INSERT: FND_FLEX.EVENT('PRE-INSERT');
PRE-UPDATE : FND_FLEX.EVENT('PRE-UPDATE');
WHEN-VALIDATE-ITEM: FND_FLEX.EVENT('WHEN-VALIDATE-ITEM');
WHEN-VALIDATE-RECORD: FND_FLEX.EVENT('WHEN-VALIDATE-RECORD');
7.2在PRE-FORM的TRIGGER中加入
fnd_descr_flex.define ( BLOCK=>'HEK_DISCOUNT_POLICY_H_NEW',
FIELD=>'DESC_FLEX',
APPL_SHORT_NAME=>'HEK',
DESC_FLEX_NAME=>'HEK返利_订金');
说明一下相应参数:BLOCK:指启用弹性域的数据块名 FIELD:指后面新增的ITEM APPL_SHORT_NAME:指相应模块的简称(系统管理员->应用->注册) DESC_FLEX_NAME:指注册弹性域时的名称
8.在要启用弹性域的数据块中增加一个ITEM。名称为:DESC_FLEX。
8.1 DESC_FLEX属性设置如下:
子类信息:TEXT_ITEM_DESC_FLEX
数据库项:否
画布:MAIN_C
8.2在DESC_FLEX项的以下trigger中加入:
WHEN-VALIDATE-ITEM :FND_FLEX.EVENT('WHEN-VALIDATE-ITEM');
WHEN- NEW-ITEM-INSTANCE:FND_FLEX.EVENT('WHEN-NEW-ITEM-INSTANCE');
9.完成效果:
条件控制说明性弹性域
说明:通过一个字段的值控制是否显示说明性弹性域。与普通的说明性弹性域类似。下面仅说明不同之处。
1. 注册弹性域列时,定义一个参考列。
点击参考字段按钮
DISCOUNT_TYPE是要启用弹性域数据表中的一个字段,即通过这个字段,来控制是否显示弹性域。
2.定义弹性域段
新增一条上下文值记录,例:年返。即当DISCOUNT_TYPE的值为“年返”时,才显示弹性域。
3.实现效果。
调用会计科目弹性域
1.首先在创建数据表时,添一个字段用来保存会计科目的ID。如:CODE_COMBINATION_ID
2.在FORM相应的数据块增加两个ITEM,用来显示科目NUMBER与DESCRITION。
例:GL_CODE_NUMBER、GL_CODE_DESC
注 : ENABLE_LIST_LAMP是TEMPLATE模板自带的,无须手工去定义。 将GL_CODE_NUMBER项的LOV设成“ENABLE_LIST_LAMP”,数据库项设为“否”。 将GL_CODE_NUMBER“从列表中验证”属性设成否。
3.在Form Level 的WHEN-NEW-FORM-INSTANCE Trigger初始化弹性域
FND_KEY_FLEX.DEFINE(BLOCK => 'BlockName ',
FIELD => 'GL_CODE_NUMBER',
ID => 'CODE_COMBINATION_ID',
DESCRIPTION => 'GL_CODE_DESC',
APPL_SHORT_NAME => 'SQLGL',
CODE => 'GL#', NUM => 101, --STRUCTURE Number REQUIRED => 'N' );
说明:APPL_SHORT_NAME查找:系统管理员=>应用=>注册
CODE查找:应用开发员=>键=>注册
NUM查
4.可以在GL_CODE_NUMBER项的WHEN-VALIDATE- ITEM触发器中,对所选择的科目进行核查。
IF : BlockName.CODE_COMBINATION_ID = -1 then
fnd_message.debug('所选科目组合无效!');
raise form_trigger_failure;
end if;
5、完成效果:
键弹性域与说明性弹性域明显的区别。就是使用键弹性域的数据表只保留了ID。以会计科目弹性域为例,数据表只保存了ID(即CODE_COMBINATION_ID,这个字段是对应会计科目的ID)。GL_CODE_NUMBER、 GL_CODE_DESC都只是控制块的ITEM,通过CODE_COMBINATION_ID来查询对应的科目。
EBS11i FormMVC模式
一个同事在的视频会议上提到,在做EBS开发时,要用MVC模式来满足不断在变化的业务需求。很有意思的话题,值得展开讨论。JAVA设计模式的书偶看了N次,但只会在JAVA应用,但从未想过在其他的4GL编程语言实现。以下是我的理解。 首先,按JAVA的那一套,MVC是指MODEL(模型层)、VIEW(表现层)、CONTROLLER(控制层)的缩写。编写软件为什么分层,这个是软件工程的需求。也就是说分层的目的是提高软件的可维护性,避免“动一牵百”的修改软件。 其次,按照这种软件分层原理,在JAVA中比较容易实现。Struts就是这类比较典型应用。模型层可以用实体类来实现,表现层可以用 JSP+STRUTS标签实现,控制层可以action类实现。那么在ORACL FORMS如何实现分层呢?以下对号入座的想法。 MODEL用数据块来实现、VIEW用FORM界面实现、控制层呢?MVC中最为关键的是CONTROLLER。因为CONTROLLER实现业务逻辑与流程流转。并且按照设计模式的要求,CONTROLLER又被分成若干小层,如DAO层、SERVICES层等等,并且带来的“面向接口编程”之方法。 ORACLE FORM实现业务逻辑与界面分离是通过程序单元来实现,控制层具体表现在ORACLE FORMS当中没有,只能通过繁琐的PL/SQL语句实现。 不知道这种理解对不对。这个话题是很有意思的,大家觉得呢?欢迎讨论哦。
EBS预警功能自定义开发
EBS预警分为事件预警和定期预警。预警功能非常强大,本例以订单录入后,以邮件的形式通知财务主管进行“订单登记”审核。
1、 定义预警。
操作路径:预警系统管理器=>预警=>定义
2、 编写SQL语句(注:SQL必须包含INTO、where rowid=:rowid)
select ORDER_NUMBER into &order_number from OE_ORDER_HEADERS_all head WHERE rowid = :ROWID
3、 点击上图中的“活动按钮”。
4、 点击上图中的“活动详细资料”按钮。
5、 定义活动集。
6、 预警安装
点击“预警详细资料”按钮。
7、 完成效果如下:
EBS自定义邮件通知
前面已经介绍过了EBS预警的功能,事实上Oracle数据库本身就有提供UTL_SIMPLE(ORACLE10g以后变成UTL_MAIL)包来实现邮件发送的功能,利用这个包可以开发出更灵活东西来。首先要安装utlsmtp.sql、utltcp.sql这两个包。
①发送带有 URL邮件代码
declare
p_sender varchar2(30) := '[email protected]';
p_recipient varchar2(30) := '[email protected]';
p_subject varchar2(50) := '使用PL/SQL发送邮件';
p_body long := '这是邮件正文内容啦!我又来啦!!!进行ORACLE ERP';
mail_conn utl_smtp.connection;
mail_host varchar2(15) := 'mail.hek.cn';
user_name varchar2(156) := '[email protected]';
user_pwd varchar2(156) := '***';
begin --创建一个TCP MAIL连接
mail_conn := utl_smtp.open_connection(mail_host, 25);
--ehlo与helo的区别:是否对邮件主机进行登陆认证
--utl_smtp.helo(main_conn,mail_host);
utl_smtp.ehlo(mail_conn, mail_host);
--登陆认证语句 utl_smtp.command(mail_conn, 'AUTH LOGIN');
--对用户及密码进行加密
utl_smtp.command(mail_conn, demo_base64.encode(utl_raw.cast_to_raw(user_name)));
utl_smtp.command(mail_conn, demo_base64.encode(utl_raw.cast_to_raw(user_pwd)));
--指定发件人 utl_smtp.mail(mail_conn, p_sender);
--指定收件人 utl_smtp.rcpt(mail_conn, p_recipient);
--开始写邮件内容 utl_smtp.open_data(mail_conn);
--指定显示的发件人,注意这边的显示的发件人可以上面指定发件人不同
--这实际上是SMTP协议的缺陷,也是造成垃圾邮件主要原因
utl_smtp.write_data(mail_conn, 'From:' || p_sender || utl_tcp.CRLF); utl_smtp.write_data(mail_conn, 'To:' || p_recipient || utl_tcp.crlf);
--邮件主题:中文必须进行编码转换,否则会乱码 utl_smtp.write_raw_data(mail_conn, utl_raw.cast_to_raw(convert('Subject:' || p_subject || utl_tcp.CRLF, 'ZHS16GBK')));
--设置邮件内容模式为HTML,也可以直接设置文本Content-Type:text/plain
utl_smtp.write_raw_data(mail_conn, utl_raw.cast_to_raw(convert('Content-Type:text/html;
charset=GBK' || utl_tcp.CRLF, 'ZHS16GBK')));
utl_smtp.write_data(mail_conn, utl_tcp.CRLF);
--邮件正文 utl_smtp.write_raw_data(mail_conn, utl_raw.cast_to_raw(convert(p_body, 'ZHS16GBK')));
--关闭连接 utl_smtp.close_data(mail_conn); utl_smtp.quit(mail_conn); exception when utl_smtp.transient_error or utl_smtp.permanent_error then utl_smtp.quit(mail_conn);
raise_application_error(-20000, sqlerrm);
when others then raise_application_error(-20001, 'The send mail was error ' || sqlerrm);
end;
②发送带有附件的邮件
declare
p_sender varchar2(30) := '[email protected]';
p_recipient varchar2(30) := '[email protected]';
p_subject varchar2(50) := 'PL/SQL发邮件、带链接、带附件';
p_body long := 'PL/SQL发邮件、带链接带附件
这是邮件正文内容啦!这是带附件的啦!!
进行ORACLE ERP';
mail_conn utl_smtp.connection;
mail_host varchar2(15) := 'mail.hek.cn';
user_name varchar2(156) := '[email protected]';
user_pwd varchar2(156) := 'metalink'; --发附件要用到的变量
L_FIL BFILE; L_FILE_LEN NUMBER;
L_MODULO NUMBER; L_PIECES NUMBER;
L_FILE_HANDLE UTL_FILE.FILE_TYPE;
L_AMT BINARY_INTEGER:=672*3;/* ensures proper format; 2016 */
L_FILEPOS PLS_INTEGER:=1;/* pointer for the file */
L_CHUNKS NUMBER; L_BUF RAW(2100); L_DATA RAW(2100);
L_MAX_LINE_WIDTH NUMBER:=54; L_LINE VARCHAR2(1000);
L_MESG VARCHAR2(32767);
BOUNDARY CONSTANT VARCHAR2(256) := '-----7D81B75CCC90D2974F7A1CBD';
FIRST_BOUNDARY CONSTANT VARCHAR2(256) := '--' || BOUNDARY || utl_tcp.CRLF;
LAST_BOUNDARY CONSTANT VARCHAR2(256) := '--' || BOUNDARY || '--' || utl_tcp.CRLF;
--发送带有附件邮件,MIME必须设为multipart/mixed
MULTIPART_MIME_TYPE CONSTANT VARCHAR2(256) := 'multipart/mixed; boundary="'|| BOUNDARY || '"';
begin
--创建一个TCP MAIL连接
mail_conn := utl_smtp.open_connection(mail_host, 25);
--ehlo与helo的区别:是否对邮件主机进行登陆认证 -
-utl_smtp.helo(main_conn,mail_host);
utl_smtp.ehlo(mail_conn, mail_host);
--登陆认证语句 utl_smtp.command(mail_conn, 'AUTH LOGIN');
--对用户及密码进行加密
utl_smtp.command(mail_conn, demo_base64.encode(utl_raw.cast_to_raw(user_name)));
utl_smtp.command(mail_conn, demo_base64.encode(utl_raw.cast_to_raw(user_pwd)));
--指定发件人
utl_smtp.mail(mail_conn, p_sender); --指定收件人
utl_smtp.rcpt(mail_conn, p_recipient); --开始写邮件内容
utl_smtp.open_data(mail_conn);
utl_smtp.write_data(mail_conn, 'From:' || p_sender || utl_tcp.CRLF);
utl_smtp.write_data(mail_conn, 'To:' || p_recipient || utl_tcp.crlf);
utl_smtp.write_data(mail_conn, 'Subject: ' || p_subject || utl_tcp.crlf);
--中文编码转换
utl_smtp.write_raw_data(mail_conn,utl_raw.cast_to_raw(convert('Subject:' ||p_subject ||utl_tcp.CRLF,'ZHS16GBK'))); utl_smtp.write_raw_data(mail_conn,utl_raw.cast_to_raw(convert('Content-Type:'||MULTIPART_MIME_TYPE||utl_tcp.CRLF,'ZHS16GBK')));
--utl_tcp.CRLF 数据流行尾符
utl_smtp.write_data(mail_conn, utl_tcp.CRLF); -
-邮件正文
utl_smtp.write_data(mail_CONN, FIRST_BOUNDARY);
utl_smtp.write_raw_data(mail_conn,utl_raw.cast_to_raw(convert('Content-Type:text/html;charset=GB2312' ||utl_tcp.CRLF, 'ZHS16GBK')));
utl_smtp.write_data(mail_conn, utl_tcp.CRLF);
utl_smtp.write_raw_data(mail_conn,utl_raw.cast_to_raw(convert(p_body, 'ZHS16GBK')));
utl_smtp.write_data(mail_conn, utl_tcp.CRLF);
--附件格式
utl_smtp.write_data(mail_CONN, FIRST_BOUNDARY);
utl_smtp.write_raw_data(mail_conn,utl_raw.cast_to_raw(convert('Content-Type:text/html;charset=GB2312' ||utl_tcp.CRLF, 'ZHS16GBK')));
utl_smtp.WRITE_RAW_DATA(mail_conn, UTL_RAW.CAST_TO_RAW(CONVERT('Content-Disposition' || ':' ||'attachment;filename="'||'qq.xls"' || utl_tcp.CRLF, 'ZHS16GBK')));
utl_smtp.write_raw_data(mail_conn,utl_raw.cast_to_raw(convert('Content-Transfer-Encoding:base64'||utl_tcp.CRLF,'ZHS16GBK')));
utl_smtp.write_data(mail_CONN, UTL_TCP.CRLF);
--附件二进制流
BEGIN
--把附件分成多份,这样可以发送超过32K的附件
L_FILEPOS := 1;
--CREATE OR REPLACE DIRECTORY U_FIEL AS '/data/book/'
--qq.xls附件放在ORACLE服务器/data/book/下,注意大写
L_FIL := BFILENAME('U_FIEL', 'qq.xls');
L_FILE_LEN := DBMS_LOB.GETLENGTH(L_FIL);
L_MODULO := MOD(L_FILE_LEN, L_AMT);
L_PIECES := TRUNC(L_FILE_LEN / L_AMT);
IF (L_MODULO <> 0) THEN L_PIECES := L_PIECES + 1; END IF;
DBMS_LOB.FILEOPEN(L_FIL, DBMS_LOB.FILE_READONLY);
DBMS_LOB.READ(L_FIL, L_AMT, L_FILEPOS, L_BUF);
L_DATA := NULL; FOR I IN 1 .. L_PIECES LOOP L_FILEPOS := I * L_AMT + 1;
L_FILE_LEN := L_FILE_LEN - L_AMT;
L_DATA := UTL_RAW.CONCAT(L_DATA, L_BUF);
L_CHUNKS := TRUNC(UTL_RAW.LENGTH(L_DATA) / L_MAX_LINE_WIDTH);
IF (I <> L_PIECES) THEN L_CHUNKS := L_CHUNKS - 1;
END IF;
utl_smtp.write_raw_data(MAIL_CONN, UTL_ENCODE.BASE64_ENCODE(L_DATA));
L_DATA := NULL;
IF (L_FILE_LEN < L_AMT AND L_FILE_LEN > 0) THEN L_AMT := L_FILE_LEN;
END IF;
DBMS_LOB.READ(L_FIL, L_AMT, L_FILEPOS, L_BUF);
END LOOP;
DBMS_LOB.FILECLOSE(L_FIL);
EXCEPTION WHEN OTHERS THEN DBMS_LOB.FILECLOSE(L_FIL);
utl_smtp.WRITE_DATA(mail_CONN, UTL_TCP.CRLF);
RAISE;
END;
--结束处理二进制附件
--关闭连接
utl_smtp.close_data(mail_conn); utl_smtp.quit(mail_conn);
exception when utl_smtp.transient_error or
utl_smtp.permanent_error then utl_smtp.quit(mail_conn);
raise_application_error(-20000, sqlerrm);
when others then
utl_smtp.quit(mail_conn);
DBMS_OUTPUT.put_line(sqlerrm);
end;