Oracle Form 实战总结

更新时间:2024-04-02 08:29:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Lab 1: 创建客户化应用

本节介绍如何创建一个客户化应用。 Step 1: 建立Directory

以applprod用户登录操作系统 运行:$ cd $APPL_TOP

在目录$APPL_TOP下建立目录satyam,其结构如图(注意大小写的区分)

satyam1.0.0adminbinformsUSliblogmesgoutreportsUSresourcesqlpatchsqlimport图1.1

Step 2: 修改Environment File

以applprod用户登录操作系统

修改环境文件 PROD.env ,增加如下2行:

SATYAM_TOP = “/demoapp/demoappl /satyam/1.0.0” Export SATYAM_TOP

重启ORACLE APPLICATIONS,以使更改生效。

Step 3: 创建Tablespace/User 以applprod用户登录操作系统 运行:$ sqlplus

在SQL-PLUS中,以用户名SYSTEM连结数据库 运行以下命令建立TABLESPACE: create tablespace satyam_data

datafile ' /demo/demodata/satyam_data01.dbf'

size 500M reuse autoextend on next 10M ; create tablespace satyam_idx

datafile '/demo/demodata/satyam_idx01.dbf' size 100M reuse autoextend on next 10M ;

运行以下命令建立USER: create user satyam

identified by satyam

default tablespace satyam_data temporary tablespace temp ; 运行以下命令授权:

grant connect, resource to satyam ; grant select any table to satyam ; grant select any sequence to satyam ;

如果是一次性导入的话下面是运行创建Table, Sequence, Trigger, Synonym, View,

1

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Package等的脚本,在下一节再讨论。

Step 4: Application Setup

操作者必须先登录OA系统,并以“系统管理员”职责进入“浏览器”窗口。 (1) Application Register

在功能菜单中选择“应用-注册”,打开“应用”窗口 新建一条记录(具体内容如下),保存后,关闭该窗口 Application SATYAM Customization Short Name SATYAM Basepath SATYAM_TOP Description SATYAM Customization (2) Oracle Users

在功能菜单中选择“安全性-ORACLE-注册”,打开“ORACLE用户”窗口 新建一条记录(具体内容如下),保存后,关闭该窗口

Database User Name SATYAM Password 相应口令(系统要求输入2次,以确认) Privilege Enabled Install Group 1 Description SATYAM Customization

(3)Data Groups

在功能菜单中选择“安全性-ORACLE-数据组”,打开“数据组”窗口

用窗口菜单“查询-查找”来检索出“标准”数据组。并在其下的明细行中新建一条记录(具体内容如下),保存后,关闭该窗口(如果OA启用了MRC,则需要在“MRC”数据组中添加同样一条记录)。

Application SATYAM Customization ORACLE ID APPS Description

(4)Request Groups

在功能菜单中选择“安全性-责任-请求”,打开请“求组窗口”,创建一条新记录。

Group SATYAM Concurrent Programs Application SATYAM Customization Code Description 接下来的步骤是建立Menu,Responsibility,Profile等,在此就不再一一赘述了。

Lab 2: 创建客户化数据库对象的注意事项

在开发客制化Form往往要创建的新的数据库对象。比如。比如一个客制化的应用为SATYAM,

对应的Schema为SATYAM,那么实在对象如table、Sequence、index、triggers等应建在SATYAM Schema的表空间中,其他代码对象如packages, views, procedures, functions等创建在Apps Schema下,常见错误是把表建在Apps Schema下,又来建别名,这个时候删除别名时会报对象不存在,而建别名的时候又报对象已存在。Apps Schema应创建其他用户对象的synonyms或者得到授权访问这些对象。

下面是一段典型创建客户化表,索引,视图等对象的典型代码,可以对比验证下是否符合上一段所说的原则。

PROMPT CONNECT DATABASE...

CONNECT SATYAM/&&SATYAM_PASSWORD@&&SID_NAME; --以SATYAM用户登录,创建实在对象

2

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 PROMPT CREATE TABLE......

DROP TABLE satyam_catalog_group_headers_all; CREATE TABLE satyam_catalog_group_headers_all ( CATALOG_GROUP_HEADER_ID NUMBER not null, CATALOG_GROUP_SERIAL_NUMBER VARCHAR2(30), CATALOG_GROUP_NAME VARCHAR2(100), CATALOG_GROUP_DESCRIPTION VARCHAR2(240), ? , ORG_ID NUMBER not null, CREATION_DATE DATE not null, CREATED_BY NUMBER not null, LAST_UPDATED_BY NUMBER not null, LAST_UPDATE_DATE DATE not null, LAST_UPDATE_LOGIN NUMBER,

ATTRIBUTE_CATEGORY VARCHAR2(30), ATTRIBUTE1 VARCHAR2(150), ? , ATTRIBUTE15 VARCHAR2(150), REQUEST_ID NUMBER, PROGRAM_APPLICATION_ID NUMBER, PROGRAM_ID NUMBER, PROGRAM_UPDATE_DATE DATE ) TABLESPACE satyam_data;

PROMPT CREATE INDEX......

drop index SATYAM_CATALOG_group_HEADERS_U1; drop index SATYAM_CATALOG_group_HEADERS_U2;

CREATE UNIQUE INDEX SATYAM_CATALOG_group_HEADERS_U1 ON

SATYAM_CATALOG_group_HEADERS_ALL(catalog_group_header_id) TABLESPACE SATYAM_IDX;

CREATE UNIQUE INDEX SATYAM_CATALOG_group_HEADERS_U2 ON

SATYAM_CATALOG_group_HEADERS_ALL(org_id,catalog_group_name) TABLESPACE SATYAM_IDX;

PROMPT GRANT/REVOKE OBJECT PRIVILEGES --向APPS用户授权

GRANT SELECT, INSERT, UPDATE, DELETE, REFERENCES, ALTER, INDEX ON

SATYAM.SATYAM_CATALOG_group_HEADERS_ALL TO APPS WITH GRANT OPTION; GRANT SELECT, INSERT, UPDATE, DELETE, REFERENCES, ALTER, INDEX ON

SATYAM.SATYAM_CATALOG_group_lineS_ALL TO APPS WITH GRANT OPTION;

PROMPT CREATE SEQUENCE

DROP SEQUENCE SATYAM_CATALOG_group_HEADERS_S;

CREATE SEQUENCE SATYAM_CATALOG_group_HEADERS_S START WITH 1;

PROMPT CONNECT DATABASE......

CONNECT APPS/&&APPS_PASSWORD@&&SID_NAME; --以APPS用户登录,创建虚对象

3

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 PROMPT CREATE SYNONYM.....

DROP SYNONYM SATYAM_CATALOG_group_HEADERS_ALL; DROP SYNONYM SATYAM_CATALOG_group_HEADERS_S;

CREATE SYNONYM SATYAM_CATALOG_group_HEADERS_ALL FOR

SATYAM.SATYAM_CATALOG_group_HEADERS_ALL;

CREATE SYNONYM SATYAM_CATALOG_group_HEADERS_S FOR

SATYAM.SATYAM_CATALOG_group_HEADERS_S; COMMIT;

PROMPT CREATE OR REPLACE VIEW.....

CREATE OR REPLACE VIEW SATYAM_CATALOG_GROUP_HEADERS AS SELECT

catalog_group_header_id, catalog_group_serial_number, catalog_group_name,

catalog_group_description, ?, org_id,

creation_date, created_by,

last_updated_by, last_update_date, last_update_login, attribute_category, attribute1, ?,

attribute15, request_id,

program_application_id, program_id,

program_update_date

FROM satyam_catalog_group_headers_all WHERE NVL(ORG_ID,

NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'), 1, 1), ' ',NULL,

SUBSTRB(USERENV('CLIENT_INFO'), 1, 10))), -99)) =

NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'), 1, 1), ' ', NULL,

SUBSTRB(USERENV('CLIENT_INFO'), 1, 10))), -99) ;

CREATE OR REPLACE VIEW SATYAM_CATALOG_GROUP_HEADERS_V AS SELECT

catalog_group_header_id, catalog_group_serial_number, catalog_group_name,

catalog_group_description, ?,

4

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 org_id,

creation_date, created_by,

last_updated_by, last_update_date, last_update_login, attribute_category, attribute1, ?,

request_id,

program_application_id, program_id,

program_update_date

FROM satyam_catalog_group_headers;

在上段代码中,首先建立了一个satyam_catalog_group_headers_all基表,有几个特殊字段ORG_ID(保存OU_ID),五个Set_Who字段(CREATION_DATE、CREATED_BY、CREATION_DATE、CREATED_BY、LAST_UPDATE_LOGIN)相信大家都很熟悉了。ATTRIBUTE字段ATTRIBUTE_CATEGORY、 ATTRIBUTE1--ATTRIBUTE15为使用弹性域预留;REQUEST_ID、PROGRAM_APPLICATION_ID、

PROGRAM_ID、PROGRAM_UPDATE_DATE是四个并发程序相关的字段,我也没有用过。 随后又创建了2个View: satyam_catalog_group_headers,

satyam_catalog_group_headers_v,前者的作用在于根据OU屏蔽业务数据,不用去管那一串条件的含义,照抄会用即可。后者用于关联其它表或视图的数据。

我习惯采用的命名方式为基表(A):XXXX_ALL;限制OU的的视图(B):XXXX;关联其它表或视图的视图(C):XXXX_V。

如果要在客户化表上应用弹性域或预警,必须注册Tables/Columns,系统AD_DD包里有四个procedure经常会用到,

procedure register_table (p_appl_short_name in varchar2, p_tab_name in varchar2, p_tab_type in varchar2,

p_next_extent in number default 512, p_pct_free in number default 10, p_pct_used in number default 70);

