Oracle Form 6i开发

Oracle Form. 6i开发的心得(1)2011-02-14 14:48Lab 0: Prepare

   环境:服务器LINUX,安装EBS11.5.10环境

          工作站 XP-2 安装Oracle Form. 6i开发工具 路径:C:\OracleDev6i_new

先期准备:

        配置tnsname.ora , C:\OracleDev6i_new\NET80\ADMIN

        Eg:          

SATYAM=

                (DESCRIPTION=

                     (ADDRESS=(PROTOCOL=tcp)(HOST=172.24.12.63)(PORT=1521))

                     (CONNECT_DATA=

                          (SID=VIS)

                      )

                 )

            用FTP工具将服务器上EBS_Sys/visappl/11.5.0/au/forms/US路径下appstand.fmb , template.fmb文件,EBS_Sys/visappl/11.5.0/au/resource路径下所有后缀名为.pll的文件下载到C:\OracleDev6i_new\FORMS60路径下或者是注册表中oracle/formpath键值对应的某个路径下

 

Lab 1: Architecture

    该部分介绍了如何在EBS中定义APPLICATION。

    首先在系统中定义DEMVC用户,授予Application Developer和System administrator的 Responsibility,这两个Responsibility在随后的设置中会经常用到,可以说是开发中必须使用的两个Responsibility

     我在系统中定义下图所示的Application, Application和 Short Name在后续的forms、menus、concurrent programs和other application components设置中会经常用到,short name不应包含空格、长于四个字符并且不能和系统中其它Application重名,建议取名与Application全名意义相关,Basepath中输入的是服务器上环境变量的名字,该变量会被解析成服务器的对应路径,存放开发好的form文件(.fmx)及其相关的外部文件等。  


 


其中AU_TOP路径下存放了许多共享文件,该路径下的doc、forms、resource子路径可以存放客制化的帮助文件、appstand.fmb和referenced forms、.pll库文件。

 举个例子来说,我曾经把客户机上开发的form直接上传到server的DEM_TOP路径下,直接在该路径下编译生成.fmx文件。

   cd $DEM_TOP/forms/US  ;

f60gen module=DEMVCEOR userid=apps/apps

虽然编译、输出也能成功,但是生成的窗体在运行时总是有问题。

于是我将客户机上开发的form先上传到server的DEM_TOP路径下,编译后在DEM_TOP路径下生成.fmx文件

   cd $AU_TOP/forms/US 

f60gen module=DEMVCEOR userid=apps/apps output_file=$DEM_TOP/forms/US/DEMVCEOR.fmx

  这样就一点问题也没有了。

  我想原因是存放在DEM_TOP路径下的.fmb文件在编译时无法获得依赖的文件或资源,虽然生成了可执行文件,但是因缺少依赖项不可用。

 至于诸如AU_TOP、DEM_TOP的环境变量是如何对应操作系统路径的,是因为在Sever端APPL_TOP路径下的.env文件设置了系统的环境变量,此外APPL_TOP/admin路径下的adovars.env文件可设置Custom Applications的环境变量。


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


   这些结构,自底向上依次创建。

   随后的工作是Register Oracle User(Schema) [因为我使用的是Apps,该步省略)]——Add Your Application to a Data Group(我设置Data Group为Standard,Oracle ID为APPS)——Create a Responsibility——Create an Application User(这个我先前已创建过DEMVC用户)——Register Your Tables(这个步骤是针对那些需要用到flexfields or Oracle Alerts的tables,在本练习中有提及但是没有用到,系统已经预先定义好了,我们直接拿过来用就行了)

 
Oracle Form. 6i开发的心得(2)2011-02-14 14:50Lab 2: Menus and Function Security

   在这章主要学习了如何设置Menus和 Functions的结构,使其显示在Navigation Windows中

  设置次序为:

   Forms——Functions(form. function / Subfunction)——Menus and Submenus——Responsibilities——Applications

   定义Forms时,注意Application代表该form属于哪个Application(定义Application时Application字段的内容),form字段必须和生成的.fmx同名,User Form. Name字段填写的内容在随后的Function窗口中将被用来标示该form。

  下图定义了名为DEMVCEOR的form,也就是说在Server端$DEM_TOP\forms\US路径下必须生成名为DEMVCEOR.fmx的可执行文件。注意:User Form. Name-“TEAM VC DEMO ORDERS”会在后面的设置中用到。


  定义Functions时,Description选项卡中Function 的内容唯一标示一个function,在启动窗体的代码里有时会用到Function的内容用于判断某个function的可用性(随后会有用到该情况的练习,个人认为subfunction这样使用的比较多)。User Function Name的内容会在用户将function添加到menu时用以标示该function和出现在Navigator窗体右侧的top ten list中。Description 内容我觉得是出现在Navigator窗体左侧用以标示该窗体。Form. 选项卡中form字段可以根据定义forms时的User Form. Name选出,Parameters的字段我没设,好像本练习没用到。


  随后的工作是为Menu添加Submenu和function,比较简单。其中User Menu Name在向Responsibility中添加menu时用来标示menu; Sequence 我的理解就是menu的代号,用于描述menu和submenu之间的结构;Navigator Prompt内容会出现在Navigator窗体左侧用以标示该menu。Description: Navigator 窗体functions选项卡上方menu路径下的menu描述。

 

 然后就可以在Responsibility窗体中添加Menu或function了。 

 最后在User窗体中添加Responsibility。

 

 

 

Lab 3: Container Objects

 下面就真正开始进入Oracle form的开发。拷贝template.fmb的副本,打开后重命名为在定义Forms时form字段的名字,必须这样命名,否则系统找不到该form的可执行文件,我的窗体文件就叫DEMVCEOR.fmb

  可以这么认为,最大的容器对象是Form,一个form可以包括多个window,一个window可以包括多个canvas,canvas又分content canvas,tab canvas和stacked canvas,content canvas上可以容纳tab canvas和stacked canvas,一个canvas又可以布置多个item;另一个体系是data,一个form可以包括多个datablock,一个datablock可以包括多个item。我觉得item是连接data和layout的桥梁,如果是基于数据库的item,就必须指定Database Item属性为Yes,并为Column Name属性赋相应值。若该Item需要显示在Canvas上,那就必须指定该Item的Canvas属性。除此之外,还需为每个canvas指定其对应的window属性。

   可以在block的属性中设置scroll bar属性;如果block要显示的是单条记录的话,设置Navigator Style属性为Same Record,如果要显示的是多条记录的话,设置Navigator Style属性为Change record;假如block的数据源是基于复杂视图的话,Key Mode属性设为Non-Updateable;

     在设置master-detail blocks时,在master block中可以设置许多控制master-detail行为的属性。例如:Master Deletes,Coordination等。Relation应当创建在master block中。我在练习中曾犯过这样的错误,在master和detail blocks中设置了相同的Relation,结果挂到系统里一看detail block中的数据怎么也出不来,同事告诉我这是因为两个block中的Relation形成了死锁。他还说如果要删除detail block的Relation,应在master block的data block wizard中进行,反之亦然。我自己在object navigator里直接删除了detail block的Relation,结果也没问题,所以我就没用他的方法。

   此外,还需设置各个block的顺序,如果没有显式指定各个block的Previous Navigation Data Block和Next Navigation Data Block,那么导航顺序将默认为各个

Block在object navigator中的顺序。此外module的Navigation Block属性将决定窗口打开时展现的block及其所在的Canvas、window。此外,最好将每个block的主键字段的item-level Primary Key属性设为Yes。

在本练习中,我还遇到了练习中遇到的第一个trigger,在后面的一节我会重点总结trigger。

还有一点不可忽视的是,对于object navigator中窗体的每个对象,都应尽可能地设置正确的Property Class,否则在Form. 6i中设计的对象在窗体被挂到系统中时无法正常显示,我被该问题困扰许久,直到问过同事才搞清楚,Form窗体的大多数对象都有对应的Property Class,开发时应一一设上。
 
Oracle Form. 6i开发的心得(3)2011-02-14 14:51Lab 4: Widgets

这个练习主要介绍了各种widget。

无论是在region、block还是在window中,item的导航顺序(Tab)总是从左到右,从上到下。