procedure register_column (p_appl_short_name in varchar2, p_tab_name in varchar2, p_col_name in varchar2, p_col_seq in number, p_col_type in varchar2, p_col_width in number, p_nullable in varchar2, p_translate in varchar2,

p_precision in number default null, p_scale in number default null);

procedure delete_table (p_appl_short_name in varchar2, p_tab_name in varchar2);

procedure delete_column (p_appl_short_name in varchar2,

5

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 p_tab_name in varchar2, p_col_name in varchar2); eg:

ad_dd.register_table(’SATYAM’, ’SATYAM_CATALOG_GROUP_HEADERS_ALL’, ’T’,8, 10, 90);

ad_dd.register_column(’SATYAM’, ’SATYAM_CATALOG_GROUP_HEADERS_ALL’,’ATTRIBUTE_CATEGORY’, 1, ’VARCHAR2’, 150, ’Y’, ’N’);

ad_dd.register_column(’SATYAM’, ’SATYAM_CATALOG_GROUP_HEADERS_ALL’,’ATTRIBUTE1’, 2, ’VARCHAR2’, 150, ’Y’, ’N’);

ad_dd.register_column(’SATYAM’, ’SATYAM_CATALOG_GROUP_HEADERS_ALL’,’ATTRIBUTE15’, 16, ’VARCHAR2’, 150, ’Y’, ’N’);

Lab 3: 新建Form

修改Template.fmb模板是新建Form最快方法,对于在EBS系统中使用的Form,利用Template可以省去许多繁杂的工作。开发之前记得把服务器上appstand.fmb,pll,fmb文件

($AU_TOP/RESOURCE/US路径下)下载到本地指定路径,以防打开或编译fmb文件时找不到引用对象。

在Form builder中打开Template.fmb文件后,修改module name后另存为。下一步是修改3个地方。

1 pre-form trigger(form level)

修改此处FND_STANDARD.FORM_INFO的版本号,日期,作者

App_window.set_window_position中BLOCKNAME要替换为window name,否则打开Form时会有问题。

2 when-new-form-instance trigger(form level)

修改FDRCSID中FMB文件名

6

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 3 program unit : app_custom.close_window

此处至少要制定your first window的名字,否则窗体不是关不掉就是出现红条,其他的代码可以根据程序的需要修改。

Template中所有的对象都可以保留,也可以删除重建。我的建议是2个Block(BLOCKNANE和DETAILBLOCK)可以删除,其他对象如WINDOW(BLOCKNAME),CANVAS(BLOCKNAME)等都可以保留,修改对应的属性即可,新建的对象如BLOCK等记得设置正确的PROPERTY CLASS。

另外几个属性要特别注意下: MODULE

Property Note

First 已运行Form光标最先进入的数据块,建议设

navigation data 置,否则系统会根据BLOCK对象的排列顺序决

block 定First navigation data block

WINDOW

Property Note Primary Canvas 建议指定

CANVAS

Property Note WINDOW 建议指定

这些属性可能由于删除再新建对象发生丢失,所以每次开发Form时都需要特别小心。

Lab 4: 布局方法

一个Form Module可能包含了多个window,多个Data Block,多个canvas,在对象众多的情况下,布局方法遵循一定的规则可让你事半功倍。

如图4.1,一般来说布局,应按照windows-canvases-data block-items的顺序,先简单说明下各对象与布局相关的属性。 Window

Property Note

X Position Form启动时X轴坐标 Y Position Form启动时Y轴坐标 Width Form实际宽度 Height Form实际高度

Canvas

Property Note

Window 指定Canvas所在的window

7

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 View X Position on Canvas 可见部分相对于Canvas 的X轴坐标,通常置0 View Y Position on Canvas 可见部分相对于Canvas 的Y轴坐标,通常置0 Width Canvas实际宽度 Height Canvas实际高度

图4.1

Canvas一般都要设置以上属性,但是根据Property Class的不同,几类canvas还是有所区别。

1 Content Canvas

又称基础画布或内容画布,就像刷地板要先上底漆一样,是必不可少的。一般情况下Content Canvas的宽和高应设置和windows的宽高一致或略小于,我的建议是设成一样。

2 CANVAS_STACKED_FIXED_FIELD Canvas

译作固定的堆叠画布,顾名思义,固定意味着该画布的尺寸是固定的,千万不要误以为该画布会永远锁定在界面的最上层。 Property Note

Viewport X Position 可见部分相对于Form源点的X轴坐标,修改该值影响对象在layout

窗体上的位置

Viewport Y Position 可见部分相对于Form源点的Y轴坐标,修改该值影响对象在layout

窗体上的位置

Viewport Width 可见部分的宽度,在layout窗体上显示的宽度,<= Canvas Width Viewport Height 可见部分的高度,在layout窗体上显示的宽度,参见图4.2

3 CANVAS_STACKED Canvas

译作堆叠画布。在需要显示得列太多,界面上无法一次排开的情况,需要使用滚动条时多用到这种画布。这种类型的画布,一般Canvas的宽和高要比Viewport的宽和高要大得多,另外还要设置Show Horizontal Scroll Bar属性为Yes,允许使用横向滚动条。

8

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 图4.2

4 Tab_Canvas

译作页签画布,此类画布使用起来比较简单再此就不再赘述了比较简单,与之相关的触发器我会在后面的章节总结。

关于调整布局的一些技巧

在Form builder中选Format--Layout Option—Ruler,弹出4.3窗体,Units选Character Cells,Character Cell Size不用管它,不要修改;Grid Space建议选1或0.5,Number of Snap Points Per Grid Spacing建议选1;具体含义是采用Character Cells方式进行位置调整,Grid Space表示每个Grid的幅度,Number of Snap Points Per Grid Spacing我的理解是鼠标选中后方向键调整多少次才能达到一个Grid Space的幅度。所以Grid Space越大,Number of Snap Points Per Grid Spacing越小表明每次调整的幅度越大,设成上面的数字是个人的经验,可据需要而定,很多时候我都是修改对象坐标属性,然后利用对齐功能来调位置的。

图4.3

对于CANVAS_STACKED CANVAS来说,一般都会使用横向滚动条,注意在Units选择

Character Cells时(如图4.3),应设置Viewport的下沿的Y坐标比比该画布上位置最低Item下沿Y座标还要低10-12个刻度,如图4.2所示下沿间距。

对于复杂的界面,可能同时使用4种类型的Canvas,甚至Tab Canvas上还放置了堆叠画布,整个显得非常Layout界面复杂而混乱,此时可在使用Form builder中View菜单下的3个选项 :

9

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 1Show Canvas ;2 Show Views;3 Stacked Canvas。选中Show Canvas会显示当前Canvas的实际大小的框架(width和height),并显示底色;选中Show Views会显示当前Canvas的Viewport的框架(Viewport width和Viewport height),此选项的效果在对象为堆叠画布时显得非常直观;Stack Canvas选项在画布互相堆叠时尤为好用,但是使用时一定要注意技巧。

图4.4

假如在一个基础画布上要显示固定堆叠画布SKN_HEADER(A),页签画布SKN_TAB_CANVAS(B)和另一个固定堆叠画布BLUECARD_STACKED_CANVAS(C),想把A放在最底层,B次之, C在最上面。那么在图4.4的界面上必须按住CTRL键,依次先选中A再选中B最后选中C,注意次序不能有任何的颠倒,否则点OK按钮后会发现有些Canvas看不见了。操作的原则是:先选择最底层,再选择此底层,最后选择最上层。当然你分步完成上述调整也没问题,但是一定要确保选择次序符合上述原则。

Block

注意设置Number of Records Dispalyed和相关的Scrollbar属性。

Item

Item和布局属性相关的属性有Property Class和相关prompt属性。我一般的做法是根据不同类型字段先批量修改Property Class和Format mask,第二步根据Block的设置批量设置ITEM的相关prompt属性如:

Property Note

width 日期类型1.2,时间类型.8,日期加时间1.7,按钮列一般为1.2

height 选中Property Class一般不许修改,button为.3,其他为.25 prompt justification Right(数字型字段),其他选Start

prompt attachment edge 单行块一般选Start,单行块一般选Top prompt attachment offset .025

prompt alignment offset .025

每个ITEM的prompt的属性值,我会在layout 窗口中一个个修改。

Navigate 相关

无论是Block还是Item都有一些与导航相关的属性,决定了界面上按tab键,光标的移动方式。

Block

Property Note

Navigate Style Same Record/Change Record/ Change Data Block

10

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Previous Navigation Data Block 比如关闭一个window后,光标会落到Previous Block上的

第一个可导航Item时

Next Navigation Data Block 当导航方式为Change Data Block时,光标落在前一个块最

后一个可导航Item时,再按tab键,光标就会跳到Next Block上

当Previous Navigation Data Block,Next Navigation Data Block属性为空时,系统会自动根据Form Builder对象树各block的排列顺序,自动决定各Block的导航顺序。这种默认的方式可能会带来其他的问题,比如关闭窗体时,光标落在不该落到的块上,最好指定一下。

Item

Property Note

Keyboard Navigable 设置Item是否可使用tab键导航

Previous Navigation Item 前一个可导航Item(可能会向前导航到前一个块的最后一个可

导航Item)

Next Navigation Item 后一个可导航Item时(可能会向后导航到后一个块的第一个可

导航Item)

注意当Previous Navigation Item,Next Navigation Item属性为空时,系统会自动根据Form Builder对象树各block下Item的排列顺序,自动决定各Item的导航顺序。所以当我们不想去设置Item的导航顺序,又希望界面上各字段能够按照从上到下从左到右的顺序导航,那么就必须合理安排各Item的排列顺序,切记。

Lab 5: 赋初始值&Item 对象相关属性

本节介绍一下Form各对象赋初始值的方法以及设置Item属性需要注意的地方。

1.赋初始值

在form中需要记录一些变量的值,这些值在Form的整个运行过程中都可能被引用,为了避免重复的获取,我们采用的策略为在Form的when-new-form-instance时间中将这些变量读取出来赋值给parameter(有时候是存入global变量中),以后对这些变量的引用就转换成对Form parameter的引用。以下是程序示例:

PROCEDURE get_profile_parameter IS

BEGIN

Fnd_profile.GET('ORG_ID',:PARAMETER.G_ORG_ID); Fnd_profile.GET('USER_ID',:parameter.G_USER_ID);

select user_name into :parameter.G_USER_NAME from fnd_user

where user_id = :parameter.G_USER_ID ;

select NVL(employee_id,147) into :parameter.G_EMPLOYEE_ID from fnd_user where user_id = :parameter.G_USER_ID ; END get_profile_parameter;

对于Item来说也需要赋初始值,设置日期型字段的初始值为当前日期,某主键字段为sequence的nextval

Eg: Data Type Initial Value Note Date $$DATE$$ 当前日期 NUMBER :sequence.order_header_s.nextval 取自sequence NUMBER fnd_ profile.value(?org_id‘) 取自系统变量 Char :parameter.G_USER_NAME 取自parameter

11

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 2.Item属性

各种Item由于property class的不同视觉效果与属性设置都有所不同,下面简单列举各类Item属性设置需要注意的地方。 1.Text Items

可以根据需要为Text Items设置不同的Property Class,TEXT_ITEM,

TEXT_ITEM_DISPLAY_ONLY,TEXT_ITEM_MULTILINE, TEXT_ITEM_DATE ,

REATION_OR_LAST_UPDATE_DATE,TEXT_ITEM_PERCENT_FIXED等。对于各种类型的字段,注意根据data type设置合适的Format mask。

2. Display Items

应当设置足够宽以显示数据库中该项的最长内容。

3. Check Boxes

需要特别注意的属性

Property Note Value when checked

Value when unchecked

Check Box mapping of other values checked/ unchecked/not allowed,当该选项为not

allowed时,必须指定Initial value,否则编译出错。

还有一类比较特殊的Check Boxes(property class-CHECKBOX_COORDINATION),用以控制Master-Detail Block的关联行为,在随后第6章习会再次提及。

4. Buttons

可设置Access Key。(ALT+KEY)

5. Option Groups

必须设定缺省值。

6. Poplists

在可选值不超过15个情况下,Poplists是个不错的选择,对Poplists的编程应对应List Item Value。应尽量避免将Poplists设为multi-row blocks的首字段。

Property Note

List Style 列表风格,poplist/combox/tlist使用前两种风格居多,在设置poplist

风格的Elements时,有几项就输入几项,鼠标不要做多余的操作,比如明明只有4项,偏要点到第5项上去,虽然什么都不输入,编译后列表就是多出白花花的一行,非常的难看,此时只有删除该列表Item,重头来过,甚是麻烦。

mapping of other values 不指定的话遇到该项数据不匹配的情况,可能整条记录都被屏蔽。 Initial value 不指定的话编译时会提示,不影响程序。 7. LOVS

LOV可以比Poplists容纳更多的记录,使用中需注意的地方在后续章节介绍。

8. Flexfields

关于Key弹性域和Descriptive弹性域的介绍,再后续的一章中有专门的说明。

9. Current Record Indicator

建议对于多行的Block都建立一个Property Class为CURRENT_RECORD_INDICATOR的非数据字段,作用:当前记录指示器。

12

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 10. Rowid

这个字段不是必需但很有用。Property Class:ROW_ID,当需要利用记录的物理地址更新或删除一条记录时,该字段就是必需的,当然前提是Block的数据源中必须包含该字段。

Lab 6: Compute Data

操作数据无非删除、更新、插入的动作,在Oracle Form依靠下面触发器完成这些操作。 Form Trigger

Key-commit 保存按钮触发/Ctrl+S 键盘触发/代码触发 Block Trigger

Pre-delete Pre-update Pre-insert

On-delete On-update On-insert On-lock

Oracle保存数据的机制:第一步依次从数据库中删除在Block上删除的记录(删除每条记录时先触发Pre-delete,再触发On-delete),第二步依次从数据库中更新在Block上更新的记录(更新每条记录时先触发Pre-update,再触发On-update),第三步依次向数据库中插入在Block上插入的记录(插入每条记录时先触发Pre- insert,再触发On- insert),第四步COMMIT。其中任何一步出错的话都会引发异常,回滚以前的操作。所以我猜想Oracle Form肯定有一套机制能记录Block上每条记录的状态,所以在保存时可触发相应的Trigger。

如果限制Form在一定条件下才允许保存,可以将控制逻辑写在Form级trigger pre-commit或者block级的Pre-delete,Pre-update,Pre-insert代码中(这种情况控制可以针对操作类型进行控制,更加精细),一但条件不满足,立即抛出异常或忽略本次操作。

On-lock触发器在用户在界面上企图修改数据时触发,利用了Oracle悲观封锁的机制,保证数据的完整性和一致性,关于这种机制的详细说明,可参考我另一篇日志。

http://www.erp100.com/2743/viewspace-2332.html

在需要设置who字段的block中,Pre-insert和Pre-update都要添加如下代码:

fnd_standard.set_who; oracle会调用API给那几个who字段赋值,不用我们操心了。

另外在pre-insert的触发器中设置某些 Item 的初始值,比如说给org_id字段赋值,比如给一个主键 Item赋一个sequence.nextval,这样与在Item的initial value属性中设定sequence.nextval 相比可以节省很多序列号,也可以更好保证序列号的连续性,这在某些业务中是非常关键的。 Eg:

:BLOCKNAME.ITEM_NAME := FND_PROFILE.VALUE(?VALUE_NAME‘); SELECT SEQUENCE_A.NEXTVAL INTO :BLOCKNAME.ITEM_NAME ;

操作数据前提是设置Block的Insert_Allowed,Update_Allowed,Delete_Allowed属性为Yes,对于数据源为基表,单表视图或简单多表视图,可以利用Oracle Form自身的机制,不需借助任何代码,可以实现数据的操作。

1.操作基表和单表视图

Block

Property Value Note DML Data Target Type Table

2.操作多表视图

Block

Property Value Note DML Data Target Type Table

DML Data Target Name Table_Name 操作的基表名

13

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Item(主键字段)

Property Value Note

Primary Key Yes 不指定主键的话编译会出错

以上2种情况,系统会自动构造Insert,Update,Delete,Lock语句。根据我Trace的结果来看, Update,Delete,Lock都是根据记录的ROWID来唯一确认一条记录的。

3.使用代码操作数据

对于数据的操作,我都建议用代码实现,这样比较直观,出了问题也容易找到原因。Oracle Form会分别调用ON-INSERT触发,ON-UPDATE触发,ON-DELETE触发分别完成插入、更新、删除操作。需要注意的是:在符合上面1,2类型的情况下,创建了ON-XXXXXX触发器,都里面的代码为NULL;这时候系统就不能自动操作数据了,因为Oracle Form检测到了ON-XXXXXX触发器,执行了其中的代码,即执行了 NULL;语句。

PROCEDURE INSERT_ROW IS CURSOR c is select return_fix_headers_id

from TVSN_RETURN_FIX_HEADERS_ALL where return_fix_headers_id = :HEADER.return_fix_headers_id ; BEGIN

insert into TVSN_RETURN_FIX_HEADERS_ALL

(return_fix_headers_id ,line_type ,order_header_id ,f_to_r_flag ,ORG_ID

,creation_date ,…

,attribute15 ) values

(:HEADER.return_fix_headers_id ,:HEADER.line_type ,:HEADER.order_header_id ,:HEADER.f_to_r_flag

,:PARAMETER.G_ORG_ID ,:HEADER.creation_date ,… ,:HEADER.attribute15 ) ;

open c ; fetch c into :HEADER.return_fix_headers_id ; if (c%notfound) then close c ; raise no_data_found ; end if ; close c ; END INSERT_ROW;

PROCEDURE UPDATE_ROW is BEGIN update TVSN_RETURN_FIX_HEADERS_ALL set return_fix_headers_id = :HEADER.return_fix_headers_id ,line_type = :HEADER.line_type

14

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 ,order_header_id = :HEADER.order_header_id ,f_to_r_flag = :HEADER.f_to_r_flag ,org_id = :HEADER.ORG_ID

,creation_date = :HEADER.creation_date ,…

,attribute15 = :HEADER.attribute15

where return_fix_headers_id = :HEADER.return_fix_headers_id ; if (sql%notfound) then raise no_data_found ; end if ; END UPDATE_ROW;

procedure DELETE_ROW is begin

delete from TVSN_RETURN_FIX_HEADERS_ALL

where return_fix_headers_id = :HEADER.return_fix_headers_id;

if (sql%notfound) then raise no_data_found; end if;

end DELETE_ROW;

以上3段代码是很经典的插入,修改,删除语句。特点是每次操作完成之后都有一个检验过程,红色粗体语句。可以将这些代码放在数据库,pll文件,form的program unit,甚至是直接写在trigger 中,从性能的角度出发可能放在数据库中更高效,综合维护方便等因素考虑,我建议写在orm的program unit中。

4.一个常见的错误

图6.1

有时候在输入一条新记录保存后再修改,会发生上图所示错误。错误的原因是ON-LOCK触发器检查到界面数据和后台数据不一致,所以提示用户。看到第3节PROCEDURE INSERT_ROW代码::PARAMETER.G_ORG_ID。系统向数据库中插入了一个参数的值,但是并没有保证界面上的字段:HEADER.ORG_ID有值或者:HEADER.ORG_ID的值与:PARAMETER.G_ORG_ID一致。