1.Text Items

   可以根据需要为Text Items设置不同的Property Class。大多数情况下property class—TEXT_ITEM,只读文本框property class—TEXT_ITEM_DISPLAY_ONLY,多行文本框property class—TEXT_ITEM_MULTILINE,日期文本框property class—TEXT_ITEM_DATE,WHO日期文本框 property class—CREATION_OR_LAST_UPDATE_DATE,百分数文本框property class—TEXT_ITEM_PERCENT_FIXED。

  Validate from List—Yes意味着配合LOV使用的Text Items可以根据输入的内容自动在LOV中寻找对应的内容。

2. Display Items

Display Item应当设置的足够宽以显示数据库中取出的内容。

3. Check Boxes

    Check Boxes需要设置一个数据库内容对应的值。还有一类比较特殊的Check Boxes(property class—CHECKBOX_COORDINATION),用以控制Master-Detail Block的关联行为,在随后的练习中能看到。

4. Buttons

可设置Access Key。

5. Option Groups

   必须设定缺省值。

6. Poplists

   在可选值不超过15个情况下,Poplists是个不错的选择。和.net平台下的dropdownlist控件类似,它也有List Elements和List Item Value(大写字母)之分,对Poplists的编程应对应List Item Value。应尽量避免将Poplists设为multi-row blocks的首字段。

7. LOVS

LOV可以比Poplists容纳更多的记录,而且设置上也比较复杂,随后有专门的练习。

8. Descriptive Flexfields

关于Key弹性域和描述性弹性域后面的练习会有更详细的说明,我觉得这个东西有点难理解,暂且把它当成某个数据库字段来用了。

 

 

Lab 5: Layout


上图反映了Layout的一般顺序。

布局设置:Format—>Layout Options—>Ruler中可以设置Content Canvas标尺的间距;Format—> Ruler设置是否在Layout界面中显示标尺栏;Format—> Grid设置是否在Layout界面中显示间距点;Format—> Snap to Grid设置是否以设定大小进行控件在Layout界面中的移动;Format—> Show View设置是否在Layout界面中显示window的尺寸;Format—> Show Canvas设置是否在Layout界面中显示Content Canvas;

对于各个Item来说,考虑到界面转译的需要,应尽量给prompt、label等描述性字段留出足够的空间。

Coordination Check Boxes是个比较特殊的对象,如果Master-Detail Blocks分属不同的windows,Coordination Check Boxes应放在Detail Blocks所在window的右上方。否则如果Master-Detail Blocks在同一window中以分界线隔开,Coordination Check Boxes应放在分界线右边window最右边3个字符的位置。还有一种布局与folder title有关,放在folder title line最右边3个字符的位置。

 

 

Lab 6: Enhance Items: Create LOVs

LOV相对于Poplists来说,可以容纳的记录更多,使用起来也更加灵活。

创建正确的LOV的前提是创建正确的Record Group,顺序是:在select语句中选择需要的column—>选择需要显示的column—>设置column的title和description —> 设置column的Return Value—>指定能使用该LOV的item—>设置LOV的Title和尺寸。

如果LOV在发现符合查询条件的返回记录只有一条时,自动选择该记录。如果查询到的记录很多的话,设置Automatic Refresh—> Yes,系统会缓存这些记录。

对于和LOV配合使用的其它item对象来说,必须指定其List of Values属性。
 
Oracle Form. 6i开发的心得(4)2011-02-14 14:52Lab 7: Coding with PL/SQL 

   我觉得这节是学习form开发的重点和难点。这一节里又会出现很多的概念、术语、以及和Oracle数据库相关的一些知识。

   首先我花了些时间去搞清楚Handler的概念,于.net平台下的事件驱动机制类似,form也依靠events,不同点在于form把这些事件分了form,block,item三个层次,以trigger的形式面向开发者。而Handler就类似于处理事件的函数,不同的是Handler既可以位于database server层,又可以位于application server层。通常这些handlers被封装在package对象中,调用方式如:package_name. handler_name。按照作用对象的不同,Handler又可以分为Item handlers、Event handlers、Table handlers三类。

1.Item Handlers 

Item Handlers 经常会在多种trigger触发的时候被调用,比如一个Item会在WHEN-VALIDATE-ITEM、WHEN-NEW-RECORD-INSTANCE和初始化的时候被触发,每种触发事件的处理方法又不一样,于是定义如下的Handlers,并在相应的trigger中调用。

Eg:

PROCEDURE item_name (EVENT VARCHAR2) IS

BEGIN

IF (EVENT = ’INIT’) THEN

... /* your code here */

END IF;

IF (EVENT = ’WHEN_NEW_RECORD_INSTANCE’) THEN

... /* your code here */

END IF;

IF (EVENT = ’WHEN_VALIDATE_ITEM’) THEN

... /* your code here */

END IF;

END item_name;

 

2. Events Handlers

在某个Event发生时,可以调用多个对象的与该 Events相关的Events Handlers,需要注意的是Events Handlers必须以该Event命名。

 

3. Table Handlers

   Table handlers主要支持block-level views的insert、update、delete和locks操作。

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

4.编码标准

    Oracle提出了一下的编码标准来提高性能:

      *引用字段时,使用BlockName.ItemName的形式;

      *使用亚元直接用dual,不要用sys.dual和system.dual;

      *使用object IDs获得更好的性能;

declare

x_id item;

begin

x_id := find_item(’block.item’);

/* your code here */

end;

      *总是使用显式游标;

      *超过64k的Package存放与database server层,每个Package包含的Procedures和Functions总数不超过25;

      *处理好异常;(本练习接触的不多)FND_MESSAGE显示系统信息,FORM_TRIGGER_FAILURE停止application层的事务,APP_EXCEPTION.RAISE_EXCEPTION停止database层的事务,此外还需编码处理可预计的database server异常。

     同时Oracle还建议尽可能用Application语法代替Forms built-ins;

Eg:

* 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;

5.Examine工具

最后我还要提一下一个很有用的调试工具,当系统运行时,窗体Menu:Help–>Tools–>Examine,可以在这个工具中查看系统变量,全局变量,操作系统环境变量,SQL*PLUS变量,用户概要文件设置以及显示/隐藏的字段值。虽然我现在还不大熟悉这个功能,直觉告诉我能否用好这个工具也是高手与菜鸟的区别之一。

 

Lab 8: Controlling Windows

   这节练习主要介绍了如何对windows进行控制。概括起来包括:打开windows,关闭windows,控制master-detail windows。

  在分别介绍之间,先来了解以下windows的动作规则:

   *在非模型窗体中不要使用button编码关闭窗体,使用关闭箭头或menu项;

   *可以通过CASCADE, RIGHT, BELOW, OVERLAP or CENTER参数设置detail window 与master window之间的相对位置;

   *Context-dependent titles remain in context这个没大理解,估计说的是动态生成window title依赖于那些在title中出现的字段;

   *在query模式下window不能被关闭;

   *关闭window后并不会自动执行commit操作,除非开发人员编码提交;

   *关闭parent window时系统会提醒用户是否保存detail window变化;

   *关闭window后,焦点会离开该窗体返回previous block所在windows;

   *关闭master window会自动关闭其所有detail windows和其相关的find windows;

*关闭first window会退出form。
 
Oracle Form. 6i开发的心得(5)2011-02-14 14:521.打开windows

一般情况下 会在form-level的pre-form. trigger中调用app_window.set_window_position函数来指定the first window.

FND_STANDARD.FORM_INFO('$Revision: 115.12 $', 'Demo Order Form', 'DEMVCEOR', '$Date: 2006/07/18 17:12  $', '$Author: appldemvc $');

app_standard.event('PRE-FORM');

app_window.set_window_position('ORDERS', 'FIRST_WINDOW');

其它情况下,如使用键盘切换当前Block或用button打开窗体时,需使用APP_CUSTOM.OPEN_WINDOW函数,APP_CUSTOM.OPEN_WINDOW 中又会调用app_window.set_window_position。如果是Master-Detail Block需要建立coordination关系的,在APP_CUSTOM.OPEN_WINDOW('LINES')时,调用APP_WINDOW.SET_COORDINATION('OPEN-WINDOW', :CONTROL.ORDERS_LINES, 'ORDERS_LINES');

PROCEDURE LINES(EVENT VARCHAR2) IS

BEGIN

  IF (EVENT IN ('WHEN-BUTTON-PRESSED', 'KEY-NXTBLK'))

    THEN APP_CUSTOM.OPEN_WINDOW('LINES');

  ELSE

    FND_MESSAGE.DEBUG('Invalid event passed to orders.lines: '|| EVENT);

  END IF;

END LINES;

可在Block-level的KEY-NXTBLK trigger 或Block-level的when-button-pressed trigger中调用这些PROCEDURE;需要注意的是这些代码请先在app_custom包中改写,对应包中的注释,可以很好理解OPEN_WINDOW的逻辑。

总结一下打开windows的逻辑:1设置windows位置;2重置master-detail关系(如有需要的话);3导航到window中的一个block。

 

2.关闭windows

关闭windows的逻辑可参看app_custom.close_window函数及其注释,不难发现步骤1是判断该窗体是否处于query-mode,是的话不允许关闭;2判断该window是否为the first window,是的话调用app_window.close_first_window函数;3假如window是Detail window的话,延迟其与Master window的同步同步关系(APP_WINDOW.SET_COORDINATION)并GO_BLOCK(' previous-block ')。最后是Hide_Window。

此处我有个疑问:Oracle文档一直提是延迟(defer)而不是重设coordination,难道close windows时会自动使coordination check box变成‘DEFERRED’,一直不解。另外Hide_Window是不是也仅使window不可见,而不是使其在内存中卸载。

 

3.控制master-detail windows

master-detail windows的行为控制需通过coordination check box来表现,coordination check box创建在Control block下,Property Class –> CHECKBOX_COORDINATION。coordination check box选中与否决定其取值是‘IMMEDIATE’还是‘DEFERRED’。如果coordination check box选中的话,即使焦点在Master window时,Detail window中数据会于Master window同步;如果coordination check box没有选中,Detail window的内容不会于Master window同步,直到焦点落在Detail window时,系统才会执行查询更新数据。这个过程自己试验一下会非常的清楚。使用coordination check box必须在其WHEN-CHECKBOX-CHANGED trigger中重置Master-Detail windows的同步关系。

Eg:

PROCEDURE orders_lines(EVENT VARCHAR2) IS

  BEGIN

    IF (EVENT = 'WHEN-CHECKBOX-CHANGED') THEN

      APP_WINDOW.SET_COORDINATION (EVENT, :CONTROL.ORDERS_LINES, 'ORDERS_LINES');

    ELSE

      FND_MESSAGE.DEBUG('Invalid event passed to control.order_lines '|| EVENT);

    END IF;

END orders_lines;

 

4. Set Context-Dependent Window Title

     动态设置相关联的窗体标题依靠APP_WINDOW.SET_TITLE函数,注意在两类trigger中需要调用它。一类是Block-level的pre-record和on-insert trigger,另一类是相关联字段的when-validate-item trigger,我一直没搞清楚pre-record trigger为什么要调用APP_WINDOW.SET_TITLE函数。

 

Lab 9: Tabbed Regions

这节的内容让我记忆犹新,痛定思痛,不为别的,就为它让我郁闷了好几天,出现了错误却怎么也找不到原因。事情是这样的:根据练习的要求,我在Object Navigator中创建了多个canvas并且按要求将各个widget分布其上,然后在Form. 6i环境中编译成功,随后上传到服务器上重新编译,却总是报有个canvas上的item超界,无法生成可执行文件。检查了许久,发现不了错误,郁闷的重做一遍情况还是如此;无奈,请教同事,告知,在content canvas中添加tab canvas和stacked canvas时,需在content canvas的layout edit中创建,在Object Navigator中创建的将不会出现在content canvas上,因此即使item超界也发现不了,这才如梦方醒,真是麻烦啊!

关于Stacked Canvas尺寸和位置的设置,从属性窗口中发现有两套位置体系:一是Viewport X Position,Viewport Y Position,Viewport Width,Viewport Height;二是Viewport X Position on Canvas,Viewport Y Position on Canvas,Width,Height。试验了一下发现决定Stacked Canvas尺寸和位置的是第一套体系,修改参数后可在layout editor中看出明显的差别。而第二套体系参数修改后Canvas没什么变化,而且看的操作提示中所有Stacked Canvas的Viewport X Position on Canvasà0,Viewport Y Position on Canvasà0,Width,Height值分别比Viewport Width,Viewport Height值略大而已。我想这套体系总是有作用的,只是我不知道而已,请不吝赐教。

对于某个block的scrollbar,是可以修改其canvas属性的,决定scrollbar显示在指定的canvas上。

Tabbed Regions的行为特性:

*主键字段和fixed字段一样,最好放在fixed field stacked canvas,不要放在可变区;

*tab控制列表可从键盘激活(好像没碰到);.

*利用tab键导航item时能够跨越tab页;

*tab页可以根据需要动态地启用和禁用;

*Tabs must remain operable in query by example mode(不理解)

 

Tab-related Built-ins

 Set/Get_tab_page_property (canvas.tabpage...)

– ENABLED      – LABEL      – VISIBLE

Set/Get_canvas_property (canvas...)

– TOPMOST_TAB_PAGE

Set/Get_view_property (canvas...)

– VIEW_X/Y_POS      – HEIGHT     – WIDTH

 

Tab-related Variables

:SYSTEM.TAB_NEW_PAGE

– name of the tab page the user clicked on

:SYSTEM.EVENT_CANVAS

– name of canvas that owns the newly-selected tab page

:SYSTEM.TAB_PREVIOUS_PAGE

– name of the tab page that was topmost before the user clicked on the new one

 

Dynamically Changing Tabs

Dynamically hide tabs only at form. startup.

– set_tab_page_property(...VISIBLE)

Dynamically enabling/disabling tabs.

– set_tab_page_property(...ENABLED...)

 

此外是与tab相关的trigger,在About Trigger节介绍。
 
Oracle Form. 6i开发的心得(6)2011-02-14 14:53Lab 10: Currency Fields

这节练习主要介绍如何动态控制item的显示格式和动态生成非database item。尽管是以currency number为例,对于其他类型的字段,处理方式可以借鉴

 

1.动态控制item的显示格式

在Block-level的Post--query trigger和Item-level的when-validate-item trigger调用handler,控制显示格式。

Eg:

PROCEDURE FORMAT_PRICE(EVENT VARCHAR2) IS

BEGIN

  IF (EVENT IN ('WHEN-VALIDATE-ITEM','POST-QUERY'))

THEN

APP_ITEM_PROPERTY.SET_PROPERTY('LINES.SUGGESTED_PRICE',FORMAT_MASK,      FND_CURRENCY.GET_FORMAT_MASK(:ORDERS.CURRENCY_CODE,GET_ITEM_PROPERTY('LINES.SUGGESTED_PRICE',MAX_LENGTH)));

    APP_ITEM_PROPERTY.SET_PROPERTY('LINES.TOTAL_PRICE',FORMAT_MASK,      FND_CURRENCY.GET_FORMAT_MASK(:ORDERS.CURRENCY_CODE,GET_ITEM_PROPERTY('LINES.TOTAL_PRICE',MAX_LENGTH)));

  ELSE

        FND_MESSAGE.DEBUG('Invalid event passed to lines.format_price '||EVENT);

  END IF;

END FORMAT_PRICE;

 

2.动态生成非database item

在Block-level的Post--query trigger和Item-level的when-validate-item trigger调用handler,仍可动态生成非database item的值。前一个trigger在从数据库中获取记录时起作用,后一个trigger在修改记录后起左右,使得非database item随着database item值的改变而改变。

Eg:

PROCEDURE TOTAL_PRICE(EVENT VARCHAR2) IS

BEGIN

  IF (EVENT IN ('WHEN-VALIDATE-ITEM','POST-QUERY'))

THEN

:LINES.TOTAL_PRICE:= NVL(:LINES.ORDERED_QUANTITY,0)*NVL(:LINES.SUGGESTED_PRICE,0);

  ELSE

    FND_MESSAGE.DEBUG('Invalid event passed to lines.total_price '||EVENT);

  END IF;

END TOTAL_PRICE;

 

 

Lab 11: Runtime Behavior.

   这节练习主要介绍了在运行时怎样对各种类型的Item(日期、数字、字符串等)进行控制。

   日期型的Item往往会使用Calendar LOV,要点在于:LOV—> ENABLE_LIST_LAMP,Validate from List LOV—>No,Item-level KEY-Listval trigger调用CALENDAR.SHOW;且trigger属性Execution Hierarchy—>Override,fire in enter-query mode—>No。

    Form还经常会用到sequence,往往是在插入记录前,需要一个流水号,作为插入记录的主键字段。