一般导致这种错误的原因有:1界面字段未赋值使用了替代值存入数据库;2由于Item字段长度不够导致部分数据截断;3值列表与View字段内容不一致。建议在Lock Row字段中只锁定界面Block上的可见字段,在操作数据前保证Block字段与传入参数保持一致,最后就使用Block字段作为传入参数。

5.Master-Detail Block 的级联删除

创建relation建立主从块关系时,可以设置级联删除属性,如图6.2所示,确定后主块会

15

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 自动生成Pre-delete tigger代码为删除在数据库中对应的detail记录

图6.2

如果在创建relation为设置级联删除,可以通过修改relation的属性Delete Record Behavior

为Cascading(如图6.3),新建主块Pre-delete trigger编写删除detial记录的代码,也能达到同样的效果。

图6.3

通过这种机制,oracle Form实现了前台界面清除数据和后台数据库删除数据的一致。有两点需要注意的是:

1当Detail Block的的数据源是View时,自动生成的Master Block Pre-delete trigger代码的删除对象是View,记得改成基表。

2 当三层Master-Datail关系: C is child of B is child of A,当删除A块记录调用pre-delete代码删除B块记录,但是不会自动调用B块pre-delete代码删除C块记录,所以要将删除C块记录的语句写在A块pre-delete中。

Lab 7: Query Data

F11查询功能是Oracle ERP的一大亮点,只要属性设置正确,几乎界面上的所有字段都能做为查询字段(Query_Allowed = Yes)。我们使用Ctrl+F11执行查询,实际上是执行do_key(execute_query)语句,系统会在查询语句中自动添加Block的WHERE Clause和ORDER BY Clause,这一点可以通过帮助--检查--SYSTEM.LAST_QUERY加以验证。

另外在很多情况下,都要通过代码实现查询功能,比如打开Form时执行查询,点击按钮查询,保存后重新执行查询,某些栏位值发生变化后执行查询等

查询代码实现:

1 GO_BLOCK(?BLOCKNAME‘);

2 SET_BLOCK_PROPERTY(?BLOCKNAME‘,DFAULT_WHERE,‘where clause‘);

3 DO_KEY(?EXECUTE_QUERY‘); oracle建议用do_key(?execute_query‘)取代execute_query 在实际应用中,往往需要根据业务逻辑创建一个parameter,控制是否执行查询。

常见的实现查询功能的触发器有:when-new-form-instance,when-button-pressed,when-new-item-instance等。在某些触发器中查询功能是禁用的,如when-validate-item等。

除了F11查询功能之外,EBS还支持一种更加灵活的查询方式—QUERY FIND,即我们通常所说的手电筒查询。这是一种Non-Query模式下的查询,主要要两种模式1Row-LOV和2Find

16

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Window,所有Query Allowed属性为Yes的Block都能支持这两种方式的任意一种。 1. Row-LOV

Row-LOV类似与普通LOV,在下列情况下适于使用: (1) 一次只返回一条记录,特别适合single row block;

(2) 在detail block中自动查询关联master block的所有记录; (3) 基于主键而不是其他属性查询记录; (4) 数据量很少的情况。

比如我想建立一个关于order_line_num的lov,根据选中的订单行号码查询详细信息,以下是创建Row-LOV的步骤:

(1) 首先创建一个parameter(ORDER_LINE_NUM_QF)如用于存储返回的Primary

Key(order_line_num);

(2) 接着创建合适的Row-LOV设置order_line_num字段返回给:parameter.

ORDER_LINE_NUM_QF

(3) 再创建block-level PRE-QUERY trigger (Execution Hierarch:Before)

IF :parameter.G_query_find = 'TRUE' THEN

:lines.order_line_num := :parameter.ORDER_LINE_NUM_QF; :parameter.G_query_find := 'FALSE';

end if;

(4) 最后创建block-level QUERY_FIND trigger (Execution Hierarch:Override)

app_find.query_find('ORDER_LINES_QF');

2. Find window

Find window在下列情况下适于使用:

(1) 一次返回多条记录,特别适合multi-row block; (2) 需要多个查询条件关联进行复杂查询的; (3) 数据量很多的情况。

创建Find window的步骤相对比较复杂::

(1) 从APPSTAND.fmb中拷贝QUERY_FIND Object Group到开发窗体的.fmb文件中,此时开发窗体中会自动多出生成新的Window、Canvas、Block。删除拷贝过来的QUERY_FIND Object Group,然而新生成的对象不会被删除,合理设置这些对象的property classes属性;

(2) 重命名这些新对象,比如:ORDERS_QF,ORDERS_QF_CANVAS,ORDERS_QF_WINDOW;

(3) 修改Find Windows的title名为 ―Find ‖;

(4) 编辑NEW Button的WHEN-BUTTON-PRESSED Trigger;

app_find.new('ORDERS'); results block name 这个函数使得Find Window关闭回到原来的Block,创建一条新记录。

(5) 编辑FIND Button的WHEN-BUTTON-PRESSED Trigger; :parameter.G_query_find := 'TRUE';

app_find.find('ORDERS'); results block name :parameter.G_query_find := 'FALSE';

17

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 如果查询到数据,则关闭Find Window关闭回到原来的Block,显示查询到的记录。

(6) 编辑Find block-level的KEY-NXTBLK Trigger :parameter.G_query_find := 'TRUE';

app_find.find('ORDERS');

:parameter.G_query_find := 'FALSE'; 作用与FIND Button相同。

(7) 正确导航

Find block的Previous Navigation Data Block应设为results block,应保证用户即使不作查

询也能退出Find Window。而通过导航Block进入Find Window也是不推荐的。

(8) 开发Canvas

在Find Canvas上创建所需的Item,所有的Item都可为空,default values均设置为 NULL;

Check boxes和option groups都改为允许空值的poplists;唯一可以设置Item logic的字段类型是LOV,使用时注意Find Window不要和results Window共用LOV,因为Find Window的返回值会同时返回到results Window中,但是Find Window要和results Window共享Record Groups,保证数据一致性;创建那些在查询中要用到的隐藏字段;创建Item时,可从results block上拷贝,但要注意设置所有item为non-database items,正确的canvas、property class属性,删除所有item的trigger,保留或修改date fields的Calendar控制逻辑。

(9) 合理设置Find Window的大小

(10) 创建Results Block-Level PRE-QUERY Trigger (Execution hierarchy of Before),将Find

Window中的值拷贝到results Window中。

IF :parameter.G_query_find = 'TRUE' THEN :orders.order_id := :ORDERS_QF.ORDER_ID;

app_find.query_range(:orders_qf.date_ordered_from, :orders_qf.date_ordered_to, 'ORDERS.DATE_ORDERED');

COPY (:orders_qf.order_status, 'ORDERS.ORDER_STATUS'); :orders.customer_id := :ORDERS_QF.CUSTOMER_ID;

COPY (:orders_qf.customer_name, 'ORDERS.CUSTOMER_NAME'); :orders.sales_rep_id := :ORDERS_QF.SALES_REP_ID;

COPY (:orders_qf.sales_rep_name, 'ORDERS.SALES_REP_NAME'); if :orders_qf.payment_type is not null then --判断poplist是否非空 COPY (:orders_qf.payment_type, 'ORDERS.PAYMENT_TYPE'); end if;

:parameter.G_query_find := 'FALSE'; end if;

(11) 创建Results Block-level QUERY_FIND trigger (Execution hierarchy of Override) 打开

Find Window

app_find.query_find('ORDERS', 'ORDERS_QF_WINDOW',

'ORDERS_QF');

注意使用Find Window时不要设置Find Window和Results Window的Item同步,减少cross-field validation的使用,当Find Window没有查到数据时,焦点仍然停留在该窗体中。

18

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Lab 8: About LOV

需要设置List of Value Item属性的情况有: 1 Calendar

Property Value Data Type Date

Format Mask YYYY-MM-DD

List of Values ENABLE_LIST_LAMP Validate from List No KEY-LISTVAL Trigger

CALENDAR.SHOW;

2 Descriptive Flexfield

Property Value List of Values ENABLE_LIST_LAMP Validate from List No

3 使用LOV获取值的Item

Property Value List of Values LOV_NAME Validate from List Yes

相对于List来说,LOV可以容纳的记录更多,而且是动态从数据库获取数据。

使用向导新建LOV会同时创建Record Group,通过向导修改查询语句会同时修改Record Group的查询语句,一个Record Group可以同时被多个LOV引用。 LOV的一些属性:

Property Value Note

Filter Before Display Yes/No 选Yes,点开LOV窗口时不会自动查询,必须手动输入查

询条件进行搜索。

Automatic Refresh Yes/No 选Yes,每次弹出 LOV都会执行查询,选定记录后系统

会将缓存清除;选No,每次弹出 LOV都会根据一个标志位决定是否执行查询,选定记录后系统不清除缓存。在同一Form中多个LOV引用相同的Record Group,建议该属性设成一致,最好设成Yes。

Automatic Select Yes/No 选Yes,如LOV发现查询结果只有一条记录时,自动选择

该记录。

Automatic Skip Yes/No 选Yes,在LOV选定一条记录后光标跳到下一个Item,

选No, 光标停留当前Item。

1 LOV多栏位搜索功能:在条件框中输‘搜索条件‘%,只针对第一个栏位搜索;输%‘搜索条件‘,针对所有栏位搜索。

2 尽量将返回值列放在LOV的首列,否则直接在文本框中输入数据而不是从LOV列表选取,验证数据时出现问题(Oracle Form会用输入数据和第一列数据进行匹配)。

级联的LOV可以用使用2种方式实现:

1 根据上级LOV值的变化,动态改变下级LOV的Record Group;