Eg:   

PROCEDURE ORDER_ID(EVENT VARCHAR2) IS

  CURSOR C IS

    SELECT DEM_ORDERS_S.NEXTVAL FROM DUAL;

BEGIN

  IF (EVENT='ON-INSERT') THEN

    OPEN C;

    FETCH C INTO :ORDERS.ORDER_ID;

    IF (C%NOTFOUND) THEN

      CLOSE C;

      RAISE NO_DATA_FOUND;

    END IF;

    CLOSE C;

  ELSE

    FND_MESSAGE.DEBUG('Invalid event passed to orders.order_id: '||EVENT);

  END IF;

END ORDER_ID;

此段代码会放block-level on-insert trigger中被调用,而且应放在insert_row过程之前执行。

如果有title依赖Master window的Detail window,那么在Master window插入新记录时,应立即随后设置Detail window的title。

Eg:(on-insert trigger)

ORDERS.ORDER_ID('ON-INSERT');

ORDERS.INSERT_ROW;

LINES.LINES_TITLE('ON-INSERT');

除了这些,还有很多控制格式的API,如:APP_MUMBER,APP_DATE,FND_DATE,FND_CURRENCY,需要的时候再去手册中查阅。

 

 

Lab 12: Conditionally Dependent Items

这节练习主要是介绍窗体上各个widget之间的Dependencies控制。Dependencies

我觉得这种控制的关键在于:1确定具有Dependencies关系的items,2正确设置这种Dependencies控制逻辑,3在恰当的时刻调用这些逻辑。

Dependencies关系有多种,比如说一个item依赖一个item,多个item依赖一个item,一个item依赖多个item等。

控制逻辑也有多种,比如说一个item只在另一个item非空时才被激活;一个Item只在另一个item满足某些特定条件时才被激活;一个item从非激活状态变成激活状态后不能为空;一组Item同时只能有一个Item可被赋值;一组Item如果有一个Item被赋值,其他Item也一定要被赋值;当被依赖的item值发生变化时,依赖项值立刻被清空等。

我们经常会在以下的trigger中调用Dependencies控制逻辑:PRE-RECORD,when-create-record, when-validate-item,when-checkbox-changed, when-radio-changed,when-list-changed和‘INIT’事件。我觉得PRE-RECORD和INIT是在操作数据前初始化这些逻辑,而其他是在item发生变化后验证这些逻辑,满足条件则进行后续的动作。

Eg:

APP_FIELD.SET_DEPENDENT_FIELD(EVENT,

:block.master_item = CONDITION,

’block.dependent_item’);

 

APP_FIELD.SET_DEPENDENT_FIELD(EVENT,

((:block.master_item1 IS NOT NULL) AND

(:block.master_item2 IS NOT NULL)),

’block.dependent_item’);

 

APP_FIELD.SET_EXCLUSIVE_FIELD(EVENT,

’block.item1’,

’block.item2’,

’block.item3’);

 

APP_FIELD.SET_INCLUSIVE_FIELD(EVENT,

’block.item1’,

’block.item2’);

 

APP_FIELD.SET_REQUIRED_FIELD(EVENT,

(CONDITION),

’block.item’);

 

Oracle建议使用APP_ITEM_PROPERTY.SET_PROPERTY API函数取代SET_ITEM_PROPERTY (Oracle Forms built-in函数)动态设置Item的属性。可以设置的属性有:DISPLAYED,ENABLED,ENTERABLE,ALTERABLE (item instance level),ALTERABLE_PLUS (item level),REQUIRED。

设置Item有两种方式:根据Item_ID和根据Item_NAME。

Syntax Using Item ID

item_id:= Find_item(’block_name.item_name’);

app_item_property.set_property(item_id,

property_name,

setting);

Example

note_item_id := Find_item(’orders.note’);

app_item_property.set_property(note_item_id,

REQUIRED, PROPERTY_ON);

Syntax Using Block.Item_Name

app_item_property.set_property(

’block_name.item_name’,

property_name,

setting);

Example

app_item_property.set_property(’orders.note’,

DISPLAYED, PROPERTY_OFF);

 

与之相对应的是可以使用APP_ITEM_PROPERTY.GET_PROPERTY API函数取代GET_ITEM_PROPERTY (Oracle Forms built-in函数)动态获取Item的属性。

设置Item-level和Item-instance-level属性时需特别的注意,Item-level属性影响所有的记录,而Item-instance-level属性只影响特定的记录(我的理解是当前行)。对于诸如INSERT_ALLOWED,UPDATEABLE和NAVIGABLE等属性来说,是能够在item level和item-instance level重复设置的。如果在item-level设成OFF,而在item-instance level设成ON ,重复设置的最终效果仍是OFF。如果同时设了ALTERABLE和ENABLED,可能最终效果也不会像预期的那样。所以碰到这样的情况时要特别小心。

     此外,本节还提到一些有关User Profile的知识,好像没什么具体示例,没大搞明白,留待以后研究吧。
 
Oracle Form. 6i开发的心得(7)2011-02-14 14:55Lab 13: Message Dictionary

本节介绍如何在窗体中使用Message。使用Message应该是挺简单的,主要是两个步骤:设置Message和显示Message。

 

1.     设置Message

我觉得按照是否需要在Message Dictionary定义Message可以分为两种。下图显示的是在Message窗体中定义了一个名为DEMVC_SHIP_BEFORE_ORDER的Message。

      

Oracle建议Name采用大写字母加下划线的命名方式,Application就是需要用到该Message的Application,Language当前语言(应该与后面的翻译有关),Number在显示的时候有时会用到(前面还会带前缀,不填的话就不出现,前缀也会省略),Current Message Text就是要显示的文本了,带’&’的则说明该Message带参数,图中的Message就有&ORDERDATE和&SHIPDATE两个参数。

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

在Message Dictionary定义完Message后,在窗体中显示Message之前,还需要在窗体中定义Message。步骤为1FND_MESSAGE.SET_NAME设置MESSAGE_NAME和Application Short Name;2 FND_MESSAGE.SET_TOKEN设置MESSAGE参数,注意必要时对参数进行格式转换。

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;

有些Message并不需要先在Message Dictionary中定义,只需在窗体中定义后就可直接调用,如:

Example 1

/* Set up a specific string (from a variable) and show it */

FND_MESSAGE.SET_STRING (sql_error_message);

FND_MESSAGE.ERROR;

Example 2

/* Set up a specific string and show it */

FND_MESSAGE.SET_STRING (’Hello World’);

FND_MESSAGE.SHOW;

 

下例展示了FND_MESSAGE.RETRIEVE函数的用法。我的理解是必须在服务器端执行APP_EXCEPTION.RAISE_EXCEPTION过程后才RETRIEVE到信息。

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*/

 

再来看下fnd_message.get的用法,我感觉有点像赋值。

fnd_message.set_name(appl_short_name,message_name);

fnd_message.get(window_title);

set_window_property(’WINDOW_NAME’, TITLE,

window_title);

 

2.显示Message

    显示Message使用FND_MESSAGE API函数,根据需要选用。

? FND_MESSAGE.ERROR to display an error

? FND_MESSAGE.WARN to display a warning

? FND_MESSAGE.QUESTION to display a question

? FND_MESSAGE.SHOW to display an informational message

? FND_MESSAGE.HINT to display a hint


还有一点比较遗憾,练习中提及可以设置外部文件,获取和写入Message信息,我试了,好像有报错,可能是路径不对,以后再研究吧
 
Oracle Form. 6i开发的心得(8)2011-02-14 14:55Lab 14: Flexfields

     刚开始看这一节的文档时,觉的Flexfields的概念很难理解,生吞活剥地按照步骤做完这一练习,再联系后面几节的练习又生吞了一遍文档,Flexfields的面貌才渐渐清晰过来。

    学完了这节,我才知道为什么Orders表中会有atrribute1~attribute9这几个奇怪的字段。

     Flexfields分为两种,Key Flexfields和Discriptive Flexfields,Flexfields中包含的字段叫做segment。尽管使用Flexfields的目的都是使系统获得更加具有柔性的数据结构,但是两种Flexfields的作用、组成结构和使用时机都有很大的不同。