2 下级LOV的Record Group查询语句引用上级LOV当前值作为过滤条件; 注意: LOV将值返回给界面上不可见的栏位 (id字段等)时,清空界面上可见的LOV返回返回栏位时,别忘了清空不可见栏位的值;当清空上级LOV栏位或修改上级LOV栏位值时,别忘了清空或初始化下级LOV相关返回字段。

19

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 与LOV相关的BUILT-IN

SET_ITEM_PROPERTY(?ITEM_NAME‘,LOV_NAME,‘ LOV_NAME‘)

SET_LOV_COLUMN_PROPERTY(lov_name VARCHAR2,colnum NUMBER,

property NUMBER, value VARCHAR2);

SET_LOV_PROPERTY(?LOV_NAME‘,GROUP_NAME,‘ GROUP_NAME‘) ;

SET_LOV_PROPERTY(?LOV_NAME‘,AUTO_REFRESH,‘ PROPERTY_TRUE‘) ; …….

Lab 9: About Message

定义Message

在系统Message Dictionary定义Message的优点在于: 1 一次定义,多次引用; 2 可定义参数,避免硬编码;

3 可很好解决多语言环境下消息文本的转译问题。

应用开发员—应用产品—消息,进入如图9.1所示界面。 下图定义了一个名为DEMVC_SHIP_BEFORE_ORDER的Message。

图9.1

Oracle建议Message Name采用大写字母加下划线的命名方式不要超过30个字符,Application就是引用Message的应用,Language即语言环境,输入Number值在显示的时候会添加APP-前缀,为空的话无前缀也会省略,Current Message Text就是要显示的文本了,带‘&‘的则说明该Message带参数,图中的Message就有&ORDERDATE和&SHIPDATE两个参数。可定义Name相同Language不同的消息,用作多语言环境下的文本转译。

定义Message信息时除了语法、语义上的注意事项外,还需遵循一些编码规范。比如说:定义的参数最好有特定的含义并且易于区分,尽量不要将一些仅表达语义的短语作为参数传入(因为Oracle在翻译Message的时候默认不会翻译参数,相当于这些参数都是硬编码的)。

显示消息

显示消息可以调用消息字典中定义的消息,也可以显示服务器上出错信息和消息堆栈中的信息,还可以显示程序代码生成的消息文本。

1调用消息字典消息 步骤如下:

20

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 (1)FND_MESSAGE.SET_NAME设置MESSAGE_NAME和Application Short Name;

(2)2 FND_MESSAGE.SET_TOKEN设置MESSAGE参数,注意必要时对参数进行格式转换。 Eg:

IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN IF :ORDERS.DATE_SHIPPED IS NOT NULL THEN

IF :ORDERS.DATE_SHIPPED < :ORDERS.DATE_ORDERED THEN FND_MESSAGE.SET_NAME('DEM','DEMVC_SHIP_BEFORE_ORDER');

FND_MESSAGE.SET_TOKEN('ORDERDATE',APP_DATE.DATE_TO_CHARDATE

(:ORDERS.DATE_ORDERED),FALSE);

FND_MESSAGE.SET_TOKEN('SHIPDATE',APP_DATE.DATE_TO_CHARDATE

(:ORDERS.DATE_SHIPPED),FALSE);

FND_MESSAGE.ERROR; END IF; END IF; END IF;

2 显示服务器上出错信息和消息堆栈中的信息 Eg1:

FND_MESSAGE.SET_NAME (‘FND‘, ‘FLEX_COMPILE_ERROR‘); FND_MESSAGE.SET_TOKEN (‘PROCEDURE‘, ‘My Procedure‘); APP_EXCEPTION.RAISE_EXCEPTION;

/* Retrieve an expected message from the server side, then show it to the user */ FND_MESSAGE.RETRIEVE; FND_MESSAGE.ERROR;

/* Then either raise FORM_TRIGGER_FAILURE, or exit routine*/

Eg2:

/* Get translated string from message file */ declare

msg varchar2(2000); begin

FND_MESSAGE.SET_NAME (‘FND‘, ‘A_2000_BYTE_MSG‘); msg := FND_MESSAGE.GET; end;

/* We now have a translated value in the msg variable for forms built–in or other function */

3显示程序代码生成的消息(这也是我们经常用到的)。 Example 1

FND_MESSAGE.SET_STRING (‘系统发生了一个严重的错误‘); FND_MESSAGE.ERROR;

21

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614

Example 2

IF (FND_MESSAGE.WARN) THEN WARNNING_FLAG := ?Y‘; ELSE

WARNNING_FLAG := ?N‘; END IF ;

Example 3

FND_MESSAGE.SET_STRING (‘Hello World‘); FND_MESSAGE.SHOW;

Example 4

FND_MESSAGE.SET_STRING (‘我要显示在状态栏上‘); FND_MESSAGE.HINT;

FND_MESSAGE.ERASE; --清空Form状态栏信息

Example 5

FND_MESSAGE.SET_STRING('确认后记录将不可修改,是否确认?'); IF FND_MESSAGE.QUESTION('是',null,'否',1,3,'question') = 3 THEN :HEADER.INV_CONFIRM_FLAG := 'N'; ELSE

DO_KEY('commit_form'); END IF ;

22

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614

Example 6

FND_MESSAGE.DEBUG ('这是一条调试信息');

Lab 10: 启用Flexfields

本节介绍如何在Form中启用弹性域。 1注册表列

在应用弹性域之前必须先定义弹性域,定义之前必须先注册表列。注册的方法在第2章已有详细的介绍,再此不再赘述。

2 定义Flexfield