1. Key Flexfields

     在我看来,Key Flexfields可以看作是Keys的集合,这些Keys每个都对应与数据库中的一个字段。而且每个Key Flexfield都对应一个Combination Table,按照Combination Table的不同使用Key Flexfields主要可分为三类:一是Combinations Form(Maintenance Form), Key Flexfield对应的字段的基表就是Combination Table,Table中会包括structure id和很多segment字段,segment字段记录那些Keys的值;二是Form. with Foreign Key Reference ,基表中Key Flexfield对应的字段作为外键引用了Combination Table中的主键字段,同样改Table中也会包括structure id和很多segment字段。通过查看Combination Table的记录,我觉得该表包含了多个Key字段的所有可能的组合(如果Key很多的话,这样是不是重复太多了);三是Form. with a Range Flexfield ,使用这种类型的Flexfield,并不需要真正绑定一个Combination Table,只需在一个Table设置structure id和多个segment N_LOW、segment N_HIGH字段,多用于出报表的情况。

     使用Key Flexfield时,由于可为每个Key Flexfield定义多个structure(对应上段提及的structure id),因此在系统中根据商务规则的需要动态的改变Key Flexfield的structure。从下图可看出,title为Accounting Flexfield的Flexfield具有多个structure。


 

2. Discriptive Flexfields

     Discriptive Flexfields相对来说比较容易些,没有那么多类别,依赖的基表就是Form的基表,表中包含了Attribute Catalog和多个Attribute N字段,至于Attribute Catalog字段我个人觉得和Key Flexfield中的structure id字段功能类似,不知道是否正确。


    与定义Key Flexfield类似,定义Discriptive Flexfields时定义一个Flexfield时不过指定对应的Application和Base Table,就是向指定场所和提供了原材料,至于如何将这些原材料组合成什么样的structure以及这些structure中的segment又有什么特殊的要求,其实是在Segment窗体中设置的。

3.开发Flexfields的步骤

*设计合理的Combination Table表结构,根据Flexfield的类别,创建相应的Combination Table并设计合适的attribute、segment、structure id、attribute catalog等字段。

*在Form. 6i中创建Flexfield:

Discriptive Flexfield:property class—〉TEXT_ITEM_DESC_FLEX,List of Values—〉ENABLE_LIST_LAMP 

Key Flexfield:List of Values—〉ENABLE_LIST_LAMP 

如果是Range Flexfields,就创建2个field。

*调用Flexfield的API函数,在Form-level的when-new-form-instance trigger中调用。

? 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_DESCR_FLEX.DEFINE(

BLOCK=>’ORDERS’,

FIELD=>’DESC_FLEX’,

APPL_SHORT_NAME=>’DEM’,

DESC_FLEX_NAME=>’DEM_ORDERS’);

这种区别和不同Flexfield的定义方法有关。

此外在Form-level的下列trigger中,加入后面的函数。如果在block/item-level上override了这些trigger,在处理代码中加上后面的函数。Block/ item-level的POST-QUERY trigger,Execution Hierarchy--à‘After’。

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-VALIDATERECORD      FND_FLEX.EVENT(‘WHEN-VALIDATE-RECORD’);

WHEN-NEW-ITEMINSTANCE    FND_FLEX.EVENT(‘WHEN-NEW-ITEMINSTANCE’);

WHEN-VALIDATE-ITEM        FND_FLEX.EVENT(‘WHEN-VALIDATE-ITEM’);

   另外TEMPLATE form在下列form-level triggers调用的APP_STANDARD.EVENT 过程直接调用了FND_FLEX函数。如果在block/item-level上override了这些trigger,在处理代码中加上后面的函数。

KEY-EDIT                     APP_STANDARD.EVENT(‘KEY-EDIT’);

KEY-LISTVAL                 APP_STANDARD.EVENT(‘KEY-LISTVAL’);

POST-FORM                   APP_STANDARD.EVENT(‘POST-FORM’);

*在系统中注册Flexfield

    注册Key Flexfield时,可以使用Qualifiers来设置Key之间的规则,Columns设置包含的可用列(尽管不会全被用到),KFV View Name和定义Descriptive Flexfield时DFV View Name类似,是一种特殊的View,用于生产报表,其名字我无法更改,猜想是系统根据Table Name自动生成。(假如一个Table使用了两个Description Flexfield,DFV View Name该如何命名?)


*在系统中定义Flexfield

定义Flexfield,Key Flexfield实际是用Segment窗体定义Structure及其对应的Segments,Descriptive Flexfield是用Segment窗体定义Context及其对应的Segments,每个Segment的值还可通过Value Set ,Segment Value去控制,具体的设置方法我就没有深入研究了。

最后我总结一下Flexfield的使用步骤:首先定义合理的Combination Table,这个是必须的前提。然后在系统里注册并定义需要的Flexfield,接着在Form. 6i中添加Flexfield字段并设置好属性,最后在Form-Level的when-new-form-instance trigger中调用函数设置Flexfield到相关字段,注意在相应级别的trigger中添加处理函数或修改属性。
 
Oracle Form. 6i开发的心得(9)2011-02-14 14:56Lab 15: Query Find

Query Find的主要作用是在Non-Query状态下查询记录,而且比query mode要灵活方便。主要有两类用法:Row-LOV和Find window。所有queryable block都应该支持这两种用法中的一种,否则主菜单中ViewàFind…/Find All会报错。

 

1. Row-LOV

Row-LOV类似与普通LOV,在下列情况下适于使用:

*一次只返回一条记录,特别适合single row block;

*在detail block中自动查询关联master block的所有记录;

*基于主键而不是其他属性查询记录;

*数据量很少的情况。

下面是创建Row-LOV的步骤:首先创建一个parameter用于存储返回的Primary Key,接着创建合适的Row-LOV设置Primary Key返回给parameter,再创建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;

最后创建block-level QUERY_FIND trigger (Execution Hierarch—>Override)

app_find.query_find('ORDER_LINES_QF');

 

2. Find window

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

*一次返回多条记录,特别适合multi-row block;

*需要多个查询条件关联进行复杂查询的;

*数据量很多的情况。

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

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

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

*修改Find Windows的title名为“Find ”;

* 编辑NEW Button的WHEN-BUTTON-PRESSED Trigger;

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

*编辑FIND Button的WHEN-BUTTON-PRESSED Trigger;

  :parameter.G_query_find := 'TRUE';

app_find.find('ORDERS');  results block name

:parameter.G_query_find := 'FALSE';

如果查询到数据,则关闭Find Window关闭回到原来的Block,显示查询到的记录。

*编辑Find block-level的KEY-NXTBLK Trigger

  :parameter.G_query_find := 'TRUE';

app_find.find('ORDERS');

:parameter.G_query_find := 'FALSE';

作用与FIND Button相同。

*正确导航

      Find block的Previous Navigation Data Block应设为results block,应保证用户即使不作查询也能退出Find Window。而通过导航Block进入Find Window也是不推荐的。

*开发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控制逻辑。

  *合理设置Find Window的大小

*创建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;

  *创建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没有查到数据时,焦点仍然停留在该窗体中。
 
Oracle Form. 6i开发的心得(10)2011-02-14 14:57Lab 16: Advanced Function Security

  在Lab2中我们了解了Basic Function Security,这是一种基于responsibility、menu和function(功能)的管理机制。Advanced Function Security将考虑更加详细的安全机制,用户是否有权使用某个form中的某个subfunction,是否有权看见form的某个部件,是否有权打开新的窗体等等。

对于这种安全控制策略,我的理解是这样的:首先在系统里定义一个subfunction,然后把它挂到某个responsibility的menu下,然后在form里判断登录的responsibility是否具有对form里特定function的使用权限,激活或禁用这些function。


定义subfunction时,还要在Form选项卡里选择依附的Form,在Property选项卡里将Type设置为subfunction。然后将该subfunction挂到指定的menu下面,下图所示的是把两个用户自定义的Function:VC Demo Form.:SPECIAL和VC Demo Form. DEMVC_DEMVCEOR_PRINT_ORDER挂到了TEAM VC MENU的下面,注意因为它们的Prompt字段没有赋值,因此它们不会出现在Object Navigator中,实际上也不需要出现。

如果不把subfunction挂到menu下面,用户将无法访问该subfunction 。


 

然后在form的pre-form. trigger中调用FND_FUNCTION.TEST函数验证登录用户的权限:

IF (FND_FUNCTION.TEST('DEMVC_DEMVCEOR_PRINT_ORDER')) THEN

     --处理代码;

END IF;

处理代码往往是激活或禁用一些窗体部件,比如buttons, windows, fields, alternative regions等。实际也是激活或禁用某些窗体功能。

此处我有个疑问,我定义的Function如果Type设置为subfunction,系统就会报错。设置为Form,其他不变,运行正常,百思不得其解。

 

  Oracle EBS还允许客制化Form的菜单栏。

用户最多可以定义45个form-level的trigger,名称必须为SPECIALn,其中SPECIAL1 to SPECIAL15属于Tools菜单项,SPECIAL16 to SPECIAL30属于Reports菜单项, SPECIAL31 to SPECIAL45属于Actions菜单项,其中Reports、Actions的名称可以被修改。

初始化示例:

   IF (FND_FUNCTION.TEST('DEMVC_DEMVCEOR_PRINT_ORDER')) THEN

  app_special.instantiate('SPECIAL1','&Print Order');

  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');

    app_special.instantiate('SPECIAL4_CHECKBOX','Special &4 Box');

    app_special.set_checkbox('SPECIAL4_CHECKBOX','TRUE');

    app_special.instantiate('SPECIAL18','Specia&l 18 Line SEP', separator=>'LINE');

    app_special.instantiate('SPECIAL32','Specia&l 32 Line', '',TRUE,'LINE');

    app_special.instantiate('SPECIAL33','Specia&l 33');

    app_special.instantiate('SPECIAL30','Specia&l 30');

    app_special.instantiate('SPECIAL31','Specia&l 31 Line','',TRUE,'LINE');

    app_special.instantiate('SPECIAL45','Spe&cial 45');

 /* and display a button on the form. */

    app_item_property.set_property('orders.print_order',

                               DISPLAYED, PROPERTY_ON);

    ELSE

    app_item_property.set_property('orders.print_order',

                               DISPLAYED, PROPERTY_OFF);

    END IF;

app_special.instantiate设置菜单项的文字说明,app_special.enable设置菜单项是否可用,app_special.set_checkbox设置check_box菜单项的选定状态。

在form-level的SPECIALn trigger中,定义了这些菜单项选中后的处理代码,如下所示:

Eg1:

  demvceor.PRINT_ORDER('SPECIAL1');

Eg2:

 if (app_special.get_checkbox('SPECIAL3_CHECKBOX')='TRUE') then

     fnd_message.debug('Special 3 is True!');

else

     fnd_message.debug('Special 3 is False!');

end if;

  

  右键菜单也可以自定义,form-level PRE-POPUP-MENU trigger引用APPSTAND.EVENT('PRE-POPUP-MENU')建立default menu;建立block /item level pre-pop-menu trigger须(Execution Hierarchy àAfter)。

从block /item level pre-pop-menu trigger(Execution Hierarchy àAfter)中调用 app_popup.instantate函数初始化右键菜单项:

   procedure APP_POPUP.INSTANTIATE(

option_name varchar2,

txt varchar2,

initially_enabled boolean default true,

        separator varchar2 default null);

   Example

  ? This example results in a menu that has a line above the second custom entry and

   has the third custom entry grayed out (disabled)

APP_POPUP.INSTANTIATE(

‘POPUP1’,’First Entry’);

APP_POPUP.INSTANTIATE(

‘POPUP2’,’Second Entry’, TRUE, ‘LINE’);

APP_POPUP.INSTANTIATE(

‘POPUP3’,’Third Entry’, FALSE);

  右键菜单的处理函数写在名为POPUP1 through POPUP10的trigger中,应该在正确的level上建立这些trigger (通常是block/item level)。

 

Lab 17: Setting Up Attachments

我觉得Oracle中这个功能很好,就像在不破坏基表结构的前提下,动态地创建了若干个非结构化列,既可以存储普通文本,网页,还可以上传文件像word,excel,ppt,mp3等类型文件。比如对一行记录要做大量说明或是音频视频说明的,就可以采用这种办法。

在应用Attachment之前,先要搞清楚几个概念。一是Document Entity,Document Entity 可分为Base Entity和Related Entity,简言之都可以认为是数据库的表,也是Attachment依附的主体。一个Entity中每条记录都可附加多个Attachment,依靠Entity的Primary Key对应。

所以应用Attachment的第一个步骤是定义Document Entity。如下图所示:

    

 

另一个是先要搞清楚的概念Document Category,我的理解是Document Category限制了可附加Attachment的类型。所以第二个步骤是定义Document Category。

 

第三个步骤是定义Attachment Function,Attachment Function必须指定应用的窗体,在下图的Name中选择。Enabled Session Context Field我觉得是应用上下文Title。

    

接下来在Category窗体中选择第二步定义的Category,可选择多个。

打开Block窗体,下面的设置是关键。Block Declaration窗体中的Context字段与上下文Title有关。

    

 

再打开Entity Declaration窗体,设置Privileges选项卡的各项属性,接着再设置Primary Key Fields选项卡的各项,个人觉得这个地方非常重要不能设错。

 

    

 

   

 

在系统中运行Form,点击工具条别针型按钮,就可以添加附件了,效果如图所示:

   

 

 

  注意,如果你在某个Form中利用某个Entity的Primary Key应用了Attachment,在系统其他任何Form中,只要引用了该Entity的Primary Key,用户就可以查看基于这些Primary Key的Attachment。
 
Oracle Form. 6i开发的心得(11)2011-02-14 14:58Lab 18: Concurrent Processing

在系统里我们会经常用到并发处理,最常见的使用是报表以及数据库的操作。所有的并发处理都是由Concurrent Manager进程处理的,这其中包括了很多排队调度的机制以及出错恢复的机制,文档中说的很详细也很复杂。本节仅仅介绍了如何从系统中提交并发请求。提交并发请求的发式有很多种,比如使用Standard Request Submission,使用View/Requests或者使用某个窗体不见引发等等。

下面结合一下使用Concurrent Requests的步骤作进一步的说明。

第一步是建立Concurrent Program Executable,这是建立Concurrent Program的前提,随后建立的Concurrent Program会引用这个Concurrent Program Executable,一个Concurrent Program Executable可以被任意多个Concurrent Program所引用。


上图建立了一个名为DEMVCREPORT的Concurrent Program Executable,在Team VC Order Entry Demo Application中被使用,Execution Method指的是这种Executable的类型(Oracle Report、SQL*Loader、PL/SQL Stored Procedure等),Execution File Name这里指的是DEMREPRT.sql文件。Execution File Path指的是Execution File存放的路径,一般是在Application Basepath对应目录下。

第二步是建立Concurrent Program,Enabled checkbox决定是否立即激活该Concurrent Program;Execution Region中选择上一步定义的Concurrent Program Executable;Request Region中的Use in SRS checkbox决定是否可以在Standard Request Submission窗体中提交request;Output Region是输出设置的常规选项。

Copy to选项卡:可以把当前Concurrent Program的设置拷贝到另一个Concurrent Program中;Session Control选项卡:对当前Session属性进行设置,比如优化模式,回退段等;Incompatibilities选项卡:设置与当前Concurrent Program不可同时处理的其他Program;Parameters选项卡:设置当前Concurrent Program的传入参数。

如下图所示:当前Concurrent Program定义了一个名为Order Number的传入参数,同时还设置了它的验证方式和显示格式。


 


 

第三步是建立Concurrent Program Library,由于这一步在练习中没有出现,我也就没做。

  第四步是将Concurrent Program添加到System Administrator Reports request group中。这步说的比较隐晦,初学者往往不知道该怎么做。处理方法其实就是在Responsibility设置Request Group,如果不设的话,Concurrent Request没法用。

 


 

完成了以上的步骤后,就可以在系统中提交Concurrent Request。先看一下在SRS中提交的情况。


 

此外,在Form中也可以提交请求。先期工作如下:在form中添加一个parameter REQ_ID,用以存储concurrent request ID。编写Request的Handler,如下所示:

 

PROCEDURE PRINT_ORDER(EVENT VARCHAR2) IS