进入应用开发员--弹性域--(说明性/键)--注册可新定义一个弹性域,有关详细的步骤和概念可参考我另一篇帖子(http://bbs.erp100.com/viewthread.php?tid=3965 Flexfield学习研究心得,回头看来改贴表达部分观点难免谬误疏漏,仅供各位参考,不到之处请指正)。

如图10.1所示,只有在步骤1中已注册的Table,Colummn在定义弹性域时才能被选到。 对于同种类型的弹性域,每一个基表应只定义一个,但是一个弹性域可对应多个Structure,在FORM中启用弹性域时,可利用代码指定此弹性域使用的Structure。

23

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614

图10.1

3在Form中启用弹性域 (1) 添加对应的弹性域字段

一般在基表对应的BLOCK下面新建对应的弹性域字段,相关的外观设置可参考Oracle 标准系统。

Discriptive Flexfields Column

Property Value Note property class TEXT_ITEM_DESC_FLEX List of Values ENABLE_LIST_LAMP Database Item No

Key Flexfield Column

Property Value Note List of Values ENABLE_LIST_LAMP Database Item No

(2) 添加必要的触发器

BLOCK LEVEL

24

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Trigger Code

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-RECORD FND_FLEX.EVENT(?WHEN-VALIDATE-RECORD‘); WHEN-NEW-ITEMINSTANCE FND_FLEX.EVENT(?WHEN-NEW-ITEMINSTANCE‘); WHEN-VALIDATE-ITEM FND_FLEX.EVENT(?WHEN-VALIDATE-ITEM‘);

ITEM LEVEL

Trigger Code

WHEN-NEW-ITEMINSTANCE FND_FLEX.EVENT(?WHEN-NEW-ITEMINSTANCE‘); WHEN-VALIDATE-ITEM FND_FLEX.EVENT(?WHEN-VALIDATE-ITEM‘);

(3) 代码定义弹性域字段

在Form-level的when-new-form-instance Trigger中调用Flexfield的API函数初始化弹性域栏位。

FND_DESCR_FLEX.DEFINE for descriptive flexfields FND_KEY_FLEX.DEFINE for key flexfields

FND_RANGE_FLEX.DEFINE for range flexfields

FND_DESCR_FLEX.DEFINE和FND_KEY_FLEX.DEFINE参数有所不同。 Eg:

定义总账会计科目关键性弹性域 FND_KEY_FLEX.DEFINE( BLOCK=>‘LINES‘,

FIELD=>‘ACCTG_FLEX_VALUES‘, ID=>‘GL_ACCOUNT_CC_ID‘, APPL_SHORT_NAME=>‘SQLGL‘, CODE=>‘GL#‘, NUM=>‘101‘);

定义物料类别关键性弹性域 FND_KEY_FLEX.DEFINE( BLOCK=>'CONTROL', FIELD=>'SKU_CODE', ID=>'SKU_ID',

DESCRIPTION=>'DESCRIPTION',

--COLUMN=>'description DESCRIPTION(15)', APPL_SHORT_NAME=>'INV', CODE=>'MSTK',

DATA_SET=>'101', REQUIRED=>'N',

--UPDATEABLE=>NULL, VALIDATE=>'FULL', --DINSERT=> 'Y',

USEDBFLDS=> 'N' );

定义订单描述性弹性域

FND_DESCR_FLEX.DEFINE( BLOCK=>‘ORDERS‘, FIELD=>‘DESC_FLEX‘,

APPL_SHORT_NAME=>‘DEM‘,

25

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 DESC_FLEX_NAME=>‘DEM_ORDERS‘);

Lab 11: Trigger&Coding

Trigger是Oracle Form的精要,我觉得Trigger就类似于VB、C#中的Event,一旦界面上触发了什么动作,系统就会自动调用相应得trigger代码(Oracle称为Handler,可以理解为处理触发的事件的函数)去处理。Oracle Form支持在form,block,item三个层次定义trigger,Handler可以被封装在database server(DB)层(procedure,function)也在application server(AS)层(Form 中的Program Uint对象,trigger中原始的程序代码)。按照作用对象的不同,Handler又可以分为Item handlers、Event handlers、Table handlers三类。 1. Item Handlers

Item Handlers经常定义在Item级别,处理于Item相关的触发。比如:when-validate-item, when-checkbox-changed,when-list-changed等触发器的Handler。

2. Events Handlers

在某个Event发生时,可以调用多个对象的与该 Events相关的Events Handlers,需要注意的是Events Handlers必须以该Event命名。如:when-new-record-instance,key-crerec,pre-query等触发器的Handler。

3. Table Handlers

Table handlers主要支持block的添加、修改、删除操作。如pre-insert、pre-update、pre-delete、on-insert、on-update、on-delete和on-lock等触发器的Handler。

Handler编码格式

那么这些handler究竟应存放于DB层,还是应存放于AS层,Oracle有这样的建议:调用built-ins和直接引用数据库字段(我的理解是可硬编码的)的Procedures,放在AS层;调用数据库Procedures的,涉及用多个表和程序的,代码长度>64k的Procedures放在DB层。一个议:复杂的SQL代码尽量放到database server层;尽量用存储过程参数的形式代替硬编码的形式;而且对于某些Procedures的使用,根据实际情况可以同时放在DB层和AS层。

此外编写Handler时才尽量遵循基于EVENT的编码方式::

PROCEDURE UPDATE_ADDRESS_FLAG(EVENT VARCHAR2) IS BEGIN

IF (EVENT = 'WHEN-VALIDATE-ITEM') OR (EVENT = 'PRE-RECORD')

THEN

APP_FIELD.SET_DEPENDENT_FIELD(EVENT,

(:HEADER.UPDATE_ADDRESS_FLAG = 'Y'),'HEADER.NEW_CUST');

APP_FIELD.SET_DEPENDENT_FIELD(EVENT,

(:HEADER.UPDATE_ADDRESS_FLAG = 'Y'),'HEADER.NEW_ZIP')

IF :HEADER.UPDATE_ADDRESS_FLAG = 'Y'

AND EVENT = 'WHEN-VALIDATE-ITEM' THEN

… ; END IF ; ELSE

FND_MESSAGE.DEBUG('无效的参数(HEADER.UPDATE_ADDRESS_FLAG):

'||EVENT);

END IF;

END UPDATE_ADDRESS_FLAG;

在实际Form开发中,我习惯于将每个BLOCK的Handler统一封装在名为BLOCKNAME的package中,procedure的名字为ITEMNAME,包含一个名为EVENT,类型为 VARCHAR2的参数。这样做的好处在于集中存放,维护简单,调用灵活,扩展方便。

26

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Handler编码标准

Oracle建议下列的编码标准以提高Handle性能: (1) 引用字段时,使用BlockName.ItemName的形式; (2) 使用亚元直接用dual,不要用sys.dual和system.dual; (3) 使用object IDs获得更好的性能; declare

x_id item; begin

x_id := find_item(‘block.item‘); /* your code here */ end;

(4) 尽可能使用显式游标;

(5) 超过64k的Package存放在DB层,每个Package封装的Procedure和Function总和不超过

25;

(6) 规划好异常处理机制,使用fnd_message api显示系统信息,form_trigger_failure 停止AS层的事务,app_exception.raise_exception停止DB层的事务,此外尽可能地用代码处理可预计的DB异常。

(7) 尽可能用Application 语法代替Forms built-ins ;

* FND_FUNCTION.EXECUTE代替OPEN_FORM,CALL_FORM(建议永不使用); * do_key(?exit_form‘)代替EXIT_FORM; * do_key(?commit_form‘)代替COMMIT;

* do_key(?edit_field‘)代替EDIT_FIELD/EDIT_TEXTITEM;

Trigger 属性

Fire in Enter-Query Mode:设置是否可在查询模式下触发,对于那些定义了触发器并且允许查询

的Item要特别的注意;

Execution Hierarchy:Oracle Form可在Item/Block/Form(由低到高) level别定义同名触发器 (1) Override模式,默认模式,不再触发高级别事件

(2) Before模式,触发完本级别的事件后,再触发高级别事件 (3) After模式,先触发高级别事件,再回来触发本级别事件

一般情况下Block或Item-Level Triggers的Execution Hierarch设为Before,但是以下两种情况例外:一是定义用户右键菜单的,Block或Item-Level Triggers的Execution Hierarch属性:After;二是窗体中某个字段使用了form-level POST-QUERY trigger,那么block-level POST-QUERY trigger的Execution Hierarch属性:After。

对于Block或Item-Level Triggers,Execution Hierarch属性不能设为After的有when-new-record-instance,when-new-block-instance,when-new-item-instance。

一些常用的代码片断

(1) Window常用代码片断

APP_WINDOW.SET_TITLE('TVSNPMTSETUP','','特价的录入');

(2) Canvas常用代码片断

set_tab_page_property('TAB_CANVAS.GPWP', ENABLED,PROPERTY_FALSE); SET_CANVAS_PROPERTY('TAB_CANVAS', topmost_tab_page,'DISCOUNT'); hide_view('LINE_STACKED_CANVAS2'); show_view('LINE_STACKED_CANVAS3');

(3) Block常用代码片断 go_block(?HEADER‘)

27

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 SET_BLOCK_PROPERTY( 'LINE' ,UPDATE_ALLOWED,PROPERTY_FALSE ); SET_BLOCK_PROPERTY( 'LINE' ,INSERT_ALLOWED,PROPERTY_FALSE ); CREATE_RECORD;

CLEAR_RECORD; DELETE_RECORD;

--获得BLOCK状态

If (target_canvas_name='CATGROUP_ACT_SKN' and

( upper(get_block_property('ACT_LINES',status)) = upper('NEW') or

upper(get_block_property('ACT_LINES',status)) = upper('CHANGED') ) ) then

set_canvas_property('TAB_CATGROUP', topmost_tab_page, 'CATGROUP_ACT'); end if;

--获得BLOCK当前Record

L_CUR_RECORD := GET_BLOCK_PROPERTY('VOUCHER',CURRENT_RECORD);

--遍历一个BLOCK的所有记录 GO_BLOCK('VOUCHER');

FIRST_RECORD ; LOOP

--Customerizing Coding

IF :SYSTEM.LAST_RECORD = 'TRUE' THEN EXIT; END IF ;

NEXT_RECORD; END LOOP;

(4) Item常用代码片断

go_item(?HEADER.ORDER_NUMBER‘);

DISPLAY_ITEM('HEADER.INV_REC_INTERVAL','TEXT_SPECIAL');

SET_ITEM_PROPERTY('CONTROL.LINE_SEARCH',ENABLED,PROPERTY_FALSE); SET_ITEM_PROPERTY('CONTROL. OLD',UPDATE_ALLOWED,PROPERTY_FALSE); SET_ITEM_PROPERTY('CONTROL.OLD', INSERT_ALLOWED,PROPERTY_FALSE); SET_ITEM_PROPERTY('LINE.SKU_NUM',LOV_NAME,'SKU') ;

APP_FIELD.CLEAR_FIELDS('DETAIL.ITEM_CLASS1','DETAIL.ITEM_CLASS2',…); APP_FIELD.SET_DEPENDENT_FIELD('VALIDATE',

(:REFUNDMENT.RETURN_CUST_FLAG = Y'),'REFUNDMENT.CC_RETURN_POSTAGE'); APP_FIELD.SET_REQUIRED_FIELD('PRE-RECORD',

(:HEADER.ORDER_HEADER_ID IS NOT NULL),'HEADER.RETURN_SERVICE_NAME'); APP_FIELD.SET_EXCLUSIVE_FIELD(EVENT,‘block.item1‘,‘block.item2‘,‘block.item3‘); APP_FIELD.SET_INCLUSIVE_FIELD(EVENT,‘block.item1‘,‘block.item2‘);

Oracle 建议使用APP_ITEM_PROPERTY.SET_PROPERTY/ GET_PROPERTY API函数取代SET/GET_ITEM_PROPERTY built-in函数。

(5) Menu常用代码片断

app_special.instantiate('SPECIAL1','产品组合查询'); app_special.enable('SPECIAL1',PROPERTY_ON);

app_special.instantiate('SPECIAL2','Specia&l 2 Line', '',TRUE,'LINE');

app_special.instantiate('SPECIAL3_CHECKBOX','Spe&cial 3 Box w Line', '',TRUE,'LINE'); app_special.set_checkbox('SPECIAL3_CHECKBOX','TRUE');

28

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 app_special.instantiate('SPECIAL4_CHECKBOX','Special &4 Box');

set_menu_item_property('FILE.EXPORT',ENABLED,PROPERTY_TRUE); Oracle Form中经常会发现系统导出菜单失效,利用上段代码可强行激活。

Lab 12: Function&Form Security

关于Form的安全机制,我觉得可以从以下几个方面去分析: 1 是否有权限打开这个Form;

2 打开Form后,在操作层面上权限有限制(如有的用户仅能查询,有些用户可以更新等); 3 对于Form中不同的部件,用户的权限不一(如用户A可以点击审批的按钮,用户B不行)。

对于情况1,EBS采用我们非常熟悉的Basic Function Security,这是一种基于responsibility、menu和function(功能)的管理机制,不再赘述。

情况2时,实现的方法有多种:

(1) 定义FUNCTION时,设置QUERY_ONLY 参数;

(2) 用变量记录登录用户的信息,并依此动态的设置Block和Item的属性,需要注意的是:

Item的inset_allowed,update_allowed属性只能从界面上控制不能添加或修改,并不能控制代码进行的添加或修改;而Block的inset_allowed,update_allowed属性从真正意义上对代码行为进行限制,用代码操作上述属性为否的Block,系统会提示出错; (3) 使用Form 个性化设置; (4) 使用User Profile。

针对情况3,实现的方法有就更多:

(1) 类似情况2中,可根据Form Parameter的不同值开启或禁用Form的某些功能; (2) 使用Form 个性化设置; (3) 使用User Profile; (4) 集成工作流控制;

(5) Advanced Function Security将考虑更加详细的安全机制,用户是否有权使用某个form中

的某个subfunction,是否有权看见form的某个部件,是否有权打开新的窗体等等。对于这种安全控制策略,我的理解是这样的:首先在系统里定义一个subfunction,然后把它挂到某个responsibility的menu下,然后在form里判断登录的用户具有subfunction的访问权限,激活或禁用该Form的功能。

下面是关于Advanced Function Security的示例代码:

在form的pre-form trigger中调用FND_FUNCTION.TEST函数验证登录用户的权限: IF (FND_FUNCTION.TEST('DEMVC_DEMVCEOR_PRINT_ORDER')) THEN app_special.instantiate('SPECIAL1','产品组合查询'); app_special.enable('SPECIAL1',PROPERTY_ON);

SET_ITEM_PROPERTY('CONTROL.LINE_SEARCH',ENABLED,PROPERTY_TRUE);

END IF;

系统会检测登录用户是否具有对subfunction- DEMVC_DEMVCEOR_PRINT_ORDER的访问权限,验证通过的话则激活一个SPECIAL菜单项和一个按钮项。

Lab 13: 调用Form

Form开发中经常会用到Form间相互调用的功能,总结了以下,大概有以下3种方法: 方法1

PROCEDURE shot_plan_new IS fm_id FormModule;

29

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 BEGIN

fm_id := Find_Form('TVSNPMPSCRNMT'); if id_null(fm_id) then

app_form.open('TVSN','TVSNPMPSCRNMT'); --Application Name --Function Name else

go_form('TVSNPMPSCRNMT'); end if; END ;

实现思路:首先检测是否已经打开了目标Form,是的话就是目标Form成为当前窗体,否的话就新打开一个目标Form。假如在目标Form需要随源Form自动查询出数据,须将代码写在when-window-actived trigger中。

注意:go_form会引起源Form的when-window-deactived 和目标form的when-window-actived trigger,when-window-actived trigger在form切换时都会被触发,因此目标form如果有弹出日历等调用新窗体的操作时,需要设置是否是第一次触发标识作判断,否则可能达不到设计的效果,在该trigger中不要写FND_MESSAGE.DEBUG等调用新窗体的信息,否则会引起死锁。另外此种go_form方法如需要传递参数的话,只能依靠:global.XXXXX,关闭窗体时需要在close-windows或WHEN-WINDOW-CLOSED事件中清除,否则可能导致不需要的后果。

方法2

declare pl_id ParamList; fm_id FormModule; BEGIN

pl_id := Get_Parameter_List('SCRNMTPL'); IF NOT Id_Null(pl_id) THEN

Destroy_Parameter_List( pl_id ); END IF;

pl_id := Create_Parameter_List('SCRNMTPL');

Add_Parameter(pl_id,'SHOT_DATE',TEXT_PARAMETER,to_char(d_from,'YYYY-MM-DD')); Add_Parameter(pl_id,'CHANNEL_TYPE_ID',

TEXT_PARAMETER,:CONTROL.CHANNEL_TYPE_ID);

Add_Parameter(pl_id,'CHANNEL_TYPE_NAME',

TEXT_PARAMETER,:CONTROL.CHANNEL_TYPE_NAME);

fm_id := Find_Form('TVSNPMPSCRNMT'); if id_null(fm_id) then app_form.open('TVSN','TVSNPMPSCRNMT',NO_QUERY_ONLY,pl_id,NO_SESSION);

else go_form('TVSNPMPSCRNMT'); --估计只有通过全局变量传参数了 end if; END ;

以上两种方法都是FORM4.5的方法,方法2相对方法1的区别在于:定义了一个参数列表对象,可以添加参数后作为app_form.open的一个传入参数,打开一个新的Form。实际的效果是目标Form中与参数列表中的同名参数会被赋予传入的值,随后被其他程序引用。

下面是app_form.open API的源代码,也可以了解下open_form built-in的用法。 APPCORE.pll app_form.open

PROCEDURE open(app_shortname VARCHAR2, form_name VARCHAR2,

query_mode NUMBER DEFAULT NO_QUERY_ONLY,

30

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 parameter_list PARAMLIST DEFAULT NULL_PARAMETER_LIST, session_mode NUMBER DEFAULT SESSION) IS BEGIN

COPY('Entering app_form.open.','global.frd_debug'); if session_mode = SESSION then

open_form(fnd_navigate.formpath(app_shortname, form_name),

ACTIVATE, session_mode, NO_SHARE_LIBRARY_DATA, parameter_list); else

open_form(fnd_navigate.formpath(app_shortname, form_name), --原来是这样调用的 ACTIVATE, session_mode, SHARE_LIBRARY_DATA, parameter_list); end if;

COPY('Completed app_form.open.','global.frd_debug'); END open;

方法3

自Oracle Form 6之后,Oracle建议用发fnd_function.execute和app_navigate.execute来实现form间的相互调用,无论是从语法上还是在性能上,新方法都有所改善。

ORACLE推荐用FND_FUNCTION.EXECUTE取代OPEN_FORM、CALL_FORM procedure FND_FUNCTION.EXECUTE (function_name IN varchar2,

open_flag IN varchar2 default ‘Y‘,

session_flag IN varchar2 default ‘SESSION‘, other_params IN varchar2 default NULL, activate IN varchar2 default ‘ACTIVATE‘, browser_target IN varchar2 default NULL); Example

FND_FUNCTION.EXECUTE(FUNCTION_NAME=>‘DEM_DEMXXEOR‘, OPEN_FLAG=>‘Y‘, SESSION_FLAG=>‘Y‘, OTHER_PARAMS=>

‘ORDER_ID=‖‘||param_to_pass1||

‘‖ CUSTOMER_NAME=‖‘||param_to_pass2||‘‖‘);

APP_NAVIGATE.EXECUTE(function_name =>'TVSN_ TVSNPMPSCRNMT', open_flag => 'Y', session_flag => 'Y', other_params =>

'SHOT_DATE =\

'\

'\ '\

'\

activate_flag => 'ACTIVATE', pinned =>FALSE); procedure APP_NAVIGATE.EXECUTE( function_name in varchar2,

open_flag in varchar2 default ‘Y‘,

session_flag in varchar2 default ‘SESSION‘, other_params in varchar2 default NULL,

activate_flag in varchar2 default ‘ACTIVATE‘, pinned in boolean default FALSE);

注意:

31

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 1 当前用户具有对目标Form的访问权限;

2 在目标和源窗体的WHEN–NEW–FORM–INSTANCE和WHEN–FORM–NAVIGATE triggers中需要添加APP_STANDARD.EVENT代码;

3 使用APP_NAVIGATE.EXECUTE是需要在目标窗体的RESTART trigger添加处理重启的窗体的代码。

APP_NAVIGATE.EXECUTE 和 FND_FUNCTION.EXECUTE 会将源window的位置、大小信息存储在下面4个全局变量中,以供目标窗体使用 ? global.fnd_launch_win_x_pos ? global.fnd_launch_win_y_pos ? global.fnd_launch_win_width ? global.fnd_launch_win_height

APP_NAVIGATE.EXECUTE 和 FND_FUNCTION.EXECUTE区别在于: FND_FUNCTION.EXECUTE

每次调用FND_FUNCTION.EXECUTE都会打开一个新的Form instance,调用了几次,界面上就会出现几个目标窗体,执行查询的代码应写在WHEN–NEW–FORM–INSTANCE triggers中。 APP_NAVIGATE.EXECUTE

调用APP_NAVIGATE.EXECUTE会在目标窗体中引发下列事件: (1) do_key(‘clear_form‘) ,捕获目标窗体中的为保存修改,提示用户; (2) 步骤1成功后,将传入的参数值赋给目标窗体 (3) 执行trigger RESTART 代码

在调用时,通常将查询代码写在RESTART trigger中,第一次调用不会触发目标Form中RESTART trigger代码,因此ORACLE建议可在在WHEN–NEW–FORM–INSTANCE triggers中添加第一次调用需执行代码;第二次以后调用,系统不会重新创建一个新的Form instance,而是继续使用老的Form instance,因此将不再触发目标窗体pre-form、when–new–form–instance triggers,也就是说无论该函数被调用了多少次,界面上只会有一个目标窗体。

Lab 14: Line Number功能的实现

Form开发中,可能会用到采购订单或这销售订单中自动生成行号的功能,以下介绍实现方法:

Step1 创建3个参数和一个非数据库字段

Parameter name type Initial Value P_BOLCKNAME_MAX_LINE_NUMBER number P_BOLCKNAME_VALIDATE_FLAG char

P_BOLCKNAME_RESET_LINE_NUMBER char N

在使用line_number功能的Block中添加一个非数据库Item

Column name type Initial Value VAL_REC_FIRED_FLAG char N

Step2 在block-level trigger中添加以下代码 (1) pre_query

BOLCKNAME.line_num('PRE-QUERY'); (2) post_query

BOLCKNAME.line_num('POST-QUERY'); (3) when_create_record

IF :PARAMETER. P_BOLCKNAME_RESET_LINE_NUMBER = 'Y' THEN copy('1',:PARAMETER. P_BOLCKNAME_MAX_LINE_NUMBER); :PARAMETER. P_BOLCKNAME_RESET_LINE_NUMBER:= 'N';

32

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 END IF;

BOLCKNAME.line_num('INIT'); (4) when-validate-record

BOLCKNAME.line_num('WHEN-VALIDATE-RECORD'); (5) when-new-record-instance

BOLCKNAME.line_num('WHEN-NEW-RECORD-INSTANCE'); (6) on-clear-details(子块需添加,主块不需该trigger)

IF :system.cursor_block = 'BOLCKNAME' and nvl(:system.coordination_operation,'X') in

('CREATE_RECORD','NEXT_RECORD') THEN

copy('Y', P_BOLCKNAME_RESET_LINE_NUMBER); END IF; (7) pre_insert

BOLCKNAME.catalog_group_line_unique; --行号唯一性的检查 (8) pre_update

BOLCKNAME.catalog_group_line_unique; --行号唯一性的检查

在VAL_REC_FIRED_FLAG的Item level trigger中添加以下代码 (1) WHEN-VALIDATE-ITEM

BOLCKNAME.catalog_group_line_unique; --行号唯一性的检查 BOLCKNAME.line_num('WHEN-VALIDATE-ITEM');

Step3 在Programe Unit中添加名为BOLCKNAME的PACKAGE

在PACKAGE BLOCKNAME BODY中添加 PROCEDURE line_num(event VARCHAR2) IS x_max_line_num NUMBER := 0; x_lines_curr_max NUMBER;

BEGIN IF (event = 'WHEN-VALIDATE-ITEM') THEN --code 略 END IF ;

END ;

PROCEDURE catalog_group_header_unique is l_rec_num number; BEGIN

select count(1) into l_rec_num --code 略 END;

具体代码参看附件:blockname_body.txt

Step4在数据库中创建下面的PACKAGE

红蓝两部分代码功能类似,区别在于蓝色代码多了一个line_type传入参数,可根据条件灵活调用。

create or replace package body TVSN_CONTROL_F_PKG is

PROCEDURE check_catalog_unique(p_catalog_header_id IN NUMBER,

P_catalog_LINE_ID IN NUMBER, P_catalog_LINE_NUM IN NUMBER, p_validate_flag OUT VARCHAR2) IS p_record_number number := 0; BEGIN END;

33

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614

PROCEDURE get_catalog_max_num (P_catalog_header_id IN NUMBER,

P_MAX_NUM OUT NUMBER) IS BEGIN END;

PROCEDURE check_catalog_group_unique(p_catalog_group_header_id IN NUMBER, p_catalog_line_type IN NUMBER, P_catalog_group_LINE_ID IN NUMBER, P_catalog_group_LINE_NUM IN NUMBER, p_validate_flag OUT VARCHAR2) IS p_record_number number := 0; BEGIN END;

PROCEDURE get_catalog_group_max_num(P_catalog_group_header_id IN NUMBER, p_line_type IN NUMBER,

P_MAX_NUM OUT NUMBER) IS BEGIN

END; end TVSN_CONTROL_F_PKG;

具体代码参看附件:tvsn_control_f_pkg.txt

注:此种方法有个缺陷,即可能产生断号的Line_Number,如现有行号为4,5,6的记录删除了行号为5的记录,那么新增一条记录的行号就为7,行号5就永远不可能被用到了。

顺带介绍两个和行记录有关的API: (1) APP_RECORD.DELETE_ROW

procedure APP_RECORD.DELETE_ROW(

check_delete BOOLEAN default FALSE, --是否检查Block的delete_allowed属性 product_name varchar2 default NULL, message_name varchar2 default NULL); function APP_RECORD.DELETE_ROW( check_delete BOOLEAN default FALSE, product_name varchar2 default NULL, message_name varchar2 default NULL) return BOOLEAN;

此API可应用删除记录时需提示时,确认后执行删除,取消则不删除,可以通过重写Block的KEY-DELREC Trigger添加APP_RECORD.DELETE_ROW;实现该功能。也可以利用下面的代码实现更复杂的删除逻辑。

IF APP_RECORD.DELETE_ROW THEN Code1; ELSE

Code2; END IF;

(2) APP_RECORD. FOR_ALL_RECORDS

此API补充了本章开头介绍的Line_Number功能的断号缺陷,将两者结合起来应该是一种比较完善的解决方案,实现步骤如下: Step 1 创建item handler procedures PACKAGE BODY lines IS line_number_seq number := 0;

34

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 PROCEDURE delete_row IS BEGIN

line_number_seq := 0;

APP_RECORD.FOR_ALL_RECORDS(‘reseq_line_number‘); END delete_row; END lines;

Step 2 创建用户定义trigger RESEQ_LINE_NUMBER lines.line_number_seq := lines.line_number_seq + 1; :lines.line_number := lines.line_number_seq;

Step 3 在KEY–DELETE Trigger 调用item handler procedures

lines.line_number(‘KEY–DELETE‘); --删除记录时调用delete_row代码重置行号

注意:当块中包含的记录数非常多时,那么重置行号将非常缓慢。如果块查询出的记录集只是所有记录的一部分,而行号又是主键字段,那么重置行号有可能导致主键重复的错误。如果在查询玩数据或保存提交后需要重置行号,那么可能需要修改record的状态将其标记为未修改。

Lab 15: Debug

Oracle Form Debug的方法很多,本章简单介绍一下本人应用的经验: 1 使用Fnd_message;

2 使用代码将错误信息写入数据库或文件; 3 利用系统标准Menu功能:

(1) 工具--调试,启用该功能,需要设置Profile,比如要启用销售订单(Sale Order)的Debug功能,需设置OE:Debug为Yes,这样可以查看log文件。

(2) 帮助--历史记录,可查看当前记录的数据源、创建者、修改者、创建日期、修改日期信息,即Who字段相关信息。

(3) 帮助--关于Oracle应用产品,可以查看到登录、数据库服务器、表单服务器、表单服务器环境变量、当前表单相关信息。 (4) 帮助--诊断,此功能功能非常丰富 启用诊断菜单

启用诊断菜单,必须先设置2个Profile

35

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Profile Value Note

隐藏诊断菜单项 是/否 是否隐藏诊断菜单

公用程序:诊断 是/否 是否需要输入APPS密码 检查

利用此功能可以查看到系统环境变量、当前profile、弹性栏位信息、Form Parameter、Block、Item信息,也可以给它们赋值。查看Parameter,需手工输入参数名,在列表框中是选不到的。对于开发人员来说,查看system.last_query可以解决很多问题。

跟踪

开启跟踪功能后,Oracle会将当前Form发生的后台操作(主要是查询、添加、修改、删除)记录在Trace文件中,通过阅读该文件可以了解到许多界面上感觉不到的操作,比如很多API的操作。此功能在比较复杂的调试中可能会用到。

自定义代码

此功能实际上就是Form 个性化功能,利用个性化可以定制很多调试信息,前提是要在个性化中开启调试模式,具体介绍可参看我另一篇帖子

http://bbs.erp100.com/viewthread.php?tid=9546&extra=page=1&filter=type&typeid=43。 另外利用个性化中Action设置获取对象属性的功能,查看Form中各种对象的属性,这也算另辟蹊径吧。

Lab 16: 结语

本文诣在介绍使用Oracle Form进行EBS客户化开发应遵循的一些规则和技巧。依次介绍了创建客户化应用、创建数据库对象应遵循的原则,利用Temolate快速新建Form的注意点,布局的方法,操作数据的机制和技巧,Item、LOV、Message、Flexfield属性和使用,触发器的机制和Event Handler编码规范,Form&Function的权限控制,Debug功能的使用,两个常用功能的实现方法(line number和call form)。

谨以此文纪念我的第一个Oracle ERP项目,感谢项目组nicol老大、sam老大、nat老大悉心指导,感谢大小peter,小davd,aaron,anson,jessica,kevin,shicheng的帮助与支持,感谢客户presca、jano、tanya、dapeng、cui、guohua等的协同配合与严格要求,最后还要感谢我的vivian每天都陪着我写作还送冰凉的大西瓜,以后每每看到此文定会想起在TVSN艰苦奋斗的岁月,回味被友情和爱情照耀的幸福生活。

36

陈震南 Email: makemydaywo@yahoo.com.cn MSN: makemydaywo@hotmail.com QQ: 31505614 Profile Value Note

隐藏诊断菜单项 是/否 是否隐藏诊断菜单

公用程序:诊断 是/否 是否需要输入APPS密码 检查

利用此功能可以查看到系统环境变量、当前profile、弹性栏位信息、Form Parameter、Block、Item信息,也可以给它们赋值。查看Parameter,需手工输入参数名,在列表框中是选不到的。对于开发人员来说,查看system.last_query可以解决很多问题。

跟踪

开启跟踪功能后,Oracle会将当前Form发生的后台操作(主要是查询、添加、修改、删除)记录在Trace文件中,通过阅读该文件可以了解到许多界面上感觉不到的操作,比如很多API的操作。此功能在比较复杂的调试中可能会用到。

自定义代码

此功能实际上就是Form 个性化功能,利用个性化可以定制很多调试信息,前提是要在个性化中开启调试模式,具体介绍可参看我另一篇帖子

http://bbs.erp100.com/viewthread.php?tid=9546&extra=page=1&filter=type&typeid=43。 另外利用个性化中Action设置获取对象属性的功能,查看Form中各种对象的属性,这也算另辟蹊径吧。

Lab 16: 结语

本文诣在介绍使用Oracle Form进行EBS客户化开发应遵循的一些规则和技巧。依次介绍了创建客户化应用、创建数据库对象应遵循的原则,利用Temolate快速新建Form的注意点,布局的方法,操作数据的机制和技巧,Item、LOV、Message、Flexfield属性和使用,触发器的机制和Event Handler编码规范,Form&Function的权限控制,Debug功能的使用,两个常用功能的实现方法(line number和call form)。

谨以此文纪念我的第一个Oracle ERP项目,感谢项目组nicol老大、sam老大、nat老大悉心指导,感谢大小peter,小davd,aaron,anson,jessica,kevin,shicheng的帮助与支持,感谢客户presca、jano、tanya、dapeng、cui、guohua等的协同配合与严格要求,最后还要感谢我的vivian每天都陪着我写作还送冰凉的大西瓜,以后每每看到此文定会想起在TVSN艰苦奋斗的岁月,回味被友情和爱情照耀的幸福生活。

36

本文来源:https://www.bwwdw.com/article/edhr.html

Top