BEGIN

  IF (EVENT = 'SPECIAL1') OR (EVENT = 'WHEN-BUTTON-PRESSED') 

 THEN

     :parameter.req_id :=

     fnd_request.submit_request('DEMVC',

     'DEMREPRT','', '',

     FALSE, NAME_IN('ORDERS.ORDER_ID'),chr(0),'','','','','','','','',

     '','','','','','','','','','',

     '','','','','','','','','','',

     '','','','','','','','','','',

     '','','','','','','','','','',

     '','','','','','','','','','',

     '','','','','','','','','','',

     '','','','','','','','','','',

     '','','','','','','','','','',

     '','','','','','','','','','');

     IF :parameter.req_id = 0 THEN

         FND_MESSAGE.RETRIEVE;

         FND_MESSAGE.ERROR;

     ELSE

        IF :SYSTEM.FORM_STATUS != 'CHANGED' THEN

            IF app_form.quietcommit  THEN

                 /*form. commits without asking user to save changes*/

       fnd_message.set_name('SQLGL','GL_REQUEST_SUBMITTED');

              fnd_message.set_TOKEN('REQUEST_ID',  

        TO_CHAR(:PARAMETER.REQ_ID), FALSE);

              fnd_message.show;

            ELSE

              fnd_message.set_name('FND', 'CONC-REQUEST

        SUBMISSION FAILED');

              fnd_message.error;

            END IF;

      ELSE

            DO_KEY('COMMIT_FORM');

            IF :SYSTEM.FORM_STATUS != 'CHANGED' THEN

                 /*commit was successful*/

                 fnd_message.set_name('SQLGL','GL_REQUEST_SUBMITTED');

             fnd_message.set_TOKEN('REQUEST_ID',

       TO_CHAR(:PARAMETER.REQ_ID), FALSE);

              fnd_message.show;

            END IF;             

       END IF;

   END IF;

  ELSE

      fnd_message.debug('Invalid event passed to

      DEMXXEOR.PRINT_order: ' || EVENT);

  END IF;

END PRINT_ORDER;

 

然后在form-level的Special1和PRINT_ORDER Button的when-button-pressed Trigger中调用处理代码。

demvceor.PRINT_ORDER('SPECIAL1');

我在练习中做到这一步,在窗体中提交Request报错,同事说是由于先前定义的Execution File在系统中不存在,我在网上也没找到该文件的脚本,因此这节练习也就做到这里了。
 
Oracle Form. 6i开发的心得(12)2011-02-14 14:59Lab 19: Implementing Zoom Use the Custom Library

使用CUSTOM library可以在不修改Oracle Application代码的情况下实现Oracle Application功能的扩展,在下面的情形下,会使用到CUSTOM library,当然每种情况的处理代码也不一样。

? Zoom—从当前窗体获得参数又打开另一个窗体;

? Logic for generic events—对某些form. events的控制等,比如WHEN–FORM–NAVIGATE,WHEN–NEW–FORM–INSTANCE,WHEN–NEW–BLOCK–INSTANCE,WHEN–NEW–RECORD–INSTANCE,WHEN–NEW–ITEM–INSTANCE,WHEN–VALIDATE–RECORD,EXPORT,KEY–Fn (where n is a number between 1 and 8);

? Logic for product–specific events—对某些product–specific events的控制等,比如WHEN–LOGON–CHANGED,WHEN–RESPONSIBILITY–CHANGED;

? Custom entries for the special menus—添加客制化菜单的功能SPECIALn (where n is a number between 1 and 45);

? Setting visual attributes —运行时修改visual属性等。

有些问题在使用前先弄清,在customer library中WHEN–FORM–NAVIGATE,WHEN–NEW–BLOCK–INSTANCE,WHEN–NEW–RECORD–INSTANCE或者WHEN–NEW–ITEM–INSTANCE只在form/block/item level的同名trigger触发后才触发;WHEN–NEW–FORM–INSTANCE会在调用app_standard_event时触发,如form when-new-form-instance的app_standard_event.(when-new-form-instance);

WHEN–VALIDATE–RECORD会在调用app_standard_event或 fnd_flex.event时触发(form/ block level);SPECIALn在同名trigger触发前触发;ZOOM靠(View–>Zoom)或工具条按钮触发;EXPORT靠(File–>Export)触发;KEY–Fn在按下corresponding function key或key combination触发。在Help–>Keyboard菜单项可以找到快捷键。

下面我介绍如何利用CUSTOM library使用Zoom功能。

首先在窗体中添加两个parameter:ORDER_ID和CUSTOMER_NAME;

然后修改ORDERS block的WHERE clause属性,该block应是目标block,即目标窗体所对应的block,我认为这样改的目的兼顾非Zoom下的查询和匹配先前定义parameter的查询;

WHERE (:parameter.order_id is null or

    dem_orders_v.order_id like :parameter.order_id)

AND (:parameter.customer_name is null or

    dem_orders_v.customer_name like :parameter.customer_name)

接着在WHEN–NEW–FORM–INSTANCE trigger中添加下列代码:

/* fire automatic query if a parameter has a value from Zoom */

if (:parameter.order_id is not null) or (:parameter.customer_name is not null) then

GO_BLOCK('ORDERS');

do_key('EXECUTE_QUERY');

/* clear the parameters after the query so they don’t remain

criteria for future queries */

:parameter.order_id := null;

:parameter.customer_name := null;

end if;

目的在于每次处理完Zoom Event后清空相关parameter。

  最后我在CUSTOM.PLL文件中作了如下的修改:

function zoom_available return boolean is

    form_name  varchar2(30) := name_in('system.current_form');

    block_name varchar2(30) := name_in('system.cursor_block'); 

  begin

    if (form_name = 'DEMVCEOR' and block_name = 'ORDERS')

  then

      return TRUE;

    else

      return FALSE;

    end if;

  end zoom_available;

  这个函数会在form-level when-new-form-instance trigger的

app_standard.event(when-new-form-instance)中被调用,用来判断是否可以激活工具条上的Zoom图标按钮。

procedure event(event_name varchar2) is

     form_name      varchar2(30) := name_in('system.current_form');

   block_name     varchar2(30) := name_in('system.cursor_block'); 

    param_to_pass1 varchar2(255);

    param_to_pass2 varchar2(255);

  begin

   if (event_name = 'ZOOM') then  

      if (form_name = 'DEMVCEOR' and block_name = 'ORDERS') then

        param_to_pass1 := name_in('ORDERS.order_id');

        param_to_pass2 := name_in('ORDERS.customer_name');

        fnd_function.execute(FUNCTION_NAME=>'DEMVC_DEMVCEOR', 

          OPEN_FLAG=>'Y', SESSION_FLAG=>'Y', 

            OTHER_PARAMS=>'ORDER_ID="'||param_to_pass1||

            '" CUSTOMER_NAME="'||param_to_pass2||'"');

                end if;  

   else

      null;

   end if;

  end event;

  这个过程就是Zoom事件的处理函数,它的传入参数包括了两个预先定义的参数,form_name和block_name。注意fnd_function.execute函数的作用就是打开一个新窗体,该窗体使用了传入的参数。该过程也会在系统预先定义好的form-level ZOOM trigger的appcore_custom.event('ZOOM')中被调用。

做完以上步骤后,就可以在系统中使用ZOOM功能了,可是我很遗憾的失败了。探究原因可能是由于编译CUTOM.PLL生成的CUTOM.PLX方法不对。

我的方法:

f60gen module=CUTOM.PLL userid=apps/apps module_type=LIBRARY;

查看文档后觉得可能是:

f60gen module=DEMVCEOR userid=apps/apps COMPILE_ALL=Yes output_file=$DEM_TOP/forms/US/DEMVCEOR.fmx

  我想既然系统不自动激活ZOOM图标,那么我就自己写代码显式激活,然后显式调用处理函数,于是做了下面的工作。

在Orders block-level的when-new-block-instance trigger中添加了下列代码:

SET_MENU_ITEM_PROPERTY('VIEW.ZOOM',ENABLED,PROPERTY_TRUE;

在form-level的zoom trigger中改写成下列代码:

custom.event('ZOOM');

重新编译生成后就可以,如下图所示:


  到这里,我就做完了这个关于Form的练习,完成之后算是对开发Form的过程、重点、难点有了个大致的了解。后面是一些总结,当是补充吧,内容实现太多了,就怕自己过两天又忘了。

 
Oracle Form. 6i开发的心得(13)2011-02-14 15:00Lab 20: About Trigger1. Pre-form. trigger

   FND_STANDARD.FORM_INFO

('$Revision: 115.12 $', 'Demo Order Form', 'DEMVCEOR', $Date: 2006/07/18 17:12  $', '$Author: appldemvc $');

app_standard.event('PRE-FORM');

app_window.set_window_position('ORDERS', 'FIRST_WINDOW');

demvceor.pre_form;

   Pre-form. trigger调用APPCORE Routing APIS的APP_WINDOW.SET_WINDOW_POSITION和FNDSQF Routing APIS的 FND_STANDARD.FORM_INFO函数,这些函数在template.fmb的Pre-form. trigger中已经存在,但是必须改变这些函数的参数值,窗体才能正常使用。

  2. Record history (WHO): Track Data Changes

在练习中我发现一个奇怪的现象,在Block-Level的Pre-insert和Pre-update的trigger中都调用了FND_STANDARD.SET_WHO;而且在Order和Line Block中都包括了下表所示的几个字段。


请教过同事,他说一般的ebs的oracle table中都会包括这几个字段,主要用于数据处理的跟踪,调用FND_STANDARD.SET_WHO函数后系统将自动完成对这几个字段的操作过程,如果是并发程序,还会用到下面的几个字段。


我觉得这种字段设置方式对我们开发其它系统也是一个很好的启发。

3. Triggers in TEMPLATE

 前面已经提过,forms的开发大多由template.fmb修改而成,template窗体本身就预包含了许多标准格式的form-level的trigger,等待开发者修改。在这之中,必须要修改的trigger有:PRE-FORM;可能要修改的trigger有:KEY-CLRFRM,POST-FORM,QUERY_FIND,ACCEPT;在Block或Item Level可以添加的trigger有WHEN-NEW-RECORD-INSTANCE,WHEN-NEW-BLOCK-INSTANCE,WHEN-NEW-ITEM-INSTANCE,KEY-DUPREC,KEY-MENU,KEY-LISTVAL,QUERY_FIND,ACCEPT,ON-ERROR等。

 4. Coding Triggers

   创建Trigger时要特别注意Trigger的Execution Hierarch属性。一般情况下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。

      以下是oracle给出的Block或Item-Level Triggers的Execution Hierarch属性的限定:必须设置为Before的trigger有WHEN-NEW-RECORD-INSTANCE, WHEN-NEW-BLOCK-INSTANCE,WHEN-NEW-ITEM-INSTANCE;可以设置为Override的trigger有KEY-DUPREC,KEY-MENU,KEY-LISTVAL,QUERY_FIND, ACCEPT。

5. Tab Triggers

   与Tab相关的trigger,一个是form-level的when-tab-page-changed,另一个是block-level的when-new-item-instance。when-new-item-instance trigger处理由于tab键导航引起的tab问题,逻辑相对比较简单。when-tab-page-changed trigger则比较复杂,对着代码我也不很明白。我个人觉得处理逻辑是这样的:先判断current_canvas是不是fixed_stacked_canvas,是的话就将show_view(target_canvas)在go_item,不是的话就将target_canvas设为tab_topmost_page,然后show_view(target_canvas)在go_item。总之,要保证target_canvas一定会show出来。我的理解可能有误,希望知道的同仁能够告诉我正解,实在是复杂。

6.自己的一些感想

说到Trigger,个人觉得就是Oracle Form控制逻辑的精要,后面很多章节的Trigger我就不在这里再单单列出了。我想总结一下使用Trigger要注意的地方:

*Trigger的level 什么样的Trigger放在什么样的level上,什么样的Trigger放在多级level上,注意此时的Execution Hierarch属性该怎么设;

*Trigger该调用什么样的处理代码,有些Trigger调用API函数,有些调用客制化过程,虽然内容很多,但是一类Trigger调用了一类API,应多加熟悉;

*Trigger的执行顺序,这个很多网上都有详尽的说明;

*Trigger中多个处理过程的执行顺序,这个地方在某些时候是需要特别注意的。
 
Lab 21: Naming Standard

当年在用.net编程就因变量、控件命名不符合规范经常被老师骂,当时还不服气,现在越来越觉得命名规范的重要性,所以在新学习一门开发技术的时候,就要养成良好的命名习惯,以免日后积重难返。

1. Form. Objects命名标准

Module应与Form文件名匹配,最好由大写连续的英文字符组成,中间无任何分隔符;

  Window,canvas,block,item的命名应当简洁而有意义,一般是以对象名称命名。特殊的block 应用特殊的命名,比如TOOLBAR,CONTROL,CONTEXT;

  Query–Find Canvasses,Windows,Blocks,LOVs和Related Record Groups

命名如QF_object;

   Relations命名如master_detail

 

2. Subfunction 命名标准

开发人员需预先在系统中为form定义受限的subfunction。

Subfunctions的命名标准为

_

Eg:PO_POXPOMPO_DELETE AR_FNDRSRUN_LISTING_RPTS

User function的命名标准为

:

Eg:Purchase Orders: Delete  Run Reports: Listing

这种统一的命名规范使得系统管理员可以在Responsibilities窗体中利用LOV的Auto-reduction方便地查到某个responsibility下的可用或不可用的functions(我这种理解可能值得商榷)

  如果对于某个特定的form存在许多受限的subfunction,可以将这些subfunction分类分组。

 

3. Program Unit命名编码标准

Program Unit中package命名多以对象名为主,常常和Block重名,为每个Block创建一个同名的package也易于管理。

Package中的function和Procedure命名多重用Item的名字,定义一个传入参数event,代码对应多个事件的一一处理分支,调用起来很方便。另外就是以下划线为分隔符命名,可能是某种事件,trigger,某个特定含义。

 

PACKAGE ORDERS IS

PROCEDURE Insert_Row;

PROCEDURE Update_Row;

PROCEDURE Lock_Row;

PROCEDURE Delete_Row;

 

PROCEDURE PRE_UPDATE;

PROCEDURE PRE_INSERT;

 

PROCEDURE lines(EVENT VARCHAR2);

PROCEDURE ORDER_ID(EVENT VARCHAR2);

PROCEDURE PAYMENT_TYPE(EVENT VARCHAR2);

PROCEDURE CHECK_NUMBER(EVENT VARCHAR2);

PROCEDURE CREDIT(EVENT VARCHAR2);

PROCEDURE DATE_SHIPPED(EVENT VARCHAR2);

PROCEDURE DESC_FLEX(EVENT VARCHAR2);

 

PROCEDURE VISA(EVENT VARCHAR2);

END;

 

怎样从Oracle的FORM中调用REPORT

从Oracle的FORM中调用REPORT:

◆在菜单中调用REPORT程序(注释:不传参数,传参数可用下面的程序段)

Run_Product(REPORTS, '报表程序名.REP', SYNCHRONOUS, RUNTIME, FILESYSTEM, '', NULL);

◆在FORM中调用REPORT程序(注释:可以传参数)

DECLAREpl_id ParamList;BEGINpl_id := Get_Parameter_List('参数列表名');IF NOT Id_Null(pl_id) THENDestroy_Parameter_List( pl_id );END IF;pl_id := Create_Parameter_List('参数列表名');Add_Parameter(pl_id,'REPORT参数名1',DATA_PARAMETER,':FORM项名1');Add_Parameter(pl_id,'REPORT参数名2',TEXT_PARAMETER,':FORM项名2');… …Run_Product(REPORTS, '报表程序名.REP', SYNCHRONOUS, RUNTIME, FILESYSTEM, pl_id, NULL);END;
 

◆在报表中使用参数作为帮定变量时在其前面加一个冒号(:),例如:有一个参数p_deptno,传来的值为10,则select * form. emp where deptno=:p_deptno将返回表emp中deptno为10的记录。

◆在报表中使用参数作为词汇参数时在起前面加一个“与”符号(&),例如:有一个参数p_where,传来的值为where deptno=10,则select * from emp &p_where也将返回表emp中deptno为10的记录。

◆在在调用REPORT时,去掉report server:

Add_parameter(pl_id,'ORACLE_SHUTDOWN',TEXT_PARAMETER,'YES');

◆在调用REPORT时,使报表结果直接输出到打印机上:

Add_parameter(pl_id,'PSRAMETER_FORM',TEXT_PARAMETER,'NO');

◆在调用REPORT时,不弹出参数窗口:

add_parameter(pl_id,'PARAMFORM',TEXT_PARAMETER,'NO');

◆在调用REPORT时,以满屏方式显示:

add_parameter(pl_id, 'maximize', TEXT_PARAMETER,'yes');

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/425856/viewspace-701329/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/425856/viewspace-701329/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